logtide 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +23 -0
- data/LICENSE +21 -0
- data/README.md +168 -0
- data/lib/logtide/breadcrumb.rb +65 -0
- data/lib/logtide/circuit_breaker.rb +93 -0
- data/lib/logtide/client.rb +255 -0
- data/lib/logtide/configuration.rb +94 -0
- data/lib/logtide/dsn.rb +56 -0
- data/lib/logtide/error.rb +10 -0
- data/lib/logtide/event.rb +122 -0
- data/lib/logtide/hub.rb +101 -0
- data/lib/logtide/logger_bridge.rb +52 -0
- data/lib/logtide/metrics.rb +25 -0
- data/lib/logtide/rack/middleware.rb +111 -0
- data/lib/logtide/rails/railtie.rb +23 -0
- data/lib/logtide/retry_policy.rb +32 -0
- data/lib/logtide/scope.rb +94 -0
- data/lib/logtide/structured_exception.rb +94 -0
- data/lib/logtide/tracing/span.rb +89 -0
- data/lib/logtide/tracing.rb +61 -0
- data/lib/logtide/transport/batcher.rb +209 -0
- data/lib/logtide/transport/buffer.rb +37 -0
- data/lib/logtide/transport/http.rb +94 -0
- data/lib/logtide/transport/otlp.rb +78 -0
- data/lib/logtide/version.rb +8 -0
- data/lib/logtide.rb +152 -0
- metadata +73 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "net/http"
|
|
4
|
+
require "uri"
|
|
5
|
+
require "json"
|
|
6
|
+
require "time"
|
|
7
|
+
require "timeout"
|
|
8
|
+
require_relative "http"
|
|
9
|
+
|
|
10
|
+
module Logtide
|
|
11
|
+
module Transport
|
|
12
|
+
# Exports spans as OTLP/JSON to /v1/otlp/traces (spec 005 section 3).
|
|
13
|
+
# Shares the retryable/network-error contract with the log transport.
|
|
14
|
+
class Otlp
|
|
15
|
+
USER_AGENT = "logtide-ruby/#{Logtide::VERSION}".freeze
|
|
16
|
+
|
|
17
|
+
def initialize(url:, api_key:, resource:, timeout: 10)
|
|
18
|
+
@uri = URI.parse(url)
|
|
19
|
+
@api_key = api_key
|
|
20
|
+
@resource = resource
|
|
21
|
+
@timeout = timeout
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def deliver(spans)
|
|
25
|
+
body = JSON.generate(payload(spans))
|
|
26
|
+
response = client.request(build_request(body))
|
|
27
|
+
Response.new(status: response.code.to_i, retry_after: nil)
|
|
28
|
+
rescue *HTTP::NETWORK_ERRORS => e
|
|
29
|
+
raise NetworkError, e.message
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def payload(spans)
|
|
35
|
+
{
|
|
36
|
+
"resourceSpans" => [
|
|
37
|
+
{
|
|
38
|
+
"resource" => { "attributes" => resource_attributes },
|
|
39
|
+
"scopeSpans" => [{ "spans" => spans.map(&:to_otlp) }]
|
|
40
|
+
}
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def resource_attributes
|
|
46
|
+
[
|
|
47
|
+
string_attribute("service.name", @resource[:service_name]),
|
|
48
|
+
string_attribute("deployment.environment", @resource[:environment]),
|
|
49
|
+
string_attribute("service.version", @resource[:service_version])
|
|
50
|
+
].compact
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def string_attribute(key, value)
|
|
54
|
+
return nil if value.nil?
|
|
55
|
+
|
|
56
|
+
{ "key" => key, "value" => { "stringValue" => value.to_s } }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def client
|
|
60
|
+
http = Net::HTTP.new(@uri.host, @uri.port)
|
|
61
|
+
http.use_ssl = @uri.scheme == "https"
|
|
62
|
+
http.open_timeout = @timeout
|
|
63
|
+
http.read_timeout = @timeout
|
|
64
|
+
http.write_timeout = @timeout if http.respond_to?(:write_timeout=)
|
|
65
|
+
http
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def build_request(body)
|
|
69
|
+
request = Net::HTTP::Post.new(@uri.request_uri)
|
|
70
|
+
request["X-API-Key"] = @api_key
|
|
71
|
+
request["Content-Type"] = "application/json"
|
|
72
|
+
request["User-Agent"] = USER_AGENT
|
|
73
|
+
request.body = body
|
|
74
|
+
request
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
data/lib/logtide.rb
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "logtide/version"
|
|
4
|
+
require_relative "logtide/error"
|
|
5
|
+
require_relative "logtide/dsn"
|
|
6
|
+
require_relative "logtide/configuration"
|
|
7
|
+
require_relative "logtide/circuit_breaker"
|
|
8
|
+
require_relative "logtide/retry_policy"
|
|
9
|
+
require_relative "logtide/metrics"
|
|
10
|
+
require_relative "logtide/transport/buffer"
|
|
11
|
+
require_relative "logtide/transport/http"
|
|
12
|
+
require_relative "logtide/transport/batcher"
|
|
13
|
+
require_relative "logtide/structured_exception"
|
|
14
|
+
require_relative "logtide/breadcrumb"
|
|
15
|
+
require_relative "logtide/tracing"
|
|
16
|
+
require_relative "logtide/tracing/span"
|
|
17
|
+
require_relative "logtide/transport/otlp"
|
|
18
|
+
require_relative "logtide/scope"
|
|
19
|
+
require_relative "logtide/event"
|
|
20
|
+
require_relative "logtide/client"
|
|
21
|
+
require_relative "logtide/hub"
|
|
22
|
+
require_relative "logtide/logger_bridge"
|
|
23
|
+
require_relative "logtide/rack/middleware"
|
|
24
|
+
require_relative "logtide/rails/railtie" if defined?(Rails::Railtie)
|
|
25
|
+
|
|
26
|
+
# LogTide SDK for Ruby. The module-level methods are the documented entry point
|
|
27
|
+
# (spec 004 section 1); they delegate to the current Hub.
|
|
28
|
+
module Logtide
|
|
29
|
+
class << self
|
|
30
|
+
# Initialise the SDK and bind the global Hub. Returns the Hub.
|
|
31
|
+
def init(**options)
|
|
32
|
+
hub = Hub.new(Client.new(Configuration.new(**options)))
|
|
33
|
+
@main_hub = hub
|
|
34
|
+
register_shutdown
|
|
35
|
+
hub
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def main_hub
|
|
39
|
+
@main_hub ||= Hub.new(nil)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def current_hub
|
|
43
|
+
Thread.current[:logtide_hub] || main_hub
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def current_hub=(hub)
|
|
47
|
+
Thread.current[:logtide_hub] = hub
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Run a block with a per-request hub cloned from the main hub, then restore
|
|
51
|
+
# the previous hub. Used by framework middleware for request isolation.
|
|
52
|
+
def with_request_hub
|
|
53
|
+
previous = Thread.current[:logtide_hub]
|
|
54
|
+
self.current_hub = main_hub.clone
|
|
55
|
+
yield current_hub
|
|
56
|
+
ensure
|
|
57
|
+
Thread.current[:logtide_hub] = previous
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def capture_log(level, message, metadata = nil, **opts)
|
|
61
|
+
current_hub.capture_log(level, message, metadata, **opts)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def debug(message, metadata = nil, **opts)
|
|
65
|
+
capture_log("debug", message, metadata, **opts)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def info(message, metadata = nil, **opts)
|
|
69
|
+
capture_log("info", message, metadata, **opts)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def warn(message, metadata = nil, **opts)
|
|
73
|
+
capture_log("warn", message, metadata, **opts)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def error(message, metadata_or_exception = nil, **opts)
|
|
77
|
+
if metadata_or_exception.is_a?(Exception)
|
|
78
|
+
return capture_exception(metadata_or_exception, message: message, level: "error", **opts)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
capture_log("error", message, metadata_or_exception, **opts)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def critical(message, metadata_or_exception = nil, **opts)
|
|
85
|
+
if metadata_or_exception.is_a?(Exception)
|
|
86
|
+
return capture_exception(metadata_or_exception, message: message, level: "critical", **opts)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
capture_log("critical", message, metadata_or_exception, **opts)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def capture_exception(exception, **opts)
|
|
93
|
+
current_hub.capture_exception(exception, **opts)
|
|
94
|
+
end
|
|
95
|
+
alias capture_error capture_exception
|
|
96
|
+
|
|
97
|
+
def configure_scope(&)
|
|
98
|
+
current_hub.configure_scope(&)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def with_scope(&)
|
|
102
|
+
current_hub.with_scope(&)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def add_breadcrumb(breadcrumb)
|
|
106
|
+
current_hub.add_breadcrumb(breadcrumb)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def start_span(name, ...)
|
|
110
|
+
current_hub.start_span(name, ...)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def trace_propagation_headers
|
|
114
|
+
current_hub.trace_propagation_headers
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def set_user(user)
|
|
118
|
+
configure_scope { |scope| scope.set_user(user) }
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def set_tag(key, value)
|
|
122
|
+
configure_scope { |scope| scope.set_tag(key, value) }
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def flush(timeout = nil)
|
|
126
|
+
current_hub.flush(timeout)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def close(timeout = nil)
|
|
130
|
+
current_hub.close(timeout)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def get_metrics
|
|
134
|
+
current_hub.client&.metrics
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Test/teardown seam: drop the bound hubs.
|
|
138
|
+
def reset!
|
|
139
|
+
Thread.current[:logtide_hub] = nil
|
|
140
|
+
@main_hub = nil
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
private
|
|
144
|
+
|
|
145
|
+
def register_shutdown
|
|
146
|
+
return if @shutdown_registered
|
|
147
|
+
|
|
148
|
+
@shutdown_registered = true
|
|
149
|
+
at_exit { @main_hub&.close }
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: logtide
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- LogTide
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-06-17 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: Log management, tracing and error tracking for Ruby and Rails. Captures
|
|
14
|
+
logs, exceptions and spans and ships them to a LogTide instance.
|
|
15
|
+
email:
|
|
16
|
+
- hello@logtide.dev
|
|
17
|
+
executables: []
|
|
18
|
+
extensions: []
|
|
19
|
+
extra_rdoc_files: []
|
|
20
|
+
files:
|
|
21
|
+
- CHANGELOG.md
|
|
22
|
+
- LICENSE
|
|
23
|
+
- README.md
|
|
24
|
+
- lib/logtide.rb
|
|
25
|
+
- lib/logtide/breadcrumb.rb
|
|
26
|
+
- lib/logtide/circuit_breaker.rb
|
|
27
|
+
- lib/logtide/client.rb
|
|
28
|
+
- lib/logtide/configuration.rb
|
|
29
|
+
- lib/logtide/dsn.rb
|
|
30
|
+
- lib/logtide/error.rb
|
|
31
|
+
- lib/logtide/event.rb
|
|
32
|
+
- lib/logtide/hub.rb
|
|
33
|
+
- lib/logtide/logger_bridge.rb
|
|
34
|
+
- lib/logtide/metrics.rb
|
|
35
|
+
- lib/logtide/rack/middleware.rb
|
|
36
|
+
- lib/logtide/rails/railtie.rb
|
|
37
|
+
- lib/logtide/retry_policy.rb
|
|
38
|
+
- lib/logtide/scope.rb
|
|
39
|
+
- lib/logtide/structured_exception.rb
|
|
40
|
+
- lib/logtide/tracing.rb
|
|
41
|
+
- lib/logtide/tracing/span.rb
|
|
42
|
+
- lib/logtide/transport/batcher.rb
|
|
43
|
+
- lib/logtide/transport/buffer.rb
|
|
44
|
+
- lib/logtide/transport/http.rb
|
|
45
|
+
- lib/logtide/transport/otlp.rb
|
|
46
|
+
- lib/logtide/version.rb
|
|
47
|
+
homepage: https://github.com/logtide-dev/logtide-ruby
|
|
48
|
+
licenses:
|
|
49
|
+
- MIT
|
|
50
|
+
metadata:
|
|
51
|
+
homepage_uri: https://github.com/logtide-dev/logtide-ruby
|
|
52
|
+
source_code_uri: https://github.com/logtide-dev/logtide-ruby
|
|
53
|
+
changelog_uri: https://github.com/logtide-dev/logtide-ruby/blob/main/CHANGELOG.md
|
|
54
|
+
post_install_message:
|
|
55
|
+
rdoc_options: []
|
|
56
|
+
require_paths:
|
|
57
|
+
- lib
|
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
59
|
+
requirements:
|
|
60
|
+
- - ">="
|
|
61
|
+
- !ruby/object:Gem::Version
|
|
62
|
+
version: 3.1.0
|
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '0'
|
|
68
|
+
requirements: []
|
|
69
|
+
rubygems_version: 3.5.22
|
|
70
|
+
signing_key:
|
|
71
|
+
specification_version: 4
|
|
72
|
+
summary: Official LogTide SDK for Ruby
|
|
73
|
+
test_files: []
|