appoptics_apm 4.8.0 → 4.9.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.
data/lib/appoptics_apm.rb CHANGED
@@ -49,6 +49,7 @@ begin
49
49
  if AppOpticsAPM.loaded
50
50
  require 'appoptics_apm/instrumentation'
51
51
  require 'appoptics_apm/support/transaction_metrics'
52
+ require 'appoptics_apm/support/x_trace_options'
52
53
 
53
54
  # Frameworks
54
55
  require 'appoptics_apm/frameworks/rails'
@@ -16,13 +16,16 @@ module AppOpticsAPM
16
16
  @@instrumentation = [:action_controller, :action_controller_api, :action_view,
17
17
  :active_record, :bunnyclient, :bunnyconsumer, :cassandra, :curb,
18
18
  :dalli, :delayed_jobclient, :delayed_jobworker,
19
- :em_http_request, :excon, :faraday, :grpc_client, :grpc_server, :grape,
19
+ # :em_http_request,
20
+ :excon, :faraday, :grpc_client, :grpc_server, :grape,
20
21
  :httpclient, :nethttp, :memcached, :mongo, :moped, :padrino, :rack, :redis,
21
22
  :resqueclient, :resqueworker, :rest_client,
22
23
  :sequel, :sidekiqclient, :sidekiqworker, :sinatra, :typhoeus]
23
24
 
24
25
  # Subgrouping of instrumentation
25
- @@http_clients = [:curb, :excon, :em_http_request, :faraday, :httpclient, :nethttp, :rest_client, :typhoeus]
26
+ @@http_clients = [:curb, :excon,
27
+ # :em_http_request,
28
+ :faraday, :httpclient, :nethttp, :rest_client, :typhoeus]
26
29
 
27
30
  ##
28
31
  # load_config_file
@@ -240,11 +243,15 @@ module AppOpticsAPM
240
243
  end
241
244
 
242
245
  elsif key == :tracing_mode
243
- # CAN'T DO THIS ANYMORE, ALL TRACING COMMUNICATION TO OBOE
246
+ # CAN'T DO `set_tracing_mode` ANYMORE, ALL TRACING COMMUNICATION TO OBOE
244
247
  # IS NOW HANDLED BY TransactionSettings
245
248
  # AppOpticsAPM.set_tracing_mode(value.to_sym) if AppOpticsAPM.loaded
246
249
 
247
- # Make sure that the mode is stored as a symbol
250
+ # Make sure that the mode is stored as a symbol
251
+ @@config[key.to_sym] = value.to_sym
252
+
253
+ elsif key == :trigger_tracing_mode
254
+ # Make sure that the mode is stored as a symbol
248
255
  @@config[key.to_sym] = value.to_sym
249
256
  end
250
257
  end
@@ -84,18 +84,18 @@ module AppOpticsAPM
84
84
  end
85
85
 
86
86
  # ActionController::Base
87
- if defined?(ActionController::Base) && AppOpticsAPM::Config[:action_controller][:enabled]
87
+ if defined?(ActionController::Base) && AppOpticsAPM::Config[:action_controller][:enabled] && Rails::VERSION::MAJOR <= 6
88
88
  AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting actioncontroller' if AppOpticsAPM::Config[:verbose]
89
89
  require "appoptics_apm/frameworks/rails/inst/action_controller#{Rails::VERSION::MAJOR}"
90
90
  if Rails::VERSION::MAJOR >= 5
91
91
  ActionController::Base.send(:prepend, ::AppOpticsAPM::Inst::ActionController)
92
- else
92
+ elsif Rails::VERSION::MAJOR < 5
93
93
  AppOpticsAPM::Util.send_include(::ActionController::Base, AppOpticsAPM::Inst::ActionController)
94
94
  end
95
95
  end
96
96
 
97
- # ActionController::API - Rails 5+ or via the rails-api gem
98
- if defined?(ActionController::API) && AppOpticsAPM::Config[:action_controller_api][:enabled]
97
+ # ActionController::API - Rails 5 or via the rails-api gem
98
+ if defined?(ActionController::API) && AppOpticsAPM::Config[:action_controller_api][:enabled] && Rails::VERSION::MAJOR <= 6
99
99
  AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting actioncontroller api' if AppOpticsAPM::Config[:verbose]
