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.
- checksums.yaml +4 -4
- data/.gitignore +5 -1
- data/.irbrc +3 -0
- data/.travis.yml +39 -24
- data/CONFIG.md +1 -1
- data/README.md +1 -1
- data/Rakefile +60 -53
- data/appoptics_apm.gemspec +1 -1
- data/build_gem_upload_to_packagecloud.sh +1 -6
- data/examples/SDK/01_basic_tracing.rb +0 -2
- data/ext/oboe_metal/README.md +69 -0
- data/ext/oboe_metal/src/VERSION +1 -1
- data/ext/oboe_metal/src/oboe.h +102 -71
- data/ext/oboe_metal/src/oboe.hpp +111 -69
- data/ext/oboe_metal/src/oboe_wrap.cxx +1470 -314
- data/lib/appoptics_apm.rb +1 -0
- data/lib/appoptics_apm/config.rb +11 -4
- data/lib/appoptics_apm/frameworks/rails/inst/action_controller.rb +4 -4
- data/lib/appoptics_apm/frameworks/rails/inst/action_controller6.rb +50 -0
- data/lib/appoptics_apm/frameworks/rails/inst/action_view.rb +1 -1
- data/lib/appoptics_apm/frameworks/rails/inst/active_record.rb +2 -2
- data/lib/appoptics_apm/inst/logger_formatter.rb +4 -3
- data/lib/appoptics_apm/inst/rack.rb +11 -11
- data/lib/appoptics_apm/oboe_init_options.rb +17 -4
- data/lib/appoptics_apm/support/transaction_settings.rb +40 -16
- data/lib/appoptics_apm/support/x_trace_options.rb +110 -0
- data/lib/appoptics_apm/version.rb +1 -1
- data/lib/rails/generators/appoptics_apm/templates/appoptics_apm_initializer.rb +37 -12
- data/oboe.code-workspace +66 -0
- data/scrap.rb +134 -0
- data/scrap_gemfile +5 -0
- data/scrap_gemfile.lock +140 -0
- metadata +12 -4
data/lib/appoptics_apm.rb
CHANGED
data/lib/appoptics_apm/config.rb
CHANGED
@@ -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,
|
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,
|
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
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
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
|
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
|
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_
|
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
|
-
|
13
|
-
|
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 :
|
25
|
-
attr_reader :do_propagate, :rate, :
|
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 =
|
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]
|
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,
|
58
|
+
metrics, sample, @rate, @source, @type, @auth, @status_msg, @auth_msg, @status =
|
59
|
+
AppOpticsAPM::Context.getDecisions(*args)
|
54
60
|
|
55
|
-
|
56
|
-
|
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
|