instana 1.217.0 → 2.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 +4 -4
- data/.circleci/config.yml +101 -225
- data/.tekton/.currency/scripts/generate_report.py +194 -22
- data/.tekton/pipeline.yaml +65 -152
- data/.tekton/prepuller-restart-service-account.yaml +31 -0
- data/.tekton/ruby-tracer-prepuller-cronjob.yaml +20 -0
- data/.tekton/ruby-tracer-prepuller.yaml +27 -26
- data/.tekton/task.yaml +31 -27
- data/Gemfile +5 -0
- data/README.md +2 -1
- data/Rakefile +1 -1
- data/examples/otel.rb +98 -0
- data/examples/tracing.rb +1 -0
- data/gemfiles/rails_61.gemfile +1 -0
- data/gemfiles/rails_70.gemfile +1 -0
- data/instana.gemspec +5 -0
- data/lib/instana/base.rb +4 -2
- data/lib/instana/instrumentation/action_cable.rb +8 -4
- data/lib/instana/instrumentation/action_controller.rb +2 -4
- data/lib/instana/instrumentation/action_mailer.rb +1 -1
- data/lib/instana/instrumentation/action_view.rb +4 -4
- data/lib/instana/instrumentation/active_job.rb +26 -13
- data/lib/instana/instrumentation/active_record.rb +1 -1
- data/lib/instana/instrumentation/aws_sdk_dynamodb.rb +1 -1
- data/lib/instana/instrumentation/aws_sdk_lambda.rb +1 -1
- data/lib/instana/instrumentation/aws_sdk_s3.rb +1 -1
- data/lib/instana/instrumentation/aws_sdk_sns.rb +1 -1
- data/lib/instana/instrumentation/aws_sdk_sqs.rb +1 -1
- data/lib/instana/instrumentation/dalli.rb +1 -1
- data/lib/instana/instrumentation/excon.rb +2 -2
- data/lib/instana/instrumentation/graphql.rb +3 -3
- data/lib/instana/instrumentation/grpc.rb +14 -13
- data/lib/instana/instrumentation/mongo.rb +3 -3
- data/lib/instana/instrumentation/net-http.rb +5 -4
- data/lib/instana/instrumentation/rack.rb +36 -4
- data/lib/instana/instrumentation/redis.rb +1 -1
- data/lib/instana/instrumentation/resque.rb +10 -8
- data/lib/instana/instrumentation/rest-client.rb +4 -4
- data/lib/instana/instrumentation/sequel.rb +3 -3
- data/lib/instana/instrumentation/shoryuken.rb +4 -1
- data/lib/instana/instrumentation/sidekiq-client.rb +21 -19
- data/lib/instana/instrumentation/sidekiq-worker.rb +22 -21
- data/lib/instana/instrumented_logger.rb +1 -1
- data/lib/instana/samplers/result.rb +32 -0
- data/lib/instana/samplers/samplers.rb +76 -0
- data/lib/instana/serverless.rb +4 -2
- data/lib/instana/setup.rb +4 -5
- data/lib/instana/trace/export.rb +36 -0
- data/lib/instana/{tracing → trace}/processor.rb +19 -15
- data/lib/instana/trace/span.rb +532 -0
- data/lib/instana/{tracing → trace}/span_context.rb +17 -8
- data/lib/instana/trace/span_kind.rb +51 -0
- data/lib/instana/trace/span_limits.rb +63 -0
- data/lib/instana/{tracer.rb → trace/tracer.rb} +106 -54
- data/lib/instana/trace/tracer_provider.rb +198 -0
- data/lib/instana/trace.rb +74 -0
- data/lib/instana/util.rb +11 -0
- data/lib/instana/version.rb +1 -1
- data/test/frameworks/sinatra_test.rb +2 -1
- data/test/instrumentation/aws_test.rb +7 -7
- data/test/instrumentation/dalli_test.rb +8 -8
- data/test/instrumentation/excon_test.rb +3 -3
- data/test/instrumentation/graphql_test.rb +4 -4
- data/test/instrumentation/grpc_test.rb +8 -8
- data/test/instrumentation/mongo_test.rb +1 -1
- data/test/instrumentation/net_http_test.rb +6 -6
- data/test/instrumentation/rails_action_cable_test.rb +2 -2
- data/test/instrumentation/rails_action_mailer_test.rb +1 -1
- data/test/instrumentation/rails_active_job_test.rb +1 -1
- data/test/instrumentation/rails_active_record_database_missing_test.rb +2 -3
- data/test/instrumentation/rails_active_record_test.rb +8 -7
- data/test/instrumentation/redis_test.rb +7 -7
- data/test/instrumentation/resque_test.rb +5 -5
- data/test/instrumentation/rest_client_test.rb +1 -2
- data/test/instrumentation/sequel_test.rb +12 -6
- data/test/instrumentation/sidekiq-client_test.rb +2 -2
- data/test/instrumentation/sidekiq-worker_test.rb +2 -2
- data/test/{tracing → trace}/custom_test.rb +32 -25
- data/test/{tracing → trace}/id_management_test.rb +0 -2
- data/test/{tracing → trace}/instrumented_logger_test.rb +1 -1
- data/test/{tracing → trace}/processor_test.rb +6 -6
- data/test/{tracing → trace}/span_context_test.rb +3 -3
- data/test/{tracing → trace}/span_test.rb +7 -7
- data/test/{tracing → trace}/tracer_async_test.rb +43 -30
- data/test/trace/tracer_provider_test.rb +148 -0
- data/test/{tracing → trace}/tracer_test.rb +48 -37
- metadata +105 -32
- data/examples/opentracing.rb +0 -35
- data/lib/instana/open_tracing/carrier.rb +0 -7
- data/lib/instana/open_tracing/instana_tracer.rb +0 -99
- data/lib/instana/tracing/span.rb +0 -431
- data/lib/opentracing.rb +0 -32
- data/test/tracing/opentracing_test.rb +0 -382
@@ -1,14 +1,14 @@
|
|
1
|
-
# (c) Copyright IBM Corp.
|
2
|
-
# (c) Copyright Instana Inc. 2016
|
1
|
+
# (c) Copyright IBM Corp. 2025
|
3
2
|
|
4
|
-
require
|
5
|
-
require
|
3
|
+
require 'opentelemetry/trace/tracer'
|
4
|
+
require 'instana/trace/span'
|
5
|
+
require "instana/trace/span_context"
|
6
|
+
require 'opentelemetry/context'
|
6
7
|
|
7
8
|
module Instana
|
8
|
-
class Tracer
|
9
|
-
# Support ::Instana::Tracer.xxx call style for the instantiated tracer
|
9
|
+
class Tracer < OpenTelemetry::Trace::Tracer
|
10
10
|
class << self
|
11
|
-
def method_missing(method, *args, &block)
|
11
|
+
def method_missing(method, *args, &block) # rubocop:disable Style/MissingRespondToMissing
|
12
12
|
if ::Instana.tracer.respond_to?(method)
|
13
13
|
::Instana.tracer.send(method, *args, &block)
|
14
14
|
else
|
@@ -17,7 +17,9 @@ module Instana
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
def initialize(logger
|
20
|
+
def initialize(_name, _version, tracer_provider, logger = Instana.logger)
|
21
|
+
super()
|
22
|
+
@tracer_provider = tracer_provider
|
21
23
|
@current_span = Concurrent::ThreadLocalVar.new
|
22
24
|
@logger = logger
|
23
25
|
end
|
@@ -29,8 +31,8 @@ module Instana
|
|
29
31
|
|
30
32
|
# @param [Instana::Span, NilClas] v the new current span
|
31
33
|
# Set the value of the current span
|
32
|
-
def current_span=(
|
33
|
-
@current_span.value =
|
34
|
+
def current_span=(value)
|
35
|
+
@current_span.value = value
|
34
36
|
end
|
35
37
|
|
36
38
|
#######################################
|
@@ -48,10 +50,10 @@ module Instana
|
|
48
50
|
# :span_id the ID of the parent span (must be an unsigned hex-string)
|
49
51
|
# :level specifies data collection level (optional)
|
50
52
|
#
|
51
|
-
def start_or_continue_trace(name, kvs = {}, incoming_context = nil
|
53
|
+
def start_or_continue_trace(name, kvs = {}, incoming_context = nil)
|
52
54
|
span = log_start_or_continue(name, kvs, incoming_context)
|
53
55
|
yield(span)
|
54
|
-
rescue Exception => e
|
56
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
55
57
|
log_error(e)
|
56
58
|
raise
|
57
59
|
ensure
|
@@ -69,10 +71,10 @@ module Instana
|
|
69
71
|
# @param name [String, Symbol] the name of the span to start
|
70
72
|
# @param kvs [Hash] list of key values to be reported in this new span
|
71
73
|
#
|
72
|
-
def trace(name, kvs = {}
|
74
|
+
def trace(name, kvs = {})
|
73
75
|
span = log_entry(name, kvs)
|
74
76
|
yield(span)
|
75
|
-
rescue Exception => e
|
77
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
76
78
|
log_error(e)
|
77
79
|
raise
|
78
80
|
ensure
|
@@ -100,12 +102,12 @@ module Instana
|
|
100
102
|
# Handle the potential variations on `incoming_context`
|
101
103
|
if incoming_context
|
102
104
|
if incoming_context.is_a?(Hash)
|
103
|
-
|
105
|
+
unless incoming_context.empty?
|
104
106
|
parent_context = SpanContext.new(
|
105
|
-
incoming_context[:trace_id],
|
106
|
-
incoming_context[:span_id],
|
107
|
-
incoming_context[:level],
|
108
|
-
{
|
107
|
+
trace_id: incoming_context[:trace_id],
|
108
|
+
span_id: incoming_context[:span_id],
|
109
|
+
level: incoming_context[:level],
|
110
|
+
baggage: {
|
109
111
|
external_trace_id: incoming_context[:external_trace_id],
|
110
112
|
external_state: incoming_context[:external_state]
|
111
113
|
}
|
@@ -116,14 +118,14 @@ module Instana
|
|
116
118
|
end
|
117
119
|
end
|
118
120
|
|
119
|
-
if parent_context
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
121
|
+
self.current_span = if parent_context
|
122
|
+
Span.new(name, parent_context)
|
123
|
+
else
|
124
|
+
Span.new(name)
|
125
|
+
end
|
124
126
|
|
125
|
-
|
126
|
-
|
127
|
+
current_span.set_tags(kvs) unless kvs.empty?
|
128
|
+
current_span
|
127
129
|
end
|
128
130
|
|
129
131
|
# Will establish a new span as a child of the current span
|
@@ -132,13 +134,13 @@ module Instana
|
|
132
134
|
# @param name [String, Symbol] the name of the span to create
|
133
135
|
# @param kvs [Hash] list of key values to be reported in the span
|
134
136
|
#
|
135
|
-
def log_entry(name, kvs = nil,
|
137
|
+
def log_entry(name, kvs = nil, _start_time = ::Instana::Util.now_in_ms, child_of = nil)
|
136
138
|
return unless tracing? || child_of
|
137
139
|
|
138
|
-
new_span = if child_of.nil? && !
|
139
|
-
Span.new(name,
|
140
|
+
new_span = if child_of.nil? && !current_span.nil?
|
141
|
+
Span.new(name, current_span)
|
140
142
|
else
|
141
|
-
Span.new(name,
|
143
|
+
Span.new(name, child_of)
|
142
144
|
end
|
143
145
|
new_span.set_tags(kvs) if kvs
|
144
146
|
self.current_span = new_span
|
@@ -149,17 +151,19 @@ module Instana
|
|
149
151
|
# @param kvs [Hash] list of key values to be reported in the span
|
150
152
|
#
|
151
153
|
def log_info(kvs)
|
152
|
-
return unless
|
153
|
-
|
154
|
+
return unless current_span
|
155
|
+
|
156
|
+
current_span.set_tags(kvs)
|
154
157
|
end
|
155
158
|
|
156
159
|
# Add an error to the current span
|
157
160
|
#
|
158
161
|
# @param e [Exception] Add exception to the current span
|
159
162
|
#
|
160
|
-
def log_error(
|
161
|
-
return unless
|
162
|
-
|
163
|
+
def log_error(error)
|
164
|
+
return unless current_span
|
165
|
+
|
166
|
+
current_span.record_exception(error)
|
163
167
|
end
|
164
168
|
|
165
169
|
# Closes out the current span
|
@@ -171,16 +175,16 @@ module Instana
|
|
171
175
|
# @param kvs [Hash] list of key values to be reported in the span
|
172
176
|
#
|
173
177
|
def log_exit(name, kvs = {})
|
174
|
-
return unless
|
178
|
+
return unless current_span
|
175
179
|
|
176
|
-
if
|
177
|
-
@logger.warn "Span mismatch: Attempt to end #{name} span but #{
|
180
|
+
if current_span.name != name
|
181
|
+
@logger.warn "Span mismatch: Attempt to end #{name} span but #{current_span.name} is active."
|
178
182
|
end
|
179
183
|
|
180
|
-
|
181
|
-
|
184
|
+
current_span.set_tags(kvs)
|
185
|
+
current_span.close
|
182
186
|
|
183
|
-
self.current_span =
|
187
|
+
self.current_span = current_span.parent || nil
|
184
188
|
end
|
185
189
|
|
186
190
|
# Closes out the current span in the current trace
|
@@ -193,14 +197,14 @@ module Instana
|
|
193
197
|
# @param kvs [Hash] list of key values to be reported in the span
|
194
198
|
#
|
195
199
|
def log_end(name, kvs = {}, end_time = ::Instana::Util.now_in_ms)
|
196
|
-
return unless
|
200
|
+
return unless current_span
|
197
201
|
|
198
|
-
if
|
199
|
-
@logger.warn "Span mismatch: Attempt to end #{name} span but #{
|
202
|
+
if current_span.name != name
|
203
|
+
@logger.warn "Span mismatch: Attempt to end #{name} span but #{current_span.name} is active."
|
200
204
|
end
|
201
205
|
|
202
|
-
|
203
|
-
|
206
|
+
current_span.set_tags(kvs)
|
207
|
+
current_span.close(end_time)
|
204
208
|
self.current_span = nil
|
205
209
|
end
|
206
210
|
|
@@ -220,7 +224,7 @@ module Instana
|
|
220
224
|
def log_async_entry(name, kvs)
|
221
225
|
return unless tracing?
|
222
226
|
|
223
|
-
new_span = Span.new(name,
|
227
|
+
new_span = Span.new(name, current_span)
|
224
228
|
new_span.set_tags(kvs) unless kvs.empty?
|
225
229
|
new_span
|
226
230
|
end
|
@@ -239,8 +243,8 @@ module Instana
|
|
239
243
|
# @param e [Exception] Add exception to the current span
|
240
244
|
# @param span [Span] the span for this Async op (previously returned from `log_async_entry`)
|
241
245
|
#
|
242
|
-
def log_async_error(
|
243
|
-
span.
|
246
|
+
def log_async_error(error, span)
|
247
|
+
span.record_exception(error)
|
244
248
|
end
|
245
249
|
|
246
250
|
# Closes out an asynchronous span
|
@@ -268,7 +272,7 @@ module Instana
|
|
268
272
|
# The non-nil value of this instance variable
|
269
273
|
# indicates if we are currently tracing
|
270
274
|
# in this thread or not.
|
271
|
-
(
|
275
|
+
(current_span ? true : false) ||
|
272
276
|
(::Instana.config[:allow_exit_as_root] && ::Instana.config[:tracing][:enabled])
|
273
277
|
end
|
274
278
|
|
@@ -280,9 +284,10 @@ module Instana
|
|
280
284
|
# @return [Boolean]
|
281
285
|
#
|
282
286
|
def tracing_span?(name)
|
283
|
-
if
|
284
|
-
return
|
287
|
+
if current_span
|
288
|
+
return current_span.name == name
|
285
289
|
end
|
290
|
+
|
286
291
|
false
|
287
292
|
end
|
288
293
|
|
@@ -291,8 +296,9 @@ module Instana
|
|
291
296
|
# @return [SpanContext] or nil if not tracing
|
292
297
|
#
|
293
298
|
def context
|
294
|
-
return unless
|
295
|
-
|
299
|
+
return unless current_span
|
300
|
+
|
301
|
+
current_span.context
|
296
302
|
end
|
297
303
|
|
298
304
|
# Used in the test suite, this resets the tracer to non-tracing state.
|
@@ -300,5 +306,51 @@ module Instana
|
|
300
306
|
def clear!
|
301
307
|
self.current_span = nil
|
302
308
|
end
|
309
|
+
|
310
|
+
# Creates a span that is active during the execution of the provided block.
|
311
|
+
# The span is automatically closed when the block completes, whether it completes
|
312
|
+
# normally or with an exception.
|
313
|
+
#
|
314
|
+
# @param name [String, Symbol] the name of the span to create
|
315
|
+
# @param attributes [Hash, nil] optional attributes to set on the span
|
316
|
+
# @param links [Array<Link>, nil] optional links to associate with the span
|
317
|
+
# @param start_timestamp [Integer, nil] optional start time for the span in milliseconds
|
318
|
+
# @param kind [Symbol, nil] optional span kind (e.g., :internal, :client, :server)
|
319
|
+
#
|
320
|
+
# @return [Object] the return value of the block
|
321
|
+
#
|
322
|
+
# @note This method is a wrapper around the parent class implementation and
|
323
|
+
# will only create a span if the Instana agent is ready and tracing is enabled.
|
324
|
+
#
|
325
|
+
def in_span(name, attributes: nil, links: nil, start_timestamp: nil, kind: nil)
|
326
|
+
return if !::Instana.agent.ready? || !::Instana.config[:tracing][:enabled]
|
327
|
+
|
328
|
+
super
|
329
|
+
end
|
330
|
+
|
331
|
+
# Starts a new span with the given parameters.
|
332
|
+
#
|
333
|
+
# @param name [String, Symbol] the name of the span to create (defaults to 'empty' if nil)
|
334
|
+
# @param with_parent [Context, nil] the parent context for the span (defaults to current context if nil)
|
335
|
+
# @param attributes [Hash, nil] optional attributes to set on the span
|
336
|
+
# @param links [Array<Link>, nil] optional links to associate with the span
|
337
|
+
# @param start_timestamp [Integer, nil] optional start time for the span in milliseconds
|
338
|
+
# @param kind [Symbol, nil] optional span kind (defaults to :internal if nil)
|
339
|
+
#
|
340
|
+
# @return [Span] the newly created span
|
341
|
+
#
|
342
|
+
# @note This method will only create a span if the Instana agent is ready and tracing is enabled.
|
343
|
+
# Default values are set for nil parameters: name='empty', kind=:internal,
|
344
|
+
# with_parent=current context, start_timestamp=current time.
|
345
|
+
#
|
346
|
+
def start_span(name, with_parent: nil, attributes: nil, links: nil, start_timestamp: ::Instana::Util.now_in_ms, kind: nil) # rubocop:disable Metrics/ParameterLists
|
347
|
+
return if !::Instana.agent.ready? || !::Instana.config[:tracing][:enabled]
|
348
|
+
|
349
|
+
with_parent ||= OpenTelemetry::Context.current
|
350
|
+
name ||= 'empty'
|
351
|
+
kind ||= :internal
|
352
|
+
start_timestamp ||= ::Instana::Util.now_in_ms
|
353
|
+
self.current_span = @tracer_provider.internal_start_span(name, kind, attributes, links, start_timestamp, with_parent, @instrumentation_scope)
|
354
|
+
end
|
303
355
|
end
|
304
356
|
end
|
@@ -0,0 +1,198 @@
|
|
1
|
+
# (c) Copyright IBM Corp. 2025
|
2
|
+
|
3
|
+
require 'opentelemetry/trace/tracer_provider'
|
4
|
+
require 'instana/samplers/samplers'
|
5
|
+
require 'instana/trace/span_limits'
|
6
|
+
require 'instana/trace/export'
|
7
|
+
|
8
|
+
module Instana
|
9
|
+
module Trace
|
10
|
+
# {TracerProvider} is the Instana implementation of {OpenTelemetry::Trace::TracerProvider}.
|
11
|
+
class TracerProvider < OpenTelemetry::Trace::TracerProvider
|
12
|
+
Key = Struct.new(:name, :version)
|
13
|
+
private_constant(:Key)
|
14
|
+
|
15
|
+
attr_accessor :span_limits, :id_generator, :sampler
|
16
|
+
attr_reader :resource
|
17
|
+
|
18
|
+
# Returns a new {TracerProvider} instance.
|
19
|
+
#
|
20
|
+
# @param [optional Sampler] sampler The sampling policy for new spans
|
21
|
+
# @param [optional Resource] resource The resource to associate with spans
|
22
|
+
# created by Tracers created by this TracerProvider
|
23
|
+
# @param [optional IDGenerator] id_generator The trace and span ID generation
|
24
|
+
# policy
|
25
|
+
# @param [optional SpanLimits] span_limits The limits to apply to attribute,
|
26
|
+
# event and link counts for Spans created by Tracers created by this
|
27
|
+
# TracerProvider
|
28
|
+
#
|
29
|
+
# @return [TracerProvider]
|
30
|
+
# def initialize
|
31
|
+
# super()
|
32
|
+
# end
|
33
|
+
|
34
|
+
def initialize(sampler: sampler_from_environment(Samplers.parent_based(root: Samplers::ALWAYS_ON)),
|
35
|
+
resource: nil, # Instana::Resources::Resource.create
|
36
|
+
id_generator: ::Instana::Trace,
|
37
|
+
span_limits: SpanLimits::DEFAULT)
|
38
|
+
super()
|
39
|
+
@mutex = Mutex.new
|
40
|
+
@registry = {}
|
41
|
+
@registry_mutex = Mutex.new
|
42
|
+
@span_processors = []
|
43
|
+
@span_limits = span_limits
|
44
|
+
@sampler = sampler
|
45
|
+
@id_generator = id_generator
|
46
|
+
@stopped = false
|
47
|
+
@resource = resource
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns a {Tracer} instance.
|
51
|
+
#
|
52
|
+
# @param [optional String] name Instrumentation package name
|
53
|
+
# @param [optional String] version Instrumentation package version
|
54
|
+
#
|
55
|
+
# @return [Tracer]
|
56
|
+
def tracer(name = nil, version = nil)
|
57
|
+
name ||= ''
|
58
|
+
version ||= ''
|
59
|
+
::Instana.logger.warn 'calling TracerProvider#tracer without providing a tracer name.' if name.empty?
|
60
|
+
@registry_mutex.synchronize { @registry[Key.new(name, version)] ||= Tracer.new(name, version, self) }
|
61
|
+
end
|
62
|
+
|
63
|
+
# Attempts to stop all the activity for this {TracerProvider}. Calls
|
64
|
+
# SpanProcessor#shutdown for all registered SpanProcessors.
|
65
|
+
#
|
66
|
+
# This operation may block until all the Spans are processed. Must be
|
67
|
+
# called before turning off the main application to ensure all data are
|
68
|
+
# processed and exported.
|
69
|
+
#
|
70
|
+
# After this is called all the newly created {Span}s will be no-op.
|
71
|
+
#
|
72
|
+
# @param [optional Numeric] timeout An optional timeout in seconds.
|
73
|
+
# @return [Integer] Export::SUCCESS if no error occurred, Export::FAILURE if
|
74
|
+
# a non-specific failure occurred, Export::TIMEOUT if a timeout occurred.
|
75
|
+
def shutdown(timeout: nil)
|
76
|
+
@mutex.synchronize do
|
77
|
+
if @stopped
|
78
|
+
::Instana.logger.warn('calling Tracer#shutdown multiple times.')
|
79
|
+
return Export::FAILURE
|
80
|
+
end
|
81
|
+
|
82
|
+
start_time = Instana::Util.timeout_timestamp
|
83
|
+
results = @span_processors.map do |processor|
|
84
|
+
remaining_timeout = Instana::Util.maybe_timeout(timeout, start_time)
|
85
|
+
break [Export::TIMEOUT] if remaining_timeout&.zero?
|
86
|
+
|
87
|
+
processor.shutdown(timeout: remaining_timeout)
|
88
|
+
end
|
89
|
+
@stopped = true
|
90
|
+
results.max || Export::SUCCESS
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Immediately export all spans that have not yet been exported for all the
|
95
|
+
# registered SpanProcessors.
|
96
|
+
#
|
97
|
+
# This method should only be called in cases where it is absolutely
|
98
|
+
# necessary, such as when using some FaaS providers that may suspend
|
99
|
+
# the process after an invocation, but before the `Processor` exports
|
100
|
+
# the completed spans.
|
101
|
+
#
|
102
|
+
# @param [optional Numeric] timeout An optional timeout in seconds.
|
103
|
+
# @return [Integer] Export::SUCCESS if no error occurred, Export::FAILURE if
|
104
|
+
# a non-specific failure occurred, Export::TIMEOUT if a timeout occurred.
|
105
|
+
def force_flush(timeout: nil)
|
106
|
+
@mutex.synchronize do
|
107
|
+
return Export::SUCCESS if @stopped
|
108
|
+
|
109
|
+
start_time = Instana::Util.timeout_timestamp
|
110
|
+
results = @span_processors.map do |processor|
|
111
|
+
remaining_timeout = Instana::Util.maybe_timeout(timeout, start_time)
|
112
|
+
return Export::TIMEOUT if remaining_timeout&.zero?
|
113
|
+
|
114
|
+
processor.force_flush(timeout: remaining_timeout)
|
115
|
+
end
|
116
|
+
results.max || Export::SUCCESS
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Adds a new SpanProcessor to this {Tracer}.
|
121
|
+
#
|
122
|
+
# @param span_processor the new SpanProcessor to be added.
|
123
|
+
def add_span_processor(span_processor)
|
124
|
+
@mutex.synchronize do
|
125
|
+
if @stopped
|
126
|
+
::Instana.logger.warn('calling Tracer#add_span_processor after shutdown.')
|
127
|
+
return
|
128
|
+
end
|
129
|
+
@span_processors = @span_processors.dup.push(span_processor)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# This method serves as the primary entry point for span creation. It initializes
|
134
|
+
# an Instana span, handles context, and manages sampling before returning the created span.
|
135
|
+
def internal_start_span(name, kind, attributes, links, start_timestamp, parent_context, instrumentation_scope) # rubocop:disable Metrics/ParameterLists
|
136
|
+
parent_span = OpenTelemetry::Trace.current_span(parent_context)
|
137
|
+
parent_span_context = parent_span.context if parent_span
|
138
|
+
if parent_span_context&.valid?
|
139
|
+
parent_span_id = parent_span_context.span_id
|
140
|
+
trace_id = parent_span_context.trace_id
|
141
|
+
span_id = @id_generator.generate_span_id
|
142
|
+
end
|
143
|
+
trace_id ||= @id_generator.generate_trace_id
|
144
|
+
|
145
|
+
if OpenTelemetry::Common::Utilities.untraced?(parent_context)
|
146
|
+
span_id = parent_span_id || @id_generator.generate_span_id
|
147
|
+
return OpenTelemetry::Trace.non_recording_span(OpenTelemetry::Trace::SpanContext.new(trace_id: trace_id, span_id: span_id))
|
148
|
+
end
|
149
|
+
|
150
|
+
result = @sampler.should_sample?(trace_id: trace_id, parent_context: parent_context, links: links, name: name, kind: kind, attributes: attributes)
|
151
|
+
span_id ||= @id_generator.generate_span_id
|
152
|
+
if !@stopped && result.recording? && !@stopped
|
153
|
+
trace_flags = result.sampled? ? OpenTelemetry::Trace::TraceFlags::SAMPLED : OpenTelemetry::Trace::TraceFlags::DEFAULT
|
154
|
+
context = Instana::SpanContext.new(trace_id: trace_id, span_id: span_id, trace_flags: trace_flags, tracestate: result.tracestate)
|
155
|
+
attributes = attributes&.merge(result.attributes) || result.attributes.dup
|
156
|
+
Instana::Span.new(
|
157
|
+
name,
|
158
|
+
parent_span_context,
|
159
|
+
context,
|
160
|
+
parent_span,
|
161
|
+
kind,
|
162
|
+
parent_span_id,
|
163
|
+
@span_limits,
|
164
|
+
@span_processors,
|
165
|
+
attributes,
|
166
|
+
links,
|
167
|
+
start_timestamp,
|
168
|
+
@resource,
|
169
|
+
instrumentation_scope
|
170
|
+
)
|
171
|
+
else
|
172
|
+
Instana::Trace.non_recording_span(Instana::Trace::SpanContext.new(trace_id: trace_id, span_id: span_id, tracestate: result.tracestate)) # Todo add tracestate so that the trcing doesnot happen for this span # rubocop:disable Layout/LineLength
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
private
|
177
|
+
|
178
|
+
def sampler_from_environment(default_sampler)
|
179
|
+
case ENV['OTEL_TRACES_SAMPLER']
|
180
|
+
when 'always_on' then Samplers::ALWAYS_ON
|
181
|
+
when 'always_off' then Samplers::ALWAYS_OFF
|
182
|
+
when 'traceidratio' then Samplers.trace_id_ratio_based(Float(ENV.fetch('OTEL_TRACES_SAMPLER_ARG', 1.0)))
|
183
|
+
when 'parentbased_always_on' then Samplers.parent_based(root: Samplers::ALWAYS_ON)
|
184
|
+
when 'parentbased_always_off' then Samplers.parent_based(root: Samplers::ALWAYS_OFF)
|
185
|
+
when 'parentbased_traceidratio' then Samplers.parent_based(root: Samplers.trace_id_ratio_based(Float(ENV.fetch('OTEL_TRACES_SAMPLER_ARG', 1.0))))
|
186
|
+
else default_sampler
|
187
|
+
end
|
188
|
+
rescue StandardError => e
|
189
|
+
OpenTelemetry.handle_error(exception: e, message: "installing default sampler #{default_sampler.description}")
|
190
|
+
default_sampler
|
191
|
+
end
|
192
|
+
|
193
|
+
def timeout_timestamp
|
194
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# (c) Copyright IBM Corp. 2025
|
2
|
+
require 'opentelemetry/context'
|
3
|
+
module Instana
|
4
|
+
# The Trace API allows recording a set of events, triggered as a result of a
|
5
|
+
# single logical operation, consolidated across various components of an
|
6
|
+
# application.
|
7
|
+
module Trace
|
8
|
+
include OpenTelemetry::Trace
|
9
|
+
|
10
|
+
module_function
|
11
|
+
|
12
|
+
ID_RANGE = -2**63..2**63 - 1
|
13
|
+
|
14
|
+
# Generates a valid trace identifier
|
15
|
+
|
16
|
+
def generate_trace_id(size = 1)
|
17
|
+
Array.new(size) { rand(ID_RANGE) }
|
18
|
+
.pack('q>*')
|
19
|
+
.unpack1('H*')
|
20
|
+
end
|
21
|
+
|
22
|
+
# Generates a valid span identifier
|
23
|
+
#
|
24
|
+
def generate_span_id(size = 1)
|
25
|
+
Array.new(size) { rand(ID_RANGE) }
|
26
|
+
.pack('q>*')
|
27
|
+
.unpack1('H*')
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns the current span from the current or provided context
|
31
|
+
#
|
32
|
+
# @param [optional Context] context The context to lookup the current
|
33
|
+
# {Span} from. Defaults to Context.current
|
34
|
+
def current_span(context = nil)
|
35
|
+
context ||= OpenTelemetry::Context.current
|
36
|
+
context.value(CURRENT_SPAN_KEY) || nil
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns a context containing the span, derived from the optional parent
|
40
|
+
# context, or the current context if one was not provided.
|
41
|
+
#
|
42
|
+
# @param [optional Context] context The context to use as the parent for
|
43
|
+
# the returned context
|
44
|
+
def context_with_span(span, parent_context: OpenTelemetry::Context.current)
|
45
|
+
parent_context.set_value(CURRENT_SPAN_KEY, span)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Activates/deactivates the Span within the current Context, which makes the "current span"
|
49
|
+
# available implicitly.
|
50
|
+
#
|
51
|
+
# On exit, the Span that was active before calling this method will be reactivated.
|
52
|
+
#
|
53
|
+
# @param [Span] span the span to activate
|
54
|
+
# @yield [span, context] yields span and a context containing the span to the block.
|
55
|
+
def with_span(span)
|
56
|
+
OpenTelemetry::Context.with_value(CURRENT_SPAN_KEY, span) { |c, s| yield s, c }
|
57
|
+
end
|
58
|
+
|
59
|
+
# Wraps a SpanContext with an object implementing the Span interface. This is done in order
|
60
|
+
# to expose a SpanContext as a Span in operations such as in-process Span propagation.
|
61
|
+
#
|
62
|
+
# @param [SpanContext] span_context SpanContext to be wrapped
|
63
|
+
#
|
64
|
+
# @return [Span]
|
65
|
+
def non_recording_span(span_context)
|
66
|
+
Span.new(span_context: span_context)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
require 'instana/trace/span_context'
|
72
|
+
require 'instana/trace/span_kind'
|
73
|
+
require 'instana/trace/span'
|
74
|
+
require 'instana/trace/tracer'
|
data/lib/instana/util.rb
CHANGED
@@ -170,6 +170,17 @@ module Instana
|
|
170
170
|
return '' unless given.match(/\A[a-z\d]{16,32}\z/i)
|
171
171
|
given
|
172
172
|
end
|
173
|
+
|
174
|
+
def timeout_timestamp
|
175
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
176
|
+
end
|
177
|
+
|
178
|
+
def maybe_timeout(timeout, start_time)
|
179
|
+
return nil if timeout.nil?
|
180
|
+
|
181
|
+
timeout -= (timeout_timestamp - start_time)
|
182
|
+
timeout.positive? ? timeout : 0
|
183
|
+
end
|
173
184
|
end
|
174
185
|
end
|
175
186
|
end
|
data/lib/instana/version.rb
CHANGED
@@ -7,6 +7,7 @@ require 'rack/test'
|
|
7
7
|
class SinatraTest < Minitest::Test
|
8
8
|
include Rack::Test::Methods
|
9
9
|
APP = Rack::Builder.parse_file('test/support/apps/sinatra/config.ru')
|
10
|
+
|
10
11
|
sinatra_version = Gem::Specification.find_by_name('sinatra').version
|
11
12
|
if sinatra_version < Gem::Version.new('4.0.0')
|
12
13
|
APP = APP.first
|
@@ -18,8 +19,8 @@ class SinatraTest < Minitest::Test
|
|
18
19
|
|
19
20
|
def test_basic_get
|
20
21
|
clear_all!
|
21
|
-
|
22
22
|
r = get '/'
|
23
|
+
|
23
24
|
assert last_response.ok?
|
24
25
|
|
25
26
|
|
@@ -17,7 +17,7 @@ class AwsTest < Minitest::Test
|
|
17
17
|
)
|
18
18
|
|
19
19
|
assert_raises Aws::DynamoDB::Errors::ResourceNotFoundException do
|
20
|
-
Instana
|
20
|
+
Instana.tracer.in_span(:dynamo_test, attributes: {}) do
|
21
21
|
dynamo.get_item(
|
22
22
|
table_name: 'sample_table',
|
23
23
|
key: { s: 'sample_item' }
|
@@ -45,7 +45,7 @@ class AwsTest < Minitest::Test
|
|
45
45
|
)
|
46
46
|
|
47
47
|
assert_raises Aws::S3::Errors::NoSuchBucket do
|
48
|
-
Instana
|
48
|
+
Instana.tracer.in_span(:s3_test, attributes: {}) do
|
49
49
|
s3_client.get_object(
|
50
50
|
bucket: 'sample-bucket',
|
51
51
|
key: 'sample_key'
|
@@ -73,7 +73,7 @@ class AwsTest < Minitest::Test
|
|
73
73
|
)
|
74
74
|
|
75
75
|
assert_raises Aws::SNS::Errors::NotFound do
|
76
|
-
Instana
|
76
|
+
Instana.tracer.in_span(:sns_test, attributes: {}) do
|
77
77
|
sns.publish(
|
78
78
|
topic_arn: 'topic:arn',
|
79
79
|
target_arn: 'target:arn',
|
@@ -104,7 +104,7 @@ class AwsTest < Minitest::Test
|
|
104
104
|
endpoint: "http://localhost:9911"
|
105
105
|
)
|
106
106
|
|
107
|
-
Instana
|
107
|
+
Instana.tracer.in_span(:sns_test, attributes: {}) do
|
108
108
|
sns.list_subscriptions
|
109
109
|
end
|
110
110
|
|
@@ -127,7 +127,7 @@ class AwsTest < Minitest::Test
|
|
127
127
|
create_response = nil
|
128
128
|
get_url_response = nil
|
129
129
|
|
130
|
-
Instana
|
130
|
+
Instana.tracer.in_span(:sqs_test, attributes: {}) do
|
131
131
|
create_response = sqs.create_queue(queue_name: 'test')
|
132
132
|
get_url_response = sqs.get_queue_url(queue_name: 'test')
|
133
133
|
sqs.send_message(queue_url: create_response.queue_url, message_body: 'Sample')
|
@@ -177,7 +177,7 @@ class AwsTest < Minitest::Test
|
|
177
177
|
secret_access_key: "test"
|
178
178
|
)
|
179
179
|
|
180
|
-
Instana
|
180
|
+
Instana.tracer.in_span(:lambda_test, attributes: {}) do
|
181
181
|
lambda.invoke(
|
182
182
|
function_name: 'Test',
|
183
183
|
invocation_type: 'Event',
|
@@ -216,7 +216,7 @@ class AwsTest < Minitest::Test
|
|
216
216
|
)
|
217
217
|
|
218
218
|
assert_raises(RuntimeError) do
|
219
|
-
Instana
|
219
|
+
Instana.tracer.in_span(:lambda_test, attributes: {}) do
|
220
220
|
lambda.invoke(
|
221
221
|
function_name: 'Test',
|
222
222
|
invocation_type: 'Event',
|