100
100
  require "appoptics_apm/frameworks/rails/inst/action_controller_api"
101
101
  ActionController::API.send(:prepend, ::AppOpticsAPM::Inst::ActionControllerAPI)
@@ -0,0 +1,50 @@
1
+ # Copyright (c) 2016 SolarWinds, LLC.
2
+ # All rights reserved.
3
+
4
+ module AppOpticsAPM
5
+ module Inst
6
+ #
7
+ # ActionController
8
+ #
9
+ # This modules contains the instrumentation code specific
10
+ # to Rails v6
11
+ #
12
+ module ActionController
13
+ include AppOpticsAPM::Inst::RailsBase
14
+
15
+ def process_action(method_name, *args)
16
+ kvs = {
17
+ :Controller => self.class.name,
18
+ :Action => self.action_name,
19
+ }
20
+ request.env['appoptics_apm.controller'] = kvs[:Controller]
21
+ request.env['appoptics_apm.action'] = kvs[:Action]
22
+
23
+ return super(method_name, *args) unless AppOpticsAPM.tracing?
24
+ begin
25
+ kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:action_controller][:collect_backtraces]
26
+
27
+ AppOpticsAPM::API.log_entry('rails', kvs)
28
+ super(method_name, *args)
29
+
30
+ rescue Exception => e
31
+ AppOpticsAPM::API.log_exception('rails', e) if log_rails_error?(e)
32
+ raise
33
+ ensure
34
+ AppOpticsAPM::API.log_exit('rails')
35
+ end
36
+ end
37
+
38
+ #
39
+ # render
40
+ #
41
+ # Our render wrapper that calls 'trace', which will log if we are tracing
42
+ #
43
+ def render(*args, &blk)
44
+ trace('actionview') do
45
+ super(*args, &blk)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -1,7 +1,7 @@
1
1
  # Copyright (c) 2016 SolarWinds, LLC.
2
2
  # All rights reserved.
3
3
 
4
- if defined?(ActionView::Base) && AppOpticsAPM::Config[:action_view][:enabled]
4
+ if defined?(ActionView::Base) && AppOpticsAPM::Config[:action_view][:enabled] && Rails::VERSION::MAJOR < 6
5
5
 
6
6
  ##
7
7
  # ActionView Instrumentation is version dependent. ActionView 2.x is separate
@@ -5,13 +5,13 @@ require 'appoptics_apm/frameworks/rails/inst/connection_adapters/mysql'
5
5
  require 'appoptics_apm/frameworks/rails/inst/connection_adapters/mysql2'
6
6
  require 'appoptics_apm/frameworks/rails/inst/connection_adapters/postgresql'
7
7
 
8
- if AppOpticsAPM::Config[:active_record][:enabled] && !defined?(JRUBY_VERSION)
8
+ if AppOpticsAPM::Config[:active_record][:enabled] && !defined?(JRUBY_VERSION) && Rails::VERSION::MAJOR <= 6
9
9
  begin
10
10
  adapter = ActiveRecord::Base.connection_config[:adapter]
11
11
 
12
12
  if Rails::VERSION::MAJOR < 5
13
13
  require 'appoptics_apm/frameworks/rails/inst/connection_adapters/utils'
14
- else
14
+ elsif Rails::VERSION::MAJOR >= 5
15
15
  require 'appoptics_apm/frameworks/rails/inst/connection_adapters/utils5x'
16
16
  end
17
17
 
@@ -23,13 +23,14 @@ module AppOpticsAPM
23
23
  if current_trace.log?
24
24
  case msg
25
25
  when ::String
26
- msg.strip.empty? ? msg : insert_before_empty_lines(msg, current_trace.for_log)
26
+ msg = msg.strip.empty? ? msg : insert_before_empty_lines(msg, current_trace.for_log)
27
27
  when ::Exception
28
28
  # conversion to String copied from Logger::Formatter private method #msg2str
29
- "#{msg.message} (#{msg.class}) #{current_trace.for_log}\n" <<
30
- (msg.backtrace || []).join("\n")
29
+ msg = ("#{msg.message} (#{msg.class}) #{current_trace.for_log}\n" <<
30
+ (msg.backtrace || []).join("\n"))
31
31
  end
32
32
  end
33
+ msg
33
34
  end
34
35
 
