obtrace-sdk-ruby 1.0.2 → 1.2.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 +4 -4
- data/lib/obtrace_sdk/client.rb +97 -170
- data/lib/obtrace_sdk/middleware.rb +38 -64
- data/lib/obtrace_sdk/otel_setup.rb +71 -0
- data/lib/obtrace_sdk/rails.rb +8 -8
- data/lib/obtrace_sdk/types.rb +2 -6
- data/lib/obtrace_sdk/version.rb +1 -1
- data/lib/obtrace_sdk.rb +4 -7
- metadata +63 -11
- data/lib/obtrace_sdk/context.rb +0 -18
- data/lib/obtrace_sdk/framework.rb +0 -15
- data/lib/obtrace_sdk/http_instrumentation.rb +0 -122
- data/lib/obtrace_sdk/logger_capture.rb +0 -68
- data/lib/obtrace_sdk/otlp.rb +0 -127
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e0395a5b1787dc1cfc6c7f5385d4c78736580d700efefded562fa2b29d8aeb26
|
|
4
|
+
data.tar.gz: 7100b663a95f5e2ce98432c649fdeb77057993643fc46ef2cc74ca9820ea5abd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1f42388bc450d57fa956efed164373acf6d357e652b79d60a5c3f3369ced325575ebf0e05ba51bb1122e8d861612f50b70ad4ba3656d079f46f957e34c38f231
|
|
7
|
+
data.tar.gz: fd240b922f666c45515dfa963b0c77fe44aacfc144102dcf3e5cc4e560390d9e2e468280ebca82e0bf56a774bd3c86870fd93dfd5ddee5ac70bc2bf4b337e7e2
|
data/lib/obtrace_sdk/client.rb
CHANGED
|
@@ -1,208 +1,135 @@
|
|
|
1
|
-
require "json"
|
|
2
1
|
require "net/http"
|
|
2
|
+
require "json"
|
|
3
3
|
require "uri"
|
|
4
|
-
|
|
5
|
-
require_relative "context"
|
|
6
|
-
require_relative "otlp"
|
|
7
|
-
require_relative "http_instrumentation"
|
|
8
|
-
require_relative "logger_capture"
|
|
4
|
+
require_relative "otel_setup"
|
|
9
5
|
|
|
10
6
|
module ObtraceSDK
|
|
11
7
|
class Client
|
|
12
|
-
|
|
13
|
-
raise ArgumentError, "api_key, ingest_base_url and service_name are required" if cfg.api_key.to_s.empty? || cfg.ingest_base_url.to_s.empty? || cfg.service_name.to_s.empty?
|
|
8
|
+
@@initialized = false
|
|
14
9
|
|
|
15
|
-
|
|
16
|
-
@queue = []
|
|
17
|
-
@lock = Mutex.new
|
|
18
|
-
@http = nil
|
|
19
|
-
@http_uri = nil
|
|
20
|
-
@circuit_failures = 0
|
|
21
|
-
@circuit_open_until = Time.at(0)
|
|
22
|
-
@seen_exceptions = {}
|
|
23
|
-
@seen_lock = Mutex.new
|
|
24
|
-
|
|
25
|
-
install_exception_tracepoint
|
|
26
|
-
HttpInstrumentation.install(self) if @cfg.auto_instrument_http
|
|
27
|
-
LoggerCapture.install(self) if @cfg.auto_capture_logs
|
|
28
|
-
at_exit { capture_fatal; shutdown }
|
|
29
|
-
end
|
|
10
|
+
attr_reader :tracer, :meter, :handshake_ok
|
|
30
11
|
|
|
31
|
-
def
|
|
32
|
-
|
|
33
|
-
|
|
12
|
+
def initialize(cfg)
|
|
13
|
+
if @@initialized
|
|
14
|
+
warn("[obtrace-sdk-ruby] already initialized, skipping duplicate init")
|
|
15
|
+
return
|
|
16
|
+
end
|
|
34
17
|
|
|
35
|
-
|
|
36
|
-
warn("[obtrace-sdk-ruby] non-canonical metric name: #{name}") if @cfg.validate_semantic_metrics && @cfg.debug && !SemanticMetrics.semantic_metric?(name)
|
|
37
|
-
enqueue("/otlp/v1/metrics", Otlp.metric_payload(@cfg, truncate(name, 1024), value, unit, context))
|
|
38
|
-
end
|
|
18
|
+
raise ArgumentError, "api_key, ingest_base_url and service_name are required" if cfg.api_key.to_s.empty? || cfg.ingest_base_url.to_s.empty? || cfg.service_name.to_s.empty?
|
|
39
19
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
20
|
+
@@initialized = true
|
|
21
|
+
@cfg = cfg
|
|
22
|
+
@handshake_ok = false
|
|
23
|
+
@tracer_provider = OtelSetup.configure(cfg)
|
|
24
|
+
@tracer = @tracer_provider.tracer("obtrace-sdk-ruby", ObtraceSDK::VERSION)
|
|
25
|
+
@meter = nil
|
|
26
|
+
@meter_warning_logged = false
|
|
45
27
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
28
|
+
begin
|
|
29
|
+
@meter = OpenTelemetry.meter_provider.meter("obtrace-sdk-ruby", ObtraceSDK::VERSION)
|
|
30
|
+
rescue => _e
|
|
49
31
|
end
|
|
50
32
|
|
|
51
|
-
|
|
52
|
-
{
|
|
33
|
+
Thread.new { perform_handshake }
|
|
34
|
+
at_exit { shutdown }
|
|
53
35
|
end
|
|
54
36
|
|
|
55
|
-
def
|
|
56
|
-
|
|
37
|
+
private def perform_handshake
|
|
38
|
+
base = @cfg.ingest_base_url.to_s.chomp("/")
|
|
39
|
+
return if base.empty?
|
|
40
|
+
uri = URI("#{base}/v1/init")
|
|
41
|
+
payload = JSON.generate({
|
|
42
|
+
sdk: "obtrace-sdk-ruby",
|
|
43
|
+
sdk_version: "1.2.0",
|
|
44
|
+
service_name: @cfg.service_name,
|
|
45
|
+
service_version: @cfg.service_version.to_s,
|
|
46
|
+
runtime: "ruby",
|
|
47
|
+
runtime_version: RUBY_VERSION,
|
|
48
|
+
})
|
|
49
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
50
|
+
http.use_ssl = uri.scheme == "https"
|
|
51
|
+
http.open_timeout = 5
|
|
52
|
+
http.read_timeout = 5
|
|
53
|
+
req = Net::HTTP::Post.new(uri)
|
|
54
|
+
req["Content-Type"] = "application/json"
|
|
55
|
+
req["Authorization"] = "Bearer #{@cfg.api_key}"
|
|
56
|
+
req.body = payload
|
|
57
|
+
resp = http.request(req)
|
|
58
|
+
if resp.code.to_i == 200
|
|
59
|
+
@handshake_ok = true
|
|
60
|
+
warn("[obtrace-sdk-ruby] init handshake OK") if @cfg.debug
|
|
61
|
+
elsif @cfg.debug
|
|
62
|
+
warn("[obtrace-sdk-ruby] init handshake failed: #{resp.code}")
|
|
63
|
+
end
|
|
64
|
+
rescue => e
|
|
65
|
+
warn("[obtrace-sdk-ruby] init handshake error: #{e.message}") if @cfg.debug
|
|
57
66
|
end
|
|
58
67
|
|
|
59
|
-
def
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
half_open = @circuit_failures >= 5
|
|
64
|
-
if half_open
|
|
65
|
-
return if @queue.empty?
|
|
66
|
-
batch = [@queue.shift]
|
|
67
|
-
else
|
|
68
|
-
batch = @queue.dup
|
|
69
|
-
@queue.clear
|
|
70
|
-
end
|
|
68
|
+
def log(level, message, context = nil)
|
|
69
|
+
attributes = { "obtrace.log.level" => level.to_s }
|
|
70
|
+
if context
|
|
71
|
+
context.each { |k, v| attributes["obtrace.attr.#{k}"] = v.to_s }
|
|
71
72
|
end
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
end
|
|
81
|
-
else
|
|
82
|
-
@circuit_failures += 1
|
|
83
|
-
if @circuit_failures >= 5
|
|
84
|
-
@circuit_open_until = Time.now + 30
|
|
85
|
-
warn("[obtrace-sdk-ruby] circuit breaker opened") if @cfg.debug
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
end
|
|
73
|
+
attributes["log.severity"] = level.to_s.upcase
|
|
74
|
+
attributes["log.message"] = message.to_s
|
|
75
|
+
|
|
76
|
+
span = OpenTelemetry::Trace.current_span
|
|
77
|
+
if span && span.context.valid?
|
|
78
|
+
span.add_event("log", attributes: attributes)
|
|
79
|
+
else
|
|
80
|
+
@tracer.in_span("log.#{level}", attributes: attributes) { |_s| }
|
|
89
81
|
end
|
|
90
82
|
end
|
|
91
83
|
|
|
92
|
-
def
|
|
93
|
-
@
|
|
94
|
-
flush
|
|
95
|
-
@lock.synchronize do
|
|
96
|
-
if @http
|
|
97
|
-
@http.finish rescue nil
|
|
98
|
-
@http = nil
|
|
99
|
-
@http_uri = nil
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
end
|
|
84
|
+
def metric(name, value, unit = "1", context = nil)
|
|
85
|
+
warn("[obtrace-sdk-ruby] non-canonical metric name: #{name}") if @cfg.validate_semantic_metrics && @cfg.debug && !SemanticMetrics.semantic_metric?(name)
|
|
103
86
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
@tracepoint = TracePoint.new(:raise) do |tp|
|
|
109
|
-
ex = tp.raised_exception
|
|
110
|
-
eid = ex.object_id
|
|
111
|
-
seen = client.instance_variable_get(:@seen_lock).synchronize do
|
|
112
|
-
cache = client.instance_variable_get(:@seen_exceptions)
|
|
113
|
-
next true if cache[eid]
|
|
114
|
-
cache[eid] = true
|
|
115
|
-
cache.shift if cache.size > 200
|
|
116
|
-
false
|
|
117
|
-
end
|
|
118
|
-
unless seen
|
|
119
|
-
bt = (ex.backtrace || []).first(10).join("\n")
|
|
120
|
-
client.log("error", "#{ex.class}: #{ex.message}", {
|
|
121
|
-
"exception.type" => ex.class.to_s,
|
|
122
|
-
"exception.message" => ex.message.to_s,
|
|
123
|
-
"exception.stacktrace" => bt,
|
|
124
|
-
"code.filepath" => tp.path.to_s,
|
|
125
|
-
"code.lineno" => tp.lineno.to_s,
|
|
126
|
-
"auto.source" => "tracepoint"
|
|
127
|
-
})
|
|
87
|
+
unless @meter
|
|
88
|
+
unless @meter_warning_logged
|
|
89
|
+
warn("[obtrace-sdk-ruby] meter provider not available, metrics will be dropped")
|
|
90
|
+
@meter_warning_logged = true
|
|
128
91
|
end
|
|
92
|
+
return
|
|
129
93
|
end
|
|
130
|
-
@tracepoint.enable
|
|
131
|
-
end
|
|
132
94
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
ex = $!
|
|
136
|
-
bt = (ex.backtrace || []).first(10).join("\n")
|
|
137
|
-
log("fatal", "#{ex.class}: #{ex.message}", {
|
|
138
|
-
"exception.type" => ex.class.to_s,
|
|
139
|
-
"exception.message" => ex.message.to_s,
|
|
140
|
-
"exception.stacktrace" => bt,
|
|
141
|
-
"auto.source" => "at_exit"
|
|
142
|
-
})
|
|
143
|
-
end
|
|
95
|
+
attrs = {}
|
|
96
|
+
context&.each { |k, v| attrs[k.to_s] = v.to_s }
|
|
144
97
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
s[0, max] + "...[truncated]"
|
|
98
|
+
gauge = @meter.create_gauge(name.to_s, unit: unit.to_s)
|
|
99
|
+
gauge.set(value.to_f, attributes: attrs)
|
|
148
100
|
end
|
|
149
101
|
|
|
150
|
-
def
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
warn("[obtrace-sdk-ruby] queue full, dropping oldest item") if @cfg.debug
|
|
155
|
-
end
|
|
156
|
-
@queue << { endpoint: endpoint, payload: payload.dup.freeze }
|
|
102
|
+
def span(name, attrs: nil, &block)
|
|
103
|
+
span_attrs = {}
|
|
104
|
+
if attrs
|
|
105
|
+
attrs.each { |k, v| span_attrs[k.to_s] = v.is_a?(Numeric) || v.is_a?(TrueClass) || v.is_a?(FalseClass) ? v : v.to_s }
|
|
157
106
|
end
|
|
158
|
-
end
|
|
159
107
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
return @http if @http.started?
|
|
164
|
-
rescue StandardError
|
|
108
|
+
if block
|
|
109
|
+
@tracer.in_span(name.to_s, attributes: span_attrs) do |s|
|
|
110
|
+
block.call(s)
|
|
165
111
|
end
|
|
112
|
+
else
|
|
113
|
+
@tracer.in_span(name.to_s, attributes: span_attrs) { |_s| }
|
|
166
114
|
end
|
|
167
|
-
|
|
168
|
-
@http.finish rescue nil if @http
|
|
169
|
-
@http = Net::HTTP.new(uri.host, uri.port)
|
|
170
|
-
@http.use_ssl = uri.scheme == "https"
|
|
171
|
-
@http.read_timeout = @cfg.request_timeout_sec
|
|
172
|
-
@http.start
|
|
173
|
-
@http_uri = uri
|
|
174
|
-
@http
|
|
175
115
|
end
|
|
176
116
|
|
|
177
|
-
def
|
|
178
|
-
|
|
179
|
-
http = connection_for(uri)
|
|
180
|
-
req = Net::HTTP::Post.new(uri.request_uri)
|
|
181
|
-
req["Authorization"] = "Bearer #{@cfg.api_key}"
|
|
182
|
-
req["Content-Type"] = "application/json"
|
|
183
|
-
(@cfg.default_headers || {}).each { |k, v| req[k] = v.to_s }
|
|
184
|
-
req.body = JSON.generate(item[:payload])
|
|
117
|
+
def capture_error(exception, context = nil)
|
|
118
|
+
exception = Exception.new(exception.to_s) unless exception.is_a?(Exception)
|
|
185
119
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
end
|
|
193
|
-
true
|
|
194
|
-
rescue StandardError => e
|
|
195
|
-
if retries < 2
|
|
196
|
-
retries += 1
|
|
197
|
-
sleep 1
|
|
198
|
-
@http.finish rescue nil
|
|
199
|
-
@http = nil
|
|
200
|
-
http = connection_for(uri)
|
|
201
|
-
retry
|
|
202
|
-
end
|
|
203
|
-
warn("[obtrace-sdk-ruby] send failed endpoint=#{item[:endpoint]} err=#{e.message}") if @cfg.debug
|
|
204
|
-
false
|
|
120
|
+
attrs = {}
|
|
121
|
+
context&.each { |k, v| attrs[k.to_s] = v.to_s }
|
|
122
|
+
|
|
123
|
+
@tracer.in_span("error", attributes: attrs) do |s|
|
|
124
|
+
s.record_exception(exception)
|
|
125
|
+
s.status = OpenTelemetry::Trace::Status.error(exception.message.to_s)
|
|
205
126
|
end
|
|
206
127
|
end
|
|
128
|
+
|
|
129
|
+
alias_method :capture_exception, :capture_error
|
|
130
|
+
|
|
131
|
+
def shutdown
|
|
132
|
+
@tracer_provider.shutdown if @tracer_provider.respond_to?(:shutdown)
|
|
133
|
+
end
|
|
207
134
|
end
|
|
208
135
|
end
|
|
@@ -3,84 +3,58 @@ module ObtraceSDK
|
|
|
3
3
|
def initialize(app, client)
|
|
4
4
|
@app = app
|
|
5
5
|
@client = client
|
|
6
|
+
@tracer = client.tracer
|
|
6
7
|
end
|
|
7
8
|
|
|
8
9
|
def call(env)
|
|
9
|
-
|
|
10
|
-
span_id = Context.random_hex(8)
|
|
11
|
-
start_ns = Otlp.now_unix_nano
|
|
12
|
-
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
10
|
+
span_name = "#{env["REQUEST_METHOD"]} #{env["PATH_INFO"]}"
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
error = nil
|
|
12
|
+
extracted_context = extract_context(env)
|
|
16
13
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
duration_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time) * 1000).round(2)
|
|
26
|
-
status_code = error ? 500 : status.to_i
|
|
14
|
+
span_attrs = {
|
|
15
|
+
"http.method" => env["REQUEST_METHOD"].to_s,
|
|
16
|
+
"http.target" => env["PATH_INFO"].to_s,
|
|
17
|
+
"http.host" => env["HTTP_HOST"].to_s,
|
|
18
|
+
"http.scheme" => (env["rack.url_scheme"] || "http").to_s,
|
|
19
|
+
"http.user_agent" => env["HTTP_USER_AGENT"].to_s,
|
|
20
|
+
"net.peer.ip" => (env["HTTP_X_FORWARDED_FOR"] || env["REMOTE_ADDR"]).to_s
|
|
21
|
+
}
|
|
27
22
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"http.host" => env["HTTP_HOST"].to_s,
|
|
32
|
-
"http.scheme" => env["rack.url_scheme"].to_s,
|
|
33
|
-
"http.status_code" => status_code,
|
|
34
|
-
"http.duration_ms" => duration_ms,
|
|
35
|
-
"http.user_agent" => env["HTTP_USER_AGENT"].to_s,
|
|
36
|
-
"net.peer.ip" => (env["HTTP_X_FORWARDED_FOR"] || env["REMOTE_ADDR"]).to_s
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if error
|
|
40
|
-
attrs["error"] = true
|
|
41
|
-
attrs["error.type"] = error.class.to_s
|
|
42
|
-
attrs["error.message"] = error.message.to_s
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
if env["QUERY_STRING"] && !env["QUERY_STRING"].empty?
|
|
46
|
-
attrs["http.query"] = env["QUERY_STRING"]
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
@client.span(
|
|
50
|
-
"#{env["REQUEST_METHOD"]} #{env["PATH_INFO"]}",
|
|
51
|
-
trace_id: trace_id,
|
|
52
|
-
span_id: span_id,
|
|
53
|
-
start_unix_nano: start_ns,
|
|
54
|
-
end_unix_nano: end_ns,
|
|
55
|
-
status_code: status_code >= 400 ? status_code : nil,
|
|
56
|
-
status_message: error ? error.message : "",
|
|
57
|
-
attrs: attrs
|
|
58
|
-
)
|
|
23
|
+
if env["QUERY_STRING"] && !env["QUERY_STRING"].empty?
|
|
24
|
+
span_attrs["http.query"] = env["QUERY_STRING"]
|
|
25
|
+
end
|
|
59
26
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
27
|
+
OpenTelemetry::Context.with_current(extracted_context) do
|
|
28
|
+
@tracer.in_span(span_name, attributes: span_attrs, kind: :server) do |s|
|
|
29
|
+
begin
|
|
30
|
+
status, headers, body = @app.call(env)
|
|
31
|
+
s.set_attribute("http.status_code", status.to_i)
|
|
32
|
+
if status.to_i >= 500
|
|
33
|
+
s.status = OpenTelemetry::Trace::Status.error("HTTP #{status}")
|
|
34
|
+
end
|
|
35
|
+
[status, headers, body]
|
|
36
|
+
rescue => e
|
|
37
|
+
s.record_exception(e)
|
|
38
|
+
s.status = OpenTelemetry::Trace::Status.error(e.message.to_s)
|
|
39
|
+
s.set_attribute("http.status_code", 500)
|
|
40
|
+
raise
|
|
41
|
+
end
|
|
66
42
|
end
|
|
67
|
-
|
|
68
|
-
@client.log(
|
|
69
|
-
level,
|
|
70
|
-
"#{env["REQUEST_METHOD"]} #{env["PATH_INFO"]} #{status_code} #{duration_ms}ms",
|
|
71
|
-
attrs
|
|
72
|
-
)
|
|
73
43
|
end
|
|
74
44
|
end
|
|
75
45
|
|
|
76
46
|
private
|
|
77
47
|
|
|
78
|
-
def
|
|
48
|
+
def extract_context(env)
|
|
79
49
|
traceparent = env["HTTP_TRACEPARENT"]
|
|
80
|
-
return
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
50
|
+
return OpenTelemetry::Context.current unless traceparent
|
|
51
|
+
|
|
52
|
+
OpenTelemetry.propagation.extract(
|
|
53
|
+
env,
|
|
54
|
+
getter: OpenTelemetry::Common::Propagation.rack_env_getter
|
|
55
|
+
)
|
|
56
|
+
rescue
|
|
57
|
+
OpenTelemetry::Context.current
|
|
84
58
|
end
|
|
85
59
|
end
|
|
86
60
|
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
require "opentelemetry/sdk"
|
|
2
|
+
require "opentelemetry-exporter-otlp"
|
|
3
|
+
|
|
4
|
+
module ObtraceSDK
|
|
5
|
+
module OtelSetup
|
|
6
|
+
module_function
|
|
7
|
+
|
|
8
|
+
def configure(cfg)
|
|
9
|
+
endpoint = "#{cfg.ingest_base_url.to_s.sub(%r{/$}, "")}/otlp/v1/traces"
|
|
10
|
+
|
|
11
|
+
exporter = OpenTelemetry::Exporter::OTLP::Exporter.new(
|
|
12
|
+
endpoint: endpoint,
|
|
13
|
+
headers: {
|
|
14
|
+
"Authorization" => "Bearer #{cfg.api_key}"
|
|
15
|
+
}.merge(cfg.default_headers || {}),
|
|
16
|
+
timeout: cfg.request_timeout_sec
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
resource_attrs = {
|
|
20
|
+
"service.name" => cfg.service_name,
|
|
21
|
+
"service.version" => cfg.service_version,
|
|
22
|
+
"deployment.environment" => cfg.env || "dev",
|
|
23
|
+
"runtime.name" => "ruby"
|
|
24
|
+
}
|
|
25
|
+
resource_attrs["obtrace.tenant_id"] = cfg.tenant_id if cfg.tenant_id
|
|
26
|
+
resource_attrs["obtrace.project_id"] = cfg.project_id if cfg.project_id
|
|
27
|
+
resource_attrs["obtrace.app_id"] = cfg.app_id if cfg.app_id
|
|
28
|
+
resource_attrs["obtrace.env"] = cfg.env if cfg.env
|
|
29
|
+
|
|
30
|
+
OpenTelemetry::SDK.configure do |c|
|
|
31
|
+
c.resource = OpenTelemetry::SDK::Resources::Resource.create(resource_attrs)
|
|
32
|
+
c.add_span_processor(
|
|
33
|
+
OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new(exporter)
|
|
34
|
+
)
|
|
35
|
+
auto_detect_instrumentations(c)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
OpenTelemetry.propagation = OpenTelemetry::Trace::Propagation::TraceContext.text_map_propagator
|
|
39
|
+
|
|
40
|
+
OpenTelemetry.tracer_provider
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def auto_detect_instrumentations(config)
|
|
44
|
+
instrumentations = [
|
|
45
|
+
["OpenTelemetry::Instrumentation::Net::HTTP", "opentelemetry-instrumentation-net_http"],
|
|
46
|
+
["OpenTelemetry::Instrumentation::Rack", "opentelemetry-instrumentation-rack"],
|
|
47
|
+
["OpenTelemetry::Instrumentation::Rails", "opentelemetry-instrumentation-rails"],
|
|
48
|
+
["OpenTelemetry::Instrumentation::PG", "opentelemetry-instrumentation-pg"],
|
|
49
|
+
["OpenTelemetry::Instrumentation::Redis", "opentelemetry-instrumentation-redis"],
|
|
50
|
+
["OpenTelemetry::Instrumentation::Sidekiq", "opentelemetry-instrumentation-sidekiq"],
|
|
51
|
+
["OpenTelemetry::Instrumentation::Faraday", "opentelemetry-instrumentation-faraday"],
|
|
52
|
+
["OpenTelemetry::Instrumentation::ActiveRecord", "opentelemetry-instrumentation-active_record"]
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
threads = instrumentations.map do |class_name, gem_name|
|
|
56
|
+
Thread.new(class_name, gem_name) do |cn, gn|
|
|
57
|
+
begin
|
|
58
|
+
require gn.gsub("-", "/")
|
|
59
|
+
rescue LoadError
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
threads.each(&:join)
|
|
64
|
+
|
|
65
|
+
instrumentations.each do |class_name, gem_name|
|
|
66
|
+
klass = Object.const_get(class_name) rescue next
|
|
67
|
+
config.use(class_name) if klass
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
data/lib/obtrace_sdk/rails.rb
CHANGED
|
@@ -2,10 +2,10 @@ require "obtrace_sdk"
|
|
|
2
2
|
|
|
3
3
|
module ObtraceSDK
|
|
4
4
|
class Railtie < ::Rails::Railtie
|
|
5
|
-
|
|
6
|
-
api_key = credentials_fetch("obtrace_api_key") || ENV["OBTRACE_API_KEY"]
|
|
7
|
-
ingest_url = credentials_fetch("obtrace_ingest_url") || ENV["OBTRACE_INGEST_BASE_URL"] || "https://inject.obtrace.ai"
|
|
8
|
-
service_name = credentials_fetch("obtrace_service_name") || ENV["OBTRACE_SERVICE_NAME"] || ::Rails.application.class.module_parent_name.underscore rescue "rails-app"
|
|
5
|
+
config.after_initialize do |app|
|
|
6
|
+
api_key = Railtie.credentials_fetch("obtrace_api_key") || ENV["OBTRACE_API_KEY"]
|
|
7
|
+
ingest_url = Railtie.credentials_fetch("obtrace_ingest_url") || ENV["OBTRACE_INGEST_BASE_URL"] || "https://inject.obtrace.ai"
|
|
8
|
+
service_name = Railtie.credentials_fetch("obtrace_service_name") || ENV["OBTRACE_SERVICE_NAME"] || ::Rails.application.class.module_parent_name.underscore rescue "rails-app"
|
|
9
9
|
|
|
10
10
|
next unless api_key
|
|
11
11
|
|
|
@@ -14,9 +14,9 @@ module ObtraceSDK
|
|
|
14
14
|
ingest_base_url: ingest_url,
|
|
15
15
|
service_name: service_name,
|
|
16
16
|
env: ::Rails.env.to_s,
|
|
17
|
-
tenant_id: credentials_fetch("obtrace_tenant_id") || ENV["OBTRACE_TENANT_ID"],
|
|
18
|
-
project_id: credentials_fetch("obtrace_project_id") || ENV["OBTRACE_PROJECT_ID"],
|
|
19
|
-
app_id: credentials_fetch("obtrace_app_id") || ENV["OBTRACE_APP_ID"],
|
|
17
|
+
tenant_id: Railtie.credentials_fetch("obtrace_tenant_id") || ENV["OBTRACE_TENANT_ID"],
|
|
18
|
+
project_id: Railtie.credentials_fetch("obtrace_project_id") || ENV["OBTRACE_PROJECT_ID"],
|
|
19
|
+
app_id: Railtie.credentials_fetch("obtrace_app_id") || ENV["OBTRACE_APP_ID"],
|
|
20
20
|
debug: ENV["OBTRACE_DEBUG"] == "true"
|
|
21
21
|
)
|
|
22
22
|
|
|
@@ -26,7 +26,7 @@ module ObtraceSDK
|
|
|
26
26
|
app.middleware.insert(0, ObtraceSDK::Middleware, client)
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
def credentials_fetch(key)
|
|
29
|
+
def self.Railtie.credentials_fetch(key)
|
|
30
30
|
::Rails.application.credentials.send(key) rescue nil
|
|
31
31
|
end
|
|
32
32
|
end
|
data/lib/obtrace_sdk/types.rb
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
module ObtraceSDK
|
|
2
2
|
class Config
|
|
3
3
|
attr_accessor :api_key, :ingest_base_url, :tenant_id, :project_id, :app_id, :env
|
|
4
|
-
attr_accessor :service_name, :service_version, :
|
|
4
|
+
attr_accessor :service_name, :service_version, :request_timeout_sec
|
|
5
5
|
attr_accessor :default_headers, :debug, :validate_semantic_metrics
|
|
6
|
-
attr_accessor :auto_instrument_http, :auto_capture_logs
|
|
7
6
|
|
|
8
|
-
def initialize(api_key:, ingest_base_url:, service_name:, tenant_id: nil, project_id: nil, app_id: nil, env: "dev", service_version: "1.0.0",
|
|
7
|
+
def initialize(api_key:, ingest_base_url:, service_name:, tenant_id: nil, project_id: nil, app_id: nil, env: "dev", service_version: "1.0.0", request_timeout_sec: 5, default_headers: {}, validate_semantic_metrics: false, debug: false)
|
|
9
8
|
@api_key = api_key
|
|
10
9
|
@ingest_base_url = ingest_base_url
|
|
11
10
|
@tenant_id = tenant_id
|
|
@@ -14,13 +13,10 @@ module ObtraceSDK
|
|
|
14
13
|
@env = env
|
|
15
14
|
@service_name = service_name
|
|
16
15
|
@service_version = service_version
|
|
17
|
-
@max_queue_size = max_queue_size
|
|
18
16
|
@request_timeout_sec = request_timeout_sec
|
|
19
17
|
@default_headers = default_headers
|
|
20
18
|
@validate_semantic_metrics = validate_semantic_metrics
|
|
21
19
|
@debug = debug
|
|
22
|
-
@auto_instrument_http = auto_instrument_http
|
|
23
|
-
@auto_capture_logs = auto_capture_logs
|
|
24
20
|
end
|
|
25
21
|
end
|
|
26
22
|
end
|
data/lib/obtrace_sdk/version.rb
CHANGED
data/lib/obtrace_sdk.rb
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
|
+
require_relative "obtrace_sdk/version"
|
|
1
2
|
require_relative "obtrace_sdk/types"
|
|
2
|
-
require_relative "obtrace_sdk/context"
|
|
3
|
-
require_relative "obtrace_sdk/otlp"
|
|
4
|
-
require_relative "obtrace_sdk/http_instrumentation"
|
|
5
|
-
require_relative "obtrace_sdk/logger_capture"
|
|
6
|
-
require_relative "obtrace_sdk/middleware"
|
|
7
|
-
require_relative "obtrace_sdk/client"
|
|
8
|
-
require_relative "obtrace_sdk/framework"
|
|
9
3
|
require_relative "obtrace_sdk/semantic_metrics"
|
|
4
|
+
require_relative "obtrace_sdk/otel_setup"
|
|
5
|
+
require_relative "obtrace_sdk/client"
|
|
6
|
+
require_relative "obtrace_sdk/middleware"
|
metadata
CHANGED
|
@@ -1,23 +1,65 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: obtrace-sdk-ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Obtrace
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-03-
|
|
11
|
+
date: 2026-03-29 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
|
-
name:
|
|
14
|
+
name: opentelemetry-sdk
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '1.3'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '1.3'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: opentelemetry-api
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '1.3'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '1.3'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: opentelemetry-exporter-otlp
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0.26'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0.26'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: opentelemetry-instrumentation-net_http
|
|
15
57
|
requirement: !ruby/object:Gem::Requirement
|
|
16
58
|
requirements:
|
|
17
59
|
- - ">="
|
|
18
60
|
- !ruby/object:Gem::Version
|
|
19
61
|
version: '0'
|
|
20
|
-
type: :
|
|
62
|
+
type: :development
|
|
21
63
|
prerelease: false
|
|
22
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
65
|
requirements:
|
|
@@ -25,13 +67,27 @@ dependencies:
|
|
|
25
67
|
- !ruby/object:Gem::Version
|
|
26
68
|
version: '0'
|
|
27
69
|
- !ruby/object:Gem::Dependency
|
|
28
|
-
name:
|
|
70
|
+
name: opentelemetry-instrumentation-rack
|
|
29
71
|
requirement: !ruby/object:Gem::Requirement
|
|
30
72
|
requirements:
|
|
31
73
|
- - ">="
|
|
32
74
|
- !ruby/object:Gem::Version
|
|
33
75
|
version: '0'
|
|
34
|
-
type: :
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: opentelemetry-instrumentation-rails
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0'
|
|
90
|
+
type: :development
|
|
35
91
|
prerelease: false
|
|
36
92
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
93
|
requirements:
|
|
@@ -50,12 +106,8 @@ files:
|
|
|
50
106
|
- README.md
|
|
51
107
|
- lib/obtrace_sdk.rb
|
|
52
108
|
- lib/obtrace_sdk/client.rb
|
|
53
|
-
- lib/obtrace_sdk/context.rb
|
|
54
|
-
- lib/obtrace_sdk/framework.rb
|
|
55
|
-
- lib/obtrace_sdk/http_instrumentation.rb
|
|
56
|
-
- lib/obtrace_sdk/logger_capture.rb
|
|
57
109
|
- lib/obtrace_sdk/middleware.rb
|
|
58
|
-
- lib/obtrace_sdk/
|
|
110
|
+
- lib/obtrace_sdk/otel_setup.rb
|
|
59
111
|
- lib/obtrace_sdk/rails.rb
|
|
60
112
|
- lib/obtrace_sdk/semantic_metrics.rb
|
|
61
113
|
- lib/obtrace_sdk/types.rb
|
data/lib/obtrace_sdk/context.rb
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
require "securerandom"
|
|
2
|
-
|
|
3
|
-
module ObtraceSDK
|
|
4
|
-
module Context
|
|
5
|
-
module_function
|
|
6
|
-
|
|
7
|
-
def random_hex(bytes)
|
|
8
|
-
SecureRandom.hex(bytes)
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def ensure_propagation_headers(headers = {}, trace_id: nil, span_id: nil, session_id: nil)
|
|
12
|
-
out = (headers || {}).dup
|
|
13
|
-
out["traceparent"] ||= "00-#{trace_id || random_hex(16)}-#{span_id || random_hex(8)}-01"
|
|
14
|
-
out["x-obtrace-session-id"] ||= session_id if session_id && !session_id.empty?
|
|
15
|
-
out
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
end
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
module ObtraceSDK
|
|
2
|
-
module Framework
|
|
3
|
-
module_function
|
|
4
|
-
|
|
5
|
-
# Rack-compatible middleware baseline used by Rails.
|
|
6
|
-
def rack_middleware(client, app)
|
|
7
|
-
lambda do |env|
|
|
8
|
-
client.log("info", "request.start", { method: env["REQUEST_METHOD"], path: env["PATH_INFO"] })
|
|
9
|
-
status, headers, body = app.call(env)
|
|
10
|
-
client.log("info", "request.finish", { status: status.to_i })
|
|
11
|
-
[status, headers, body]
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
require "net/http"
|
|
2
|
-
|
|
3
|
-
module ObtraceSDK
|
|
4
|
-
module HttpInstrumentation
|
|
5
|
-
@client = nil
|
|
6
|
-
@installed = false
|
|
7
|
-
|
|
8
|
-
module_function
|
|
9
|
-
|
|
10
|
-
def install(client)
|
|
11
|
-
return if @installed
|
|
12
|
-
@client = client
|
|
13
|
-
@installed = true
|
|
14
|
-
|
|
15
|
-
Net::HTTP.prepend(NetHttpPatch)
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def client
|
|
19
|
-
@client
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def installed?
|
|
23
|
-
@installed
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def reset!
|
|
27
|
-
@client = nil
|
|
28
|
-
@installed = false
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
module NetHttpPatch
|
|
32
|
-
def request(req, body = nil, &block)
|
|
33
|
-
client = ObtraceSDK::HttpInstrumentation.client
|
|
34
|
-
unless client
|
|
35
|
-
return super
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
uri = URI.parse("#{use_ssl? ? 'https' : 'http'}://#{address}:#{port}#{req.path}")
|
|
39
|
-
|
|
40
|
-
if uri.host && ObtraceSDK::HttpInstrumentation.own_endpoint?(uri)
|
|
41
|
-
return super
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
trace_id = ObtraceSDK::Context.random_hex(16)
|
|
45
|
-
span_id = ObtraceSDK::Context.random_hex(8)
|
|
46
|
-
|
|
47
|
-
req["traceparent"] ||= "00-#{trace_id}-#{span_id}-01"
|
|
48
|
-
|
|
49
|
-
start_ns = ObtraceSDK::Otlp.now_unix_nano
|
|
50
|
-
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
51
|
-
status_code = nil
|
|
52
|
-
error = nil
|
|
53
|
-
|
|
54
|
-
begin
|
|
55
|
-
response = super
|
|
56
|
-
status_code = response.code.to_i
|
|
57
|
-
response
|
|
58
|
-
rescue => e
|
|
59
|
-
error = e
|
|
60
|
-
raise
|
|
61
|
-
ensure
|
|
62
|
-
end_ns = ObtraceSDK::Otlp.now_unix_nano
|
|
63
|
-
duration_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time) * 1000).round(2)
|
|
64
|
-
|
|
65
|
-
attrs = {
|
|
66
|
-
"http.method" => req.method,
|
|
67
|
-
"http.url" => uri.to_s,
|
|
68
|
-
"http.host" => uri.host.to_s,
|
|
69
|
-
"net.peer.port" => uri.port.to_s,
|
|
70
|
-
"http.duration_ms" => duration_ms,
|
|
71
|
-
"auto.source" => "http_instrumentation"
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if status_code
|
|
75
|
-
attrs["http.status_code"] = status_code
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
if error
|
|
79
|
-
attrs["error"] = true
|
|
80
|
-
attrs["error.type"] = error.class.to_s
|
|
81
|
-
attrs["error.message"] = error.message.to_s
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
span_status = if error
|
|
85
|
-
status_code = 500
|
|
86
|
-
500
|
|
87
|
-
elsif status_code && status_code >= 400
|
|
88
|
-
status_code
|
|
89
|
-
else
|
|
90
|
-
nil
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
client.span(
|
|
94
|
-
"HTTP #{req.method}",
|
|
95
|
-
trace_id: trace_id,
|
|
96
|
-
span_id: span_id,
|
|
97
|
-
start_unix_nano: start_ns,
|
|
98
|
-
end_unix_nano: end_ns,
|
|
99
|
-
status_code: span_status,
|
|
100
|
-
status_message: error ? error.message : "",
|
|
101
|
-
attrs: attrs
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
client.log(
|
|
105
|
-
status_code && status_code >= 400 ? "warn" : "info",
|
|
106
|
-
"HTTP #{req.method} #{uri.host}#{uri.path} #{status_code || 'ERR'}",
|
|
107
|
-
attrs
|
|
108
|
-
)
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
def self.own_endpoint?(uri)
|
|
114
|
-
return false unless @client
|
|
115
|
-
cfg = @client.instance_variable_get(:@cfg)
|
|
116
|
-
return false unless cfg
|
|
117
|
-
ingest_uri = URI.parse(cfg.ingest_base_url.to_s) rescue nil
|
|
118
|
-
return false unless ingest_uri
|
|
119
|
-
uri.host == ingest_uri.host && uri.port == ingest_uri.port
|
|
120
|
-
end
|
|
121
|
-
end
|
|
122
|
-
end
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
require "logger"
|
|
2
|
-
|
|
3
|
-
module ObtraceSDK
|
|
4
|
-
module LoggerCapture
|
|
5
|
-
@client = nil
|
|
6
|
-
@installed = false
|
|
7
|
-
|
|
8
|
-
SEVERITY_MAP = {
|
|
9
|
-
0 => "debug",
|
|
10
|
-
1 => "info",
|
|
11
|
-
2 => "warn",
|
|
12
|
-
3 => "error",
|
|
13
|
-
4 => "fatal",
|
|
14
|
-
5 => "unknown"
|
|
15
|
-
}.freeze
|
|
16
|
-
|
|
17
|
-
module_function
|
|
18
|
-
|
|
19
|
-
def install(client)
|
|
20
|
-
return if @installed
|
|
21
|
-
@client = client
|
|
22
|
-
@installed = true
|
|
23
|
-
|
|
24
|
-
::Logger.prepend(LoggerPatch)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def client
|
|
28
|
-
@client
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def installed?
|
|
32
|
-
@installed
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def reset!
|
|
36
|
-
@client = nil
|
|
37
|
-
@installed = false
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
module LoggerPatch
|
|
41
|
-
def add(severity, message = nil, progname = nil, &block)
|
|
42
|
-
result = super
|
|
43
|
-
|
|
44
|
-
client = ObtraceSDK::LoggerCapture.client
|
|
45
|
-
if client
|
|
46
|
-
if message.nil?
|
|
47
|
-
if block
|
|
48
|
-
msg = block.call
|
|
49
|
-
else
|
|
50
|
-
msg = progname
|
|
51
|
-
end
|
|
52
|
-
else
|
|
53
|
-
msg = message
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
if msg && !msg.to_s.empty?
|
|
57
|
-
level = ObtraceSDK::LoggerCapture::SEVERITY_MAP[severity] || "unknown"
|
|
58
|
-
attrs = { "auto.source" => "logger_capture" }
|
|
59
|
-
attrs["logger.progname"] = progname.to_s if progname && message
|
|
60
|
-
client.log(level, msg.to_s, attrs)
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
result
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
end
|
data/lib/obtrace_sdk/otlp.rb
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
module ObtraceSDK
|
|
2
|
-
module Otlp
|
|
3
|
-
module_function
|
|
4
|
-
|
|
5
|
-
def attrs(hash)
|
|
6
|
-
return [] if hash.nil?
|
|
7
|
-
hash.map do |k, v|
|
|
8
|
-
value =
|
|
9
|
-
case v
|
|
10
|
-
when TrueClass, FalseClass
|
|
11
|
-
{ "boolValue" => v }
|
|
12
|
-
when Numeric
|
|
13
|
-
{ "doubleValue" => v.to_f }
|
|
14
|
-
else
|
|
15
|
-
{ "stringValue" => v.to_s }
|
|
16
|
-
end
|
|
17
|
-
{ "key" => k.to_s, "value" => value }
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def resource(cfg)
|
|
22
|
-
base = {
|
|
23
|
-
"service.name" => cfg.service_name,
|
|
24
|
-
"service.version" => cfg.service_version,
|
|
25
|
-
"deployment.environment" => cfg.env || "dev",
|
|
26
|
-
"runtime.name" => "ruby"
|
|
27
|
-
}
|
|
28
|
-
base["obtrace.tenant_id"] = cfg.tenant_id if cfg.tenant_id
|
|
29
|
-
base["obtrace.project_id"] = cfg.project_id if cfg.project_id
|
|
30
|
-
base["obtrace.app_id"] = cfg.app_id if cfg.app_id
|
|
31
|
-
base["obtrace.env"] = cfg.env if cfg.env
|
|
32
|
-
attrs(base)
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def now_unix_nano
|
|
36
|
-
(Time.now.to_f * 1_000_000_000).to_i.to_s
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def logs_payload(cfg, level, message, context = nil)
|
|
40
|
-
context_attrs = { "obtrace.log.level" => level }
|
|
41
|
-
if context
|
|
42
|
-
context.each { |k, v| context_attrs["obtrace.attr.#{k}"] = v }
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
{
|
|
46
|
-
"resourceLogs" => [
|
|
47
|
-
{
|
|
48
|
-
"resource" => { "attributes" => resource(cfg) },
|
|
49
|
-
"scopeLogs" => [
|
|
50
|
-
{
|
|
51
|
-
"scope" => { "name" => "obtrace-sdk-ruby", "version" => "1.0.0" },
|
|
52
|
-
"logRecords" => [
|
|
53
|
-
{
|
|
54
|
-
"timeUnixNano" => now_unix_nano,
|
|
55
|
-
"severityText" => level.to_s.upcase,
|
|
56
|
-
"body" => { "stringValue" => message.to_s },
|
|
57
|
-
"attributes" => attrs(context_attrs)
|
|
58
|
-
}
|
|
59
|
-
]
|
|
60
|
-
}
|
|
61
|
-
]
|
|
62
|
-
}
|
|
63
|
-
]
|
|
64
|
-
}
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def metric_payload(cfg, name, value, unit = "1", context = nil)
|
|
68
|
-
{
|
|
69
|
-
"resourceMetrics" => [
|
|
70
|
-
{
|
|
71
|
-
"resource" => { "attributes" => resource(cfg) },
|
|
72
|
-
"scopeMetrics" => [
|
|
73
|
-
{
|
|
74
|
-
"scope" => { "name" => "obtrace-sdk-ruby", "version" => "1.0.0" },
|
|
75
|
-
"metrics" => [
|
|
76
|
-
{
|
|
77
|
-
"name" => name.to_s,
|
|
78
|
-
"unit" => unit.to_s,
|
|
79
|
-
"gauge" => {
|
|
80
|
-
"dataPoints" => [
|
|
81
|
-
{
|
|
82
|
-
"timeUnixNano" => now_unix_nano,
|
|
83
|
-
"asDouble" => value.to_f,
|
|
84
|
-
"attributes" => attrs(context || {})
|
|
85
|
-
}
|
|
86
|
-
]
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
]
|
|
90
|
-
}
|
|
91
|
-
]
|
|
92
|
-
}
|
|
93
|
-
]
|
|
94
|
-
}
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
def span_payload(cfg, name, trace_id, span_id, start_unix_nano, end_unix_nano, status_code = nil, status_message = "", attrs_hash = nil)
|
|
98
|
-
{
|
|
99
|
-
"resourceSpans" => [
|
|
100
|
-
{
|
|
101
|
-
"resource" => { "attributes" => resource(cfg) },
|
|
102
|
-
"scopeSpans" => [
|
|
103
|
-
{
|
|
104
|
-
"scope" => { "name" => "obtrace-sdk-ruby", "version" => "1.0.0" },
|
|
105
|
-
"spans" => [
|
|
106
|
-
{
|
|
107
|
-
"traceId" => trace_id,
|
|
108
|
-
"spanId" => span_id,
|
|
109
|
-
"name" => name.to_s,
|
|
110
|
-
"kind" => 3,
|
|
111
|
-
"startTimeUnixNano" => start_unix_nano,
|
|
112
|
-
"endTimeUnixNano" => end_unix_nano,
|
|
113
|
-
"attributes" => attrs(attrs_hash || {}),
|
|
114
|
-
"status" => {
|
|
115
|
-
"code" => status_code && status_code.to_i >= 400 ? 2 : 1,
|
|
116
|
-
"message" => status_message.to_s
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
]
|
|
120
|
-
}
|
|
121
|
-
]
|
|
122
|
-
}
|
|
123
|
-
]
|
|
124
|
-
}
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
end
|