elastic-apm 2.1.2 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of elastic-apm might be problematic. Click here for more details.

@@ -49,12 +49,14 @@ module ElasticAPM
49
49
  def initialize(config)
50
50
  @config = config
51
51
 
52
- @transport = Transport::Base.new(config)
53
- @instrumenter = Instrumenter.new(config) { |event| enqueue event }
54
-
55
52
  @stacktrace_builder = StacktraceBuilder.new(config)
56
- @context_builder = ContextBuilder.new(self)
53
+ @context_builder = ContextBuilder.new(config)
57
54
  @error_builder = ErrorBuilder.new(self)
55
+
56
+ @transport = Transport::Base.new(config)
57
+ @instrumenter = Instrumenter.new(
58
+ config, stacktrace_builder: stacktrace_builder
59
+ ) { |event| enqueue event }
58
60
  end
59
61
 
60
62
  attr_reader :config, :transport, :instrumenter,
@@ -106,13 +108,13 @@ module ElasticAPM
106
108
  name = nil,
107
109
  type = nil,
108
110
  context: nil,
109
- traceparent: nil
111
+ trace_context: nil
110
112
  )
111
113
  instrumenter.start_transaction(
112
114
  name,
113
115
  type,
114
116
  context: context,
115
- traceparent: traceparent
117
+ trace_context: trace_context
116
118
  )
117
119
  end
118
120
 
@@ -120,12 +122,19 @@ module ElasticAPM
120
122
  instrumenter.end_transaction(result)
121
123
  end
122
124
 
123
- def start_span(name = nil, type = nil, backtrace: nil, context: nil)
125
+ def start_span(
126
+ name = nil,
127
+ type = nil,
128
+ backtrace: nil,
129
+ context: nil,
130
+ trace_context: nil
131
+ )
124
132
  instrumenter.start_span(
125
133
  name,
126
134
  type,
127
135
  backtrace: backtrace,
128
- context: context
136
+ context: context,
137
+ trace_context: trace_context
129
138
  )
130
139
  end
131
140
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'logger'
4
4
  require 'yaml'
5
+ require 'erb'
5
6
 
6
7
  require 'elastic_apm/util/prefixed_logger'
7
8
  require 'elastic_apm/config/duration'
@@ -21,6 +22,9 @@ module ElasticAPM
21
22
  api_buffer_size: 256,
22
23
  api_request_size: '750kb',
23
24
  api_request_time: '10s',
25
+ capture_body: true,
26
+ capture_headers: true,
27
+ capture_env: true,
24
28
  current_user_email_method: :email,
25
29
  current_user_id_method: :id,
26
30
  current_user_username_method: :username,
@@ -57,6 +61,9 @@ module ElasticAPM
57
61
  'ELASTIC_APM_API_BUFFER_SIZE' => [:int, 'api_buffer_size'],
58
62
  'ELASTIC_APM_API_REQUEST_SIZE' => [:int, 'api_request_size'],
59
63
  'ELASTIC_APM_API_REQUEST_TIME' => 'api_request_time',
64
+ 'ELASTIC_APM_CAPTURE_BODY' => [:bool, 'capture_body'],
65
+ 'ELASTIC_APM_CAPTURE_HEADERS' => [:bool, 'capture_headers'],
66
+ 'ELASTIC_APM_CAPTURE_ENV' => [:bool, 'capture_env'],
60
67
  'ELASTIC_APM_CUSTOM_KEY_FILTERS' => [:list, 'custom_key_filters'],
61
68
  'ELASTIC_APM_DEFAULT_TAGS' => [:dict, 'default_tags'],
62
69
  'ELASTIC_APM_DISABLED_SPIES' => [:list, 'disabled_spies'],
@@ -118,6 +125,9 @@ module ElasticAPM
118
125
  attr_accessor :api_buffer_size
119
126
  attr_accessor :api_request_size
120
127
  attr_accessor :api_request_time
128
+ attr_accessor :capture_body
129
+ attr_accessor :capture_headers
130
+ attr_accessor :capture_env
121
131
  attr_accessor :current_user_email_method
122
132
  attr_accessor :current_user_id_method
123
133
  attr_accessor :current_user_method
@@ -155,6 +165,9 @@ module ElasticAPM
155
165
  attr_accessor :view_paths
156
166
  attr_accessor :root_path
157
167
 