35
36
  def insert_before_empty_lines(msg, for_log)
@@ -27,30 +27,30 @@ if AppOpticsAPM.loaded
27
27
  end
28
28
 
29
29
  def call(env)
30
- incoming = AppOpticsAPM::Context.isValid
31
30
 
32
31
  # In the case of nested Ruby apps such as Grape inside of Rails
33
32
  # or Grape inside of Grape, each app has it's own instance
34
33
  # of rack middleware. We want to avoid tracing rack more than once
35
34
  return @app.call(env) if AppOpticsAPM.tracing? && AppOpticsAPM.layer == :rack
36
35
 
36
+ incoming = AppOpticsAPM::Context.isValid
37
37
  AppOpticsAPM.transaction_name = nil
38
38
 
39
39
  url = env['PATH_INFO']
40
+ options = AppOpticsAPM::XTraceOptions.new(env['HTTP_X_TRACE_OPTIONS'], env['HTTP_X_TRACE_OPTIONS_SIGNATURE'])
40
41
  xtrace = AppOpticsAPM::XTrace.valid?(env['HTTP_X_TRACE']) ? (env['HTTP_X_TRACE']) : nil
41
-
42
- settings = AppOpticsAPM::TransactionSettings.new(url, xtrace)
43
-
44
- # AppOpticsAPM.logger.warn "%%% FILTER: #{settings} %%%"
42
+ settings = AppOpticsAPM::TransactionSettings.new(url, xtrace, options)
45
43
 
46
44
  response =
47
45
  propagate_xtrace(env, settings, xtrace) do
48
- sample(env, settings) do
46
+ sample(env, settings, options) do
49
47
  AppOpticsAPM::TransactionMetrics.metrics(env, settings) do
50
48
  @app.call(env)
51
49
  end
52
50
  end
53
51
  end || [500, {}, nil]
52
+ options.add_response_header(response[1], settings)
53
+
54
54
  AppOpticsAPM::Context.clear unless incoming
55
55
  response
56
56
  rescue
@@ -66,7 +66,7 @@ if AppOpticsAPM.loaded
66
66
 
67
67
  private
68
68
 
69
- def collect(env, settings)
69
+ def collect(env)
70
70
  req = ::Rack::Request.new(env)
71
71
  report_kvs = {}
72
72
 
@@ -84,8 +84,6 @@ if AppOpticsAPM.loaded
84
84
 
85
85
  report_kvs[:URL] = AppOpticsAPM::Config[:rack][:log_args] ? ::CGI.unescape(req.fullpath) : ::CGI.unescape(req.path)
86
86
  report_kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:rack][:collect_backtraces]
87
- report_kvs[:SampleRate] = settings.rate
88
- report_kvs[:SampleSource] = settings.source
89
87
 
90
88
  # Report any request queue'ing headers. Report as 'Request-Start' or the summed Queue-Time
91
89
  report_kvs[:'Request-Start'] = env['HTTP_X_REQUEST_START'] if env.key?('HTTP_X_REQUEST_START')
@@ -126,11 +124,13 @@ if AppOpticsAPM.loaded
126
124
  [status, headers, response]
127
125
  end
128
126
 
129
- def sample(env, settings)
127
+ def sample(env, settings, options)
130
128
  xtrace = env['HTTP_X_TRACE']
131
129
  if settings.do_sample
132
130
  begin
133
- report_kvs = collect(env, settings)
131
+ report_kvs = collect(env)
132
+ settings.add_kvs(report_kvs)
133
+ options&.add_kvs(report_kvs, settings)
134
134
 
135
135
  AppOpticsAPM::API.log_start(:rack, xtrace, report_kvs, settings)
136
136
 
@@ -8,7 +8,7 @@ module AppOpticsAPM
8
8
  class OboeInitOptions
9
9
  include Singleton
10
10
 
11
- attr_reader :reporter, :host, :service_name # exposing these mainly for testing
11
+ attr_reader :reporter, :host, :service_name, :ec2_md_timeout # exposing these mainly for testing
12
12
 
13
13
  # TODO decide if these globals are useful when testing
14
14
  # OBOE_HOSTNAME_ALIAS = 0
@@ -69,6 +69,8 @@ module AppOpticsAPM
69
69
  @token_bucket_rate = (ENV['APPOPTICS_TOKEN_BUCKET_RATE'] || -1).to_i
