elastic-apm 2.1.2 → 2.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.

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