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.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +101 -225
  3. data/.tekton/.currency/scripts/generate_report.py +194 -22
  4. data/.tekton/pipeline.yaml +65 -152
  5. data/.tekton/prepuller-restart-service-account.yaml +31 -0
  6. data/.tekton/ruby-tracer-prepuller-cronjob.yaml +20 -0
  7. data/.tekton/ruby-tracer-prepuller.yaml +27 -26
  8. data/.tekton/task.yaml +31 -27
  9. data/Gemfile +5 -0
  10. data/README.md +2 -1
  11. data/Rakefile +1 -1
  12. data/examples/otel.rb +98 -0
  13. data/examples/tracing.rb +1 -0
  14. data/gemfiles/rails_61.gemfile +1 -0
  15. data/gemfiles/rails_70.gemfile +1 -0
  16. data/instana.gemspec +5 -0
  17. data/lib/instana/base.rb +4 -2
  18. data/lib/instana/instrumentation/action_cable.rb +8 -4
  19. data/lib/instana/instrumentation/action_controller.rb +2 -4
  20. data/lib/instana/instrumentation/action_mailer.rb +1 -1
  21. data/lib/instana/instrumentation/action_view.rb +4 -4
  22. data/lib/instana/instrumentation/active_job.rb +26 -13
  23. data/lib/instana/instrumentation/active_record.rb +1 -1
  24. data/lib/instana/instrumentation/aws_sdk_dynamodb.rb +1 -1
  25. data/lib/instana/instrumentation/aws_sdk_lambda.rb +1 -1
  26. data/lib/instana/instrumentation/aws_sdk_s3.rb +1 -1
  27. data/lib/instana/instrumentation/aws_sdk_sns.rb +1 -1
  28. data/lib/instana/instrumentation/aws_sdk_sqs.rb +1 -1
  29. data/lib/instana/instrumentation/dalli.rb +1 -1
  30. data/lib/instana/instrumentation/excon.rb +2 -2
  31. data/lib/instana/instrumentation/graphql.rb +3 -3
  32. data/lib/instana/instrumentation/grpc.rb +14 -13
  33. data/lib/instana/instrumentation/mongo.rb +3 -3
  34. data/lib/instana/instrumentation/net-http.rb +5 -4
  35. data/lib/instana/instrumentation/rack.rb +36 -4
  36. data/lib/instana/instrumentation/redis.rb +1 -1
  37. data/lib/instana/instrumentation/resque.rb +10 -8
  38. data/lib/instana/instrumentation/rest-client.rb +4 -4
  39. data/lib/instana/instrumentation/sequel.rb +3 -3
  40. data/lib/instana/instrumentation/shoryuken.rb +4 -1
  41. data/lib/instana/instrumentation/sidekiq-client.rb +21 -19
  42. data/lib/instana/instrumentation/sidekiq-worker.rb +22 -21
  43. data/lib/instana/instrumented_logger.rb +1 -1
  44. data/lib/instana/samplers/result.rb +32 -0
  45. data/lib/instana/samplers/samplers.rb +76 -0
  46. data/lib/instana/serverless.rb +4 -2
  47. data/lib/instana/setup.rb +4 -5
  48. data/lib/instana/trace/export.rb +36 -0
  49. data/lib/instana/{tracing → trace}/processor.rb +19 -15
  50. data/lib/instana/trace/span.rb +532 -0
  51. data/lib/instana/{tracing → trace}/span_context.rb +17 -8
  52. data/lib/instana/trace/span_kind.rb +51 -0
  53. data/lib/instana/trace/span_limits.rb +63 -0
  54. data/lib/instana/{tracer.rb → trace/tracer.rb} +106 -54
  55. data/lib/instana/trace/tracer_provider.rb +198 -0
  56. data/lib/instana/trace.rb +74 -0
  57. data/lib/instana/util.rb +11 -0
  58. data/lib/instana/version.rb +1 -1
  59. data/test/frameworks/sinatra_test.rb +2 -1
  60. data/test/instrumentation/aws_test.rb +7 -7
  61. data/test/instrumentation/dalli_test.rb +8 -8
  62. data/test/instrumentation/excon_test.rb +3 -3
  63. data/test/instrumentation/graphql_test.rb +4 -4
  64. data/test/instrumentation/grpc_test.rb +8 -8
  65. data/test/instrumentation/mongo_test.rb +1 -1
  66. data/test/instrumentation/net_http_test.rb +6 -6
  67. data/test/instrumentation/rails_action_cable_test.rb +2 -2
  68. data/test/instrumentation/rails_action_mailer_test.rb +1 -1
  69. data/test/instrumentation/rails_active_job_test.rb +1 -1
  70. data/test/instrumentation/rails_active_record_database_missing_test.rb +2 -3
  71. data/test/instrumentation/rails_active_record_test.rb +8 -7
  72. data/test/instrumentation/redis_test.rb +7 -7
  73. data/test/instrumentation/resque_test.rb +5 -5
  74. data/test/instrumentation/rest_client_test.rb +1 -2
  75. data/test/instrumentation/sequel_test.rb +12 -6
  76. data/test/instrumentation/sidekiq-client_test.rb +2 -2
  77. data/test/instrumentation/sidekiq-worker_test.rb +2 -2
  78. data/test/{tracing → trace}/custom_test.rb +32 -25
  79. data/test/{tracing → trace}/id_management_test.rb +0 -2
  80. data/test/{tracing → trace}/instrumented_logger_test.rb +1 -1
  81. data/test/{tracing → trace}/processor_test.rb +6 -6
  82. data/test/{tracing → trace}/span_context_test.rb +3 -3
  83. data/test/{tracing → trace}/span_test.rb +7 -7
  84. data/test/{tracing → trace}/tracer_async_test.rb +43 -30
  85. data/test/trace/tracer_provider_test.rb +148 -0
  86. data/test/{tracing → trace}/tracer_test.rb +48 -37
  87. metadata +105 -32
  88. data/examples/opentracing.rb +0 -35
  89. data/lib/instana/open_tracing/carrier.rb +0 -7
  90. data/lib/instana/open_tracing/instana_tracer.rb +0 -99
  91. data/lib/instana/tracing/span.rb +0 -431
  92. data/lib/opentracing.rb +0 -32
  93. data/test/tracing/opentracing_test.rb +0 -382