70
70
  # use single files in file reporter for each event
71
71
  @file_single = (ENV['APPOPTICS_REPORTER_FILE_SINGLE'].to_s.downcase == 'true') ? 1 : 0
72
+ # timeout for ec2 metadata
73
+ @ec2_md_timeout = read_and_validate_ec2_md_timeout
72
74
  end
73
75
 
74
76
  def re_init # for testing with changed ENV vars
@@ -94,7 +96,8 @@ module AppOpticsAPM
94
96
  @histogram_precision,
95
97
  @token_bucket_capacity,
96
98
  @token_bucket_rate,
97
- @file_single
99
+ @file_single,
100
+ @ec2_md_timeout
98
101
  ]
99
102
  end
100
103
 
@@ -108,7 +111,8 @@ module AppOpticsAPM
108
111
 
109
112
  reporter = ENV['APPOPTICS_REPORTER'] || 'ssl'
110
113
  # override with 'file', e.g. when running tests
111
- reporter = 'file' if ENV.key?('APPOPTICS_GEM_TEST')
114
+ # changed my mind => set the right reporter in the env when running tests !!!
115
+ # reporter = 'file' if ENV.key?('APPOPTICS_GEM_TEST')
112
116
 
113
117
  host = ''
114
118
  case reporter
@@ -121,6 +125,7 @@ module AppOpticsAPM
121
125
  # ____ AppOpticsAPM::Config[:reporter_host] and
122
126
  # ____ AppOpticsAPM::Config[:reporter_port] were moved here from
123
127
  # ____ oboe_metal.rb and are not documented anywhere
128
+ # ____ udp is for internal use only
124
129
  when 'null'
125
130
  host = ''
126
131
  end
@@ -148,7 +153,7 @@ module AppOpticsAPM
148
153
  end
149
154
 
150
155
  def validate_token(token)
151
- if (token !~ /^[0-9a-fA-F]{64}|[0-9a-zA-Z_\-]{71}$/)
156
+ if (token !~ /^[0-9a-fA-F]{64}|[0-9a-zA-Z_-]{71}$/) && ENV['APPOPTICS_COLLECTOR'] != "sslcollector:12222"
152
157
  masked = "#{token[0..3]}...#{token[-4..-1]}"
153
158
  AppOpticsAPM.logger.error "[appoptics_apm/oboe_options] APPOPTICS_SERVICE_KEY problem. API Token in wrong format. Masked token: #{masked}"
154
159
  return false
@@ -158,6 +163,7 @@ module AppOpticsAPM
158
163
  end
159
164
 
160
165
  def validate_transform_service_name(service_name)
166
+ service_name = 'test_ssl_collector' if ENV['APPOPTICS_COLLECTOR'] == "sslcollector:12222"
161
167
  if service_name.empty?
162
168
  AppOpticsAPM.logger.error "[appoptics_apm/oboe_options] APPOPTICS_SERVICE_KEY problem. Service Name is missing"
163
169
  return false
@@ -175,6 +181,13 @@ module AppOpticsAPM
175
181
  @service_name = service_name # instance variable used in testing
176
182
  true
177
183
  end
184
+
185
+ def read_and_validate_ec2_md_timeout
186
+ timeout = (ENV['APPOPTICS_EC2_METADATA_TIMEOUT'] || AppOpticsAPM::Config[:ec2_metadata_timeout])
187
+ return 1000 unless timeout.is_a?(Integer) || timeout =~ /^\d+$/
188
+ timeout = timeout.to_i
189
+ return timeout.between?(0, 3000) ? timeout : 1000
190
+ end
178
191
  end
179
192
  end
180
193
 
@@ -6,14 +6,9 @@ AO_TRACING_ENABLED = 1
6
6
  AO_TRACING_DISABLED = 0
7
7
  AO_TRACING_UNSET = -1
8
8
 
9
- AO_TRACING_DECISIONS_TRACING_DISABLED = -2
10
- AO_TRACING_DECISIONS_XTRACE_NOT_SAMPLED = -1
11
9
  AO_TRACING_DECISIONS_OK = 0