168
+ alias :capture_body? :capture_body
169
+ alias :capture_headers? :capture_headers
170
+ alias :capture_env? :capture_env
158
171
  alias :disable_send? :disable_send
159
172
  alias :http_compression? :http_compression
160
173
  alias :instrument? :instrument
@@ -229,6 +242,10 @@ module ElasticAPM
229
242
  @span_frames_min_duration_us = duration * 1_000_000
230
243
  end
231
244
 
245
+ def span_frames_min_duration?
246
+ span_frames_min_duration != 0
247
+ end
248
+
232
249
  DEPRECATED_OPTIONS = %i[
233
250
  compression_level=
234
251
  compression_minimum_size=
@@ -307,7 +324,7 @@ module ElasticAPM
307
324
 
308
325
  def set_from_config_file
309
326
  return unless File.exist?(config_file)
310
- assign(YAML.load_file(config_file) || {})
327
+ assign(YAML.safe_load(ERB.new(File.read(config_file)).result) || {})
311
328
  rescue ConfigError => e
312
329
  alert_logger.warn format(
313
330
  'Failed to configure from config file: %s',
@@ -12,9 +12,10 @@ module ElasticAPM
12
12
  attr_accessor :request, :response, :user
13
13
  attr_reader :custom, :tags
14
14
 
15
- def initialize(custom: {}, tags: {})
15
+ def initialize(custom: {}, tags: {}, user: nil)
16
16
  @custom = custom
17
17
  @tags = tags
18
+ @user = user || User.new
18
19
  end
19
20
  end
20
21
  end
@@ -4,20 +4,34 @@ module ElasticAPM
4
4
  class Context
5
5
  # @api private
6
6
  class User
7
- def initialize(config, record)
7
+ def initialize(id: nil, email: nil, username: nil)
8
+ @id = id
9
+ @email = email
10
+ @username = username
11
+ end
12
+
13
+ def self.infer(config, record)
8
14
  return unless record
9
15
 
10
- @id = safe_get(record, config.current_user_id_method)&.to_s
11
- @email = safe_get(record, config.current_user_email_method)
12
- @username = safe_get(record, config.current_user_username_method)
16
+ new(
17
+ id: safe_get(record, config.current_user_id_method)&.to_s,
18
+ email: safe_get(record, config.current_user_email_method),
19
+ username: safe_get(record, config.current_user_username_method)
20
+ )
13
21
  end
14
22
 
15
23
  attr_accessor :id, :email, :username
16
24
 
17
- private
25
+ def empty?
26
+ !id && !email && !username
27
+ end
28
+
29
+ class << self
30
+ private
18
31
 
19
- def safe_get(record, method_name)
20
- record.respond_to?(method_name) ? record.send(method_name) : nil
32
+ def safe_get(record, method_name)
33
+ record.respond_to?(method_name) ? record.send(method_name) : nil
34
+ end
21
35
  end
22
36
  end
23
37
  end
@@ -1,10 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ElasticAPM
4
- # TODO: Move to txn.add_request ?
5
4
  # @api private
6
5
  class ContextBuilder
7
- def initialize(_agent); end
6
+ def initialize(config)
7
+ @config = config
8
+ end
9
+
10
+ attr_reader :config
8
11
 
9
12
  def build(rack_env)
10
13
  context = Context.new
@@ -14,7 +17,7 @@ module ElasticAPM
14
17
 
15
18
  private
16
19
 
17
- # rubocop:disable Metrics/AbcSize
20
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
18
21
  def apply_to_request(context, rack_env)
19
22
  req = rails_req?(rack_env) ? rack_env : Rack::Request.new(rack_env)
20
23
 
@@ -25,12 +28,16 @@ module ElasticAPM
25
28
  request.http_version = build_http_version rack_env
26
29
  request.method = req.request_method
27
30
  request.url = Context::Request::Url.new(req)
28
- request.headers, request.env = get_headers_and_env(rack_env)
29
- request.body = get_body(req)
31
+
32
+ request.body = get_body(req) if config.capture_body?
33
+
34
+ headers, env = get_headers_and_env(rack_env)
35
+ request.headers = headers if config.capture_headers?
36
+ request.env = env if config.capture_env?
30
37
 
31
38
  context
32
39
  end
33
- # rubocop:enable Metrics/AbcSize
40
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
34
41
 
35
42
  def get_body(req)
36
43
  return req.POST if req.form_data?
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'elastic_apm/trace_context'
3
4
  require 'elastic_apm/span'
4
5
  require 'elastic_apm/transaction'
5
6
 
@@ -7,16 +8,16 @@ module ElasticAPM
7
8
  # rubocop:disable Metrics/ClassLength
8
9
  # @api private
9
10
  class Instrumenter
10
- include Logging
11
+ TRANSACTION_KEY = :__elastic_instrumenter_transaction_key
12
+ SPAN_KEY = :__elastic_instrumenter_spans_key
11
13
 
12
- TRANSACTION_KEY = :__elastic_transaction_key
13
- SPAN_KEY = :__elastic_span_key
14
+ include Logging
14
15
 
15
16
  # @api private
16
17
  class Current
17
18
  def initialize
18
19
  self.transaction = nil
19
- self.span = nil
20
+ self.spans = []
20
21
  end
21
22
 
22
23
  def transaction
@@ -27,23 +28,25 @@ module ElasticAPM
27
28
  Thread.current[TRANSACTION_KEY] = transaction
28
29
  end
29
30
 
30
- def span
31
- Thread.current[SPAN_KEY]
31
+ def spans
32
+ Thread.current[SPAN_KEY] ||= []
32
33
  end
33
34
 
34
- def span=(span)
35
- Thread.current[SPAN_KEY] = span
35
+ def spans=(spans)
36
+ Thread.current[SPAN_KEY] ||= []
37
+ Thread.current[SPAN_KEY] = spans
36
38
  end
37
39
  end
38
40
 
39
- def initialize(config, &enqueue)
41
+ def initialize(config, stacktrace_builder:, &enqueue)
40
42
  @config = config
43
+ @stacktrace_builder = stacktrace_builder
41
44
  @enqueue = enqueue
42
45
 
43
46
  @current = Current.new
44
47
  end
45
48
 
46
- attr_reader :config, :enqueue
49
+ attr_reader :config, :stacktrace_builder, :enqueue
47
50
 
48
51
  def start
49
52
  debug 'Starting instrumenter'
@@ -53,7 +56,7 @@ module ElasticAPM
53
56
  debug 'Stopping instrumenter'
54
57
 
55
58
  self.current_transaction = nil
56
- self.current_span = nil
59
+ current_spans.pop until current_spans.empty?
57
60
 
58
61
  @subscriber.unregister! if @subscriber
59
62
  end
@@ -78,7 +81,7 @@ module ElasticAPM
78
81
  name = nil,
79
82
  type = nil,
80
83
  context: nil,
81
- traceparent: nil
84
+ trace_context: nil
82
85
  )
83
86
  return nil unless config.instrument?
84
87
 
@@ -87,14 +90,14 @@ module ElasticAPM
87
90
  "Transactions may not be nested.\nAlready inside #{transaction}"
88
91
  end
89
92
 
90
- sampled = traceparent ? traceparent.recorded? : random_sample?
93
+ sampled = trace_context ? trace_context.recorded? : random_sample?
91
94
 
92
95
  transaction =
93
96
  Transaction.new(
94
97
  name,
95
98
  type,
96
99
  context: context,
97
- traceparent: traceparent,
100
+ trace_context: trace_context,
98
101
  sampled: sampled
99
102
  )
100
103
 
@@ -118,16 +121,23 @@ module ElasticAPM
118
121
 
119
122
  # spans
120
123
 
121
- def current_span
122
- @current.span
124
+ def current_spans
125
+ @current.spans
123
126
  end
124
127
 
125
- def current_span=(span)
126
- @current.span = span
128
+ def current_span
129
+ current_spans.last
127
130
  end
128
131
 
129
132
  # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
130
- def start_span(name, type = nil, backtrace: nil, context: nil)
133
+ # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
134
+ def start_span(
135
+ name,
136
+ type = nil,
137
+ backtrace: nil,
138
+ context: nil,
139
+ trace_context: nil
140
+ )
131
141
  return unless (transaction = current_transaction)
132
142
  return unless transaction.sampled?
133
143
 
@@ -138,32 +148,34 @@ module ElasticAPM
138
148
  return
139
149
  end
140
150
 
151
+ parent = current_span || transaction
152
+
141
153
  span = Span.new(
142
154
  name,
143
155
  type,
144
- transaction: transaction,
145
- parent: current_span || transaction,
146
- context: context
156
+ transaction_id: transaction.id,
157
+ parent_id: parent.id,
158
+ context: context,
159
+ stacktrace_builder: stacktrace_builder,
160
+ trace_context: trace_context || parent.trace_context.child
147
161
  )
148
162
 
149
- if backtrace && span_frames_min_duration?
163
+ if backtrace && config.span_frames_min_duration?
150
164
  span.original_backtrace = backtrace
151
165
  end
152
166
 
153
- self.current_span = span
167
+ current_spans.push span
154
168
 
155
169
  span.start
156
170
  end
171
+ # rubocop:enable Metrics/AbcSize, Metrics/PerceivedComplexity
157
172
  # rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity
158
173
 
159
174
  def end_span
160
- return unless (span = current_span)
175
+ return unless (span = current_spans.pop)
161
176
 
162
177
  span.done
163
178
 
164
- self.current_span =
165
- span.parent&.is_a?(Span) && span.parent || nil
166
-
167
179
  enqueue.call span
168
180
 
169
181
  span
@@ -185,7 +197,7 @@ module ElasticAPM
185
197
 
186
198
  def set_user(user)
187
199
  return unless current_transaction
188
- current_transaction.context.user = Context::User.new(config, user)
200
+ current_transaction.context.user = Context::User.infer(config, user)
189
201
  end
190
202
 
191
203
  def inspect
@@ -199,10 +211,6 @@ module ElasticAPM
199
211
  def random_sample?
200
212
  rand <= config.transaction_sample_rate
201
213
  end
202
-
203
- def span_frames_min_duration?
204
- config.span_frames_min_duration != 0
205
- end
206
214
  end
207
215
  # rubocop:enable Metrics/ClassLength
208
216
  end
@@ -1,8 +1,6 @@
1
1
  #
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'elastic_apm/traceparent'
5
-
6
4
  module ElasticAPM
7
5
  # @api private
8
6
  class Middleware
@@ -53,13 +51,13 @@ module ElasticAPM
53
51
  def start_transaction(env)
54
52
  ElasticAPM.start_transaction 'Rack', 'request',
55
53
  context: ElasticAPM.build_context(env),
56
- traceparent: traceparent(env)
54
+ trace_context: trace_context(env)
57
55
  end
58
56
 
59
- def traceparent(env)
57
+ def trace_context(env)
60
58
  return unless (header = env['HTTP_ELASTIC_APM_TRACEPARENT'])
61
- Traceparent.parse(header)
62
- rescue Traceparent::InvalidTraceparentHeader
59
+ TraceContext.parse(header)
60
+ rescue TraceContext::InvalidTraceparentHeader
63
61
  warn "Couldn't parse invalid traceparent header: #{header.inspect}"
64
62
  nil
65
63
  end
@@ -0,0 +1,346 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'elastic_apm'
4
+ require 'opentracing'
5
+
6
+ module ElasticAPM
7
+ module OpenTracing
8
+ # @api private
9
+ class Span
10
+ def initialize(elastic_span, span_context)
11
+ @elastic_span = elastic_span
12
+ @span_context = span_context
13
+ end
14
+
15
+ attr_reader :elastic_span
16
+
17
+ def operation_name=(name)
18
+ elastic_span.name = name
19
+ end
20
+
21
+ def context
22
+ @span_context
23
+ end
24
+
25
+ # rubocop:disable Metrics/MethodLength
26
+ def set_tag(key, val)
27
+ if elastic_span.is_a?(Transaction)
28
+ case key.to_s
29
+ when 'type'
30
+ elastic_span.type = val
31
+ when 'result'
32
+ elastic_span.result = val
33
+ when /user\.(\w+)/
34
+ set_user_value($1, val)
35
+ else
36
+ elastic_span.context.tags[key] = val
37
+ end
38
+ else
39
+ elastic_span.context.tags[key] = val
40
+ end
41
+ end
42
+ # rubocop:enable Metrics/MethodLength
43
+
44
+ def set_baggage_item(_key, _value)
45
+ ElasticAPM.agent.config.logger.warn(
46
+ 'Baggage is not supported by ElasticAPM'
47
+ )
48
+ end
49
+
50
+ def get_baggage_item(_key)
51
+ ElasticAPM.agent.config.logger.warn(
52
+ 'Baggage is not supported by ElasticAPM'
53
+ )
54
+
55
+ nil
56
+ end
57
+
58
+ # rubocop:disable Lint/UnusedMethodArgument
59
+ def log_kv(timestamp: nil, **fields)
60
+ if (exception = fields[:'error.object'])
61
+ ElasticAPM.report exception
62
+ elsif (message = fields[:message])
63
+ ElasticAPM.report_message message
64
+ end
65
+ end
66
+ # rubocop:enable Lint/UnusedMethodArgument
67
+
68
+ def finish(end_time: Time.now)
69
+ return unless (instrumenter = ElasticAPM.agent&.instrumenter)
70
+
71
+ elastic_span.done end_time: Util.micros(end_time)
72
+
73
+ case elastic_span
74
+ when ElasticAPM::Transaction
75
+ instrumenter.current_transaction = nil
76
+ when ElasticAPM::Span
77
+ instrumenter.current_spans.delete(elastic_span)
78
+ end
79
+
80
+ instrumenter.enqueue.call elastic_span
81
+ end
82
+
83
+ private
84
+
85
+ def set_user_value(key, value)
86
+ return unless elastic_span.is_a?(Transaction)
87
+
88
+ setter = :"#{key}="
89
+ return unless elastic_span.context.user.respond_to?(setter)
90
+ elastic_span.context.user.send(setter, value)
91
+ end
92
+ end
93
+
94
+ # @api private
95
+ class SpanContext
96
+ def initialize(id:, trace_id:, baggage: nil)
97
+ if baggage
98
+ ElasticAPM.agent.config.logger.warn(
99
+ 'Baggage is not supported by ElasticAPM'
100
+ )
101
+ end
102
+
103
+ @id = id
104
+ @trace_id = trace_id
105
+ @trace_context =
106
+ ElasticAPM::TraceContext.new(trace_id: trace_id, span_id: id)
107
+ end
108
+
109
+ attr_accessor :id, :trace_id, :trace_context
110
+
111
+ def self.from_trace_context(trace_context)
112
+ new(
113
+ trace_id: trace_context.trace_id,
114
+ id: trace_context.span_id
115
+ ).tap do |span_context|
116
+ span_context.trace_context = trace_context
117
+ end
118
+ end
119
+ end
120
+
121
+ # @api private
122
+ class Scope
123
+ def initialize(span, scope_stack, finish_on_close:)
124
+ @span = span
125
+ @scope_stack = scope_stack
126
+ @finish_on_close = finish_on_close
127
+ end
128
+
129
+ attr_reader :span
130
+
131
+ def elastic_span
132
+ span.elastic_span
133
+ end
134
+
135
+ def close
136
+ @span.finish if @finish_on_close
137
+ @scope_stack.pop
138
+ end
139
+ end
140
+
141
+ # @api private
142
+ class ScopeStack
143
+ KEY = :__elastic_apm_ot_scope_stack
144
+
145
+ def push(scope)
146
+ scopes << scope
147
+ end
148
+
149
+ def pop
150
+ scopes.pop
151
+ end
152
+
153
+ def last
154
+ scopes.last
155
+ end
156
+
157
+ private
158
+
159
+ def scopes
160
+ Thread.current[KEY] ||= []
161
+ end
162
+ end
163
+
164
+ # @api private
165
+ class ScopeManager
166
+ def initialize
167
+ @scope_stack = ScopeStack.new
168
+ end
169
+
170
+ def activate(span, finish_on_close: true)
171
+ return active if active && active.span == span
172
+
173
+ scope = Scope.new(span, @scope_stack, finish_on_close: finish_on_close)
174
+ @scope_stack.push scope
175
+ scope
176
+ end
177
+
178
+ def active
179
+ @scope_stack.last
180
+ end
181
+ end
182
+
183
+ # rubocop:disable Metrics/ClassLength
184
+ # A custom tracer to use the OpenTracing API with ElasticAPM
185
+ class Tracer
186
+ def initialize
187
+ @scope_manager = ScopeManager.new
188
+ end
189
+
190
+ attr_reader :scope_manager
191
+
192
+ def active_span
193
+ scope_manager.active&.span
194
+ end
195
+
196
+ # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists
197
+ def start_active_span(
198
+ operation_name,
199
+ child_of: nil,
200
+ references: nil,
201
+ start_time: Time.now,
202
+ tags: {},
203
+ ignore_active_scope: false,
204
+ finish_on_close: true,
205
+ **
206
+ )
207
+ span = start_span(
208
+ operation_name,
209
+ child_of: child_of,
210
+ references: references,
211
+ start_time: start_time,
212
+ tags: tags,
213
+ ignore_active_scope: ignore_active_scope
214
+ )
215
+ scope = scope_manager.activate(span, finish_on_close: finish_on_close)
216
+
217
+ if block_given?
218
+ begin
219
+ yield scope
220
+ ensure
221
+ scope.close
222
+ end
223
+ end
224
+
225
+ scope
226
+ end
227
+ # rubocop:enable Metrics/MethodLength, Metrics/ParameterLists
228
+
229
+ # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists
230
+ # rubocop:disable Metrics/AbcSize
231
+ def start_span(
232
+ operation_name,
233
+ child_of: nil,
234
+ references: nil,
235
+ start_time: Time.now,
236
+ tags: {},
237
+ ignore_active_scope: false,
238
+ **
239
+ )
240
+ span_context = prepare_span_context(
241
+ child_of: child_of,
242
+ references: references,
243
+ ignore_active_scope: ignore_active_scope
244
+ )
245
+
246
+ if span_context
247
+ trace_context =
248
+ span_context &&
249
+ span_context.respond_to?(:trace_context) &&
250
+ span_context.trace_context
251
+ end
252
+
253
+ elastic_span =
254
+ if ElasticAPM.current_transaction
255
+ ElasticAPM.start_span(
256
+ operation_name,
257
+ trace_context: trace_context
258
+ )
259
+ else
260
+ ElasticAPM.start_transaction(
261
+ operation_name,
262
+ trace_context: trace_context
263
+ )
264
+ end
265
+
266
+ # if no Elastic APM agent is running or transaction not sampled
267
+ unless elastic_span
268
+ return ::OpenTracing::Span::NOOP_INSTANCE
269
+ end
270
+
271
+ span_context ||=
272
+ SpanContext.from_trace_context(elastic_span.trace_context)
273
+
274
+ tags.each do |key, value|
275
+ elastic_span.context.tags[key] = value
276
+ end
277
+
278
+ elastic_span.start Util.micros(start_time)
279
+
280
+ Span.new(elastic_span, span_context)
281
+ end
282
+ # rubocop:enable Metrics/AbcSize
283
+ # rubocop:enable Metrics/MethodLength, Metrics/ParameterLists
284
+
285
+ def inject(span_context, format, carrier)
286
+ case format
287
+ when ::OpenTracing::FORMAT_RACK
288
+ carrier['elastic-apm-traceparent'] = span_context.to_header
289
+ else
290
+ warn 'Only injection via HTTP headers and Rack is available'
291
+ end
292
+ end
293
+
294
+ def extract(format, carrier)
295
+ case format
296
+ when ::OpenTracing::FORMAT_RACK
297
+ ElasticAPM::TraceContext
298
+ .parse(carrier['HTTP_ELASTIC_APM_TRACEPARENT'])
299
+ else
300
+ warn 'Only extraction from HTTP headers via Rack is available'
301
+ nil
302
+ end
303
+ rescue ElasticAPM::TraceContext::InvalidTraceparentHeader
304
+ nil
305
+ end
306
+
307
+ private
308
+
309
+ def prepare_span_context(
310
+ child_of:,
311
+ references:,
312
+ ignore_active_scope:
313
+ )
314
+ context_from_child_of(child_of) ||
315
+ context_from_references(references) ||
316
+ context_from_active_scope(ignore_active_scope)
317
+ end
318
+
319
+ def context_from_child_of(child_of)
320
+ return unless child_of
321
+ child_of.respond_to?(:context) ? child_of.context : child_of
322
+ end
323
+
324
+ def context_from_references(references)
325
+ return if !references || references.none?
326
+
327
+ child_of = references.find do |reference|
328
+ reference.type == ::OpenTracing::Reference::CHILD_OF
329
+ end
330
+
331
+ (child_of || references.first).context
332
+ end
333
+
334
+ def context_from_active_scope(ignore_active_scope)
335
+ if ignore_active_scope
336
+ ElasticAPM.agent&.config&.logger&.warn(
337
+ 'ignore_active_scope might lead to unexpeced results'
338
+ )
339
+ return
340
+ end
341
+ @scope_manager.active&.span&.context
342
+ end
343
+ end
344
+ # rubocop:enable Metrics/ClassLength
345
+ end
346
+ end