appoptics_apm 4.8.0 → 4.9.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|