12
- AO_TRACING_DECISIONS_NULL_OUT = 1
13
- AO_TRACING_DECISIONS_NO_CONFIG = 2
14
- AO_TRACING_DECISIONS_REPORTER_NOT_READY = 3
15
- AO_TRACING_DECISIONS_NO_VALID_SETTINGS = 4
16
- AO_TRACING_DECISIONS_QUEUE_FULL = 5
10
+
11
+ OBOE_SETTINGS_UNSET = -1
17
12
 
18
13
  module AppOpticsAPM
19
14
  ##
@@ -21,13 +16,15 @@ module AppOpticsAPM
21
16
  #
22
17
  class TransactionSettings
23
18
 
24
- attr_accessor :do_metrics, :do_sample
25
- attr_reader :do_propagate, :rate, :source
19
+ attr_accessor :do_sample, :do_metrics
20
+ attr_reader :auth_msg, :do_propagate, :status_msg, :type, :source, :rate, :xtrace
21
+ #, :status
26
22
 
27
- def initialize(url = nil, xtrace = '')
23
+ def initialize(url = '', xtrace = '', options = nil)
28
24
  @do_metrics = false
29
25
  @do_sample = false
30
26
  @do_propagate = true
27
+ @xtrace = xtrace || ''
31
28
  tracing_mode = AO_TRACING_ENABLED
32
29
 
33
30
  if AppOpticsAPM::Context.isValid
@@ -46,15 +43,23 @@ module AppOpticsAPM
46
43
  tracing_mode = AO_TRACING_DISABLED
47
44
  end
48
45
 
49
- args = [xtrace || '']
46
+ args = [@xtrace]
50
47
  args << tracing_mode
51
- args << AppOpticsAPM::Config[:sample_rate] if AppOpticsAPM::Config[:sample_rate]&. >= 0
48
+ args << (AppOpticsAPM::Config[:sample_rate] || OBOE_SETTINGS_UNSET)
49
+
50
+ if options && (options.options || options.signature)
51
+ args << (options.trigger_trace ? 1 : 0)
52
+ args << (trigger_tracing_mode_disabled? ? 0 : 1)
53
+ args << options.options
54
+ args << options.signature
55
+ args << options.timestamp
56
+ end
52
57
 
53
- metrics, sample, @rate, @source, return_code = AppOpticsAPM::Context.getDecisions(*args)
58
+ metrics, sample, @rate, @source, @type, @auth, @status_msg, @auth_msg, @status =
59
+ AppOpticsAPM::Context.getDecisions(*args)
54
60
 
55
- puts "return_code class: #{return_code.class}" unless return_code.is_a? Integer
56
- if return_code > AO_TRACING_DECISIONS_OK
57
- AppOpticsAPM.logger.warn "[appoptics-apm/sample] Problem getting the sampling decisions, code: #{return_code}"
61
+ if @status > AO_TRACING_DECISIONS_OK
62
+ AppOpticsAPM.logger.warn "[appoptics-apm/sample] Problem getting the sampling decisions: #{@status_msg} code: #{@status}"
58
63
  end
59
64
 
60
65
  @do_metrics = metrics > 0
@@ -65,6 +70,20 @@ module AppOpticsAPM
65
70
  "do_propagate: #{do_propagate}, do_sample: #{do_sample}, do_metrics: #{do_metrics} rate: #{rate}, source: #{source}"
66
71
  end
67
72
 
73
+ def add_kvs(kvs)
74
+ kvs[:SampleRate] = @rate
75
+ kvs[:SampleSource] = @source
76
+ end
77
+
78
+ def triggered_trace?
79
+ @type == 1
80
+ end
81
+
82
+ def auth_ok?
83
+ # @auth is undefined if initialize is called with an existing context
84
+ !@auth || @auth < 1
85
+ end
86
+
68
87
  private
69
88
 
70
89
  ##
@@ -104,6 +123,11 @@ module AppOpticsAPM
104
123
  false
105
124
  end
106
125
 
126
+ def trigger_tracing_mode_disabled?
127
+ AppOpticsAPM::Config[:trigger_tracing_mode] &&
128
+ AppOpticsAPM::Config[:trigger_tracing_mode] == :disabled
129
+ end
130
+
107
131
  ##
108
132
  # asset?
109
133
  #