@@ -1,14 +1,14 @@
1
- # (c) Copyright IBM Corp. 2021
2
- # (c) Copyright Instana Inc. 2016
1
+ # (c) Copyright IBM Corp. 2025
3
2
 
4
- require "instana/tracing/span"
5
- require "instana/tracing/span_context"
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: Instana.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=(v)
33
- @current_span.value = v
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, &block)
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 = {}, &block)
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
- if !incoming_context.empty?
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
- self.current_span = Span.new(name, parent_ctx: parent_context)
121
- else
122
- self.current_span = Span.new(name)
123
- end
121
+ self.current_span = if parent_context
122
+ Span.new(name, parent_context)
123
+ else
124
+ Span.new(name)
125
+ end
124
126
 
125
- self.current_span.set_tags(kvs) unless kvs.empty?
126
- self.current_span
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, start_time = ::Instana::Util.now_in_ms, child_of = 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? && !self.current_span.nil?
139
- Span.new(name, parent_ctx: self.current_span, start_time: start_time)
140
+ new_span = if child_of.nil? && !current_span.nil?
141
+ Span.new(name, current_span)
140
142
  else
141
- Span.new(name, parent_ctx: child_of, start_time: start_time)
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 self.current_span
153
- self.current_span.set_tags(kvs)
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(e)
161
- return unless self.current_span
162
- self.current_span.add_error(e)
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 self.current_span
178
+ return unless current_span
175
179
 
176
- if self.current_span.name != name
177
- @logger.warn "Span mismatch: Attempt to end #{name} span but #{self.current_span.name} is active."
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
- self.current_span.set_tags(kvs)
181
- self.current_span.close
184
+ current_span.set_tags(kvs)
185
+ current_span.close
182
186
 
183
- self.current_span = self.current_span.parent || nil
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 self.current_span
200
+ return unless current_span
197
201
 
198
- if self.current_span.name != name
199
- @logger.warn "Span mismatch: Attempt to end #{name} span but #{self.current_span.name} is active."
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
- self.current_span.set_tags(kvs)
203
- self.current_span.close(end_time)
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, parent_ctx: self.current_span)
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(e, span)
243
- span.add_error(e)
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
- (self.current_span ? true : false) ||
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 self.current_span
284
- return self.current_span.name == name
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 self.current_span
295
- self.current_span.context
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
@@ -2,6 +2,6 @@
2
2
  # (c) Copyright Instana Inc. 2016
3
3
 
4
4
  module Instana
5
- VERSION = "1.217.0"
5
+ VERSION = "2.0.0"
6
6
  VERSION_FULL = "instana-#{VERSION}"
7
7
  end
@@ -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::Tracer.start_or_continue_trace(:dynamo_test, {}) do
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::Tracer.start_or_continue_trace(:s3_test, {}) do
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::Tracer.start_or_continue_trace(:sns_test, {}) do
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::Tracer.start_or_continue_trace(:sns_test, {}) do
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::Tracer.start_or_continue_trace(:sqs_test, {}) do
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::Tracer.start_or_continue_trace(:lambda_test, {}) do
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::Tracer.start_or_continue_trace(:lambda_test, {}) do
219
+ Instana.tracer.in_span(:lambda_test, attributes: {}) do
220
220
  lambda.invoke(
221
221
  function_name: 'Test',
222
222
  invocation_type: 'Event',