@@ -0,0 +1,110 @@
1
+ # Copyright (c) 2019 SolarWinds, LLC.
2
+ # All rights reserved.
3
+
4
+
5
+ module AppOpticsAPM
6
+ class XTraceOptions
7
+
8
+ attr_reader :options, :signature, :trigger_trace, :timestamp
9
+ attr_reader :pd_keys, :custom_kvs, :ignored # used in tests
10
+ ##
11
+ # Params:
12
+ # +options+ : An X-Trace-Options @options string
13
+ # +signature+ : hmac signature to pass on for verification
14
+ #
15
+ # populates:
16
+ # - @force_trace (true|false)
17
+ # - @app_id (as given by Pingdom)
18
+ # - @probe_id (as given by Pingdom)
19
+ # - @loc (2 characters given by Pingdom)
20
+ # - @custom_kvs (hash)
21
+ # - @ignored (array)
22
+ #
23
+ # split it up by ';' separator
24
+ # kv assignment by '='
25
+ # currently valid keys:
26
+ # - force_trace (valid: 0,1) unless we use just a kv
27
+ # - application_id (format defined by pingdom (no validation))
28
+ # - probe_id
29
+ # - custom_* (';=' not allowed in key), value (validate max. length)
30
+ # - ts (unix timestamp)
31
+ # - other keys will be reported in the response options as ignored
32
+
33
+ def initialize(options, signature = nil)
34
+ @options = options.dup
35
+ @signature = signature.dup
36
+ @trigger_trace = false
37
+ @custom_kvs = {}
38
+ @pd_keys = nil
39
+ @ignored = []
40
+ @timestamp = 0
41
+
42
+ options&.split(/;+/)&.each do |val|
43
+ k = val.split('=', 2)
44
+
45
+ next unless k[0] # it can be nil, eg when the header starts with ';'
46
+
47
+ k[0]&.strip!
48
+ case k[0]
49
+ when 'trigger-trace'
50
+ if k[1]
51
+ @ignored << 'trigger-trace'
52
+ else
53
+ @trigger_trace = true
54
+ end
55
+ when 'pd-keys'
56
+ if @pd_keys
57
+ AppOpticsAPM.logger.info "[appoptics_apm/x-trace-options] Duplicate key: #{k[0]}"
58
+ else
59
+ @pd_keys = k[1].strip
60
+ end
61
+ when /^custom-[^\s]*$/
62
+ if @custom_kvs[k[0]]
63
+ AppOpticsAPM.logger.info "[appoptics_apm/x-trace-options] Duplicate key: #{k[0]}"
64
+ else
65
+ @custom_kvs[k[0]] = k[1].strip
66
+ end
67
+ when 'ts'
68
+ if @timestamp > 0
69
+ AppOpticsAPM.logger.info "[appoptics_apm/x-trace-options] Duplicate key: #{k[0]}"
70
+ else
71
+ @timestamp = k[1].to_i
72
+ end
73
+ else
74
+ @ignored << k[0]
75
+ end
76
+ end
77
+ unless @ignored.empty?
78
+ msg = "[appoptics_apm/x-trace-options] Some keys were ignored: #{@ignored.join(',' )}"
79
+ AppOpticsAPM.logger.info(msg)
80
+ end
81
+ end
82
+
83
+ def add_kvs(kvs, settings)
84
+ return unless settings.auth_ok?
85
+
86
+ @custom_kvs.each { |k,v| kvs[k] = v } unless @custom_kvs.empty?
87
+ kvs['PDKeys'] = @pd_keys if @pd_keys
88
+ kvs['TriggeredTrace'] = true if settings.triggered_trace?
89
+ end
90
+
91
+ def add_response_header(headers, settings)
92
+ return unless options
93
+
94
+ response = []
95
+ response << "auth=#{settings.auth_msg}" if @signature
96
+ if settings.auth_ok?
97
+ if @trigger_trace
98
+ trigger_msg = !settings.xtrace.empty? && settings.type == 0 ? 'ignored' : settings.status_msg
99
+ else
100
+ trigger_msg = 'not-requested'
101
+ end
102
+ response << "trigger-trace=#{trigger_msg}"
103
+ response << "ignored=#{@ignored.join(',')}" unless @ignored.empty?
104
+ end
105
+
106
+ headers['X-Trace-Options-Response'] = response.join(';')
107
+ end
108
+
109
+ end
110
+ end