appoptics_apm 4.8.4 → 4.11.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +7 -1
- data/.rubocop.yml +27 -6
- data/.travis.yml +24 -37
- data/.travis/bundle.sh +9 -0
- data/CONFIG.md +1 -1
- data/Gemfile +6 -6
- data/appoptics_apm.gemspec +6 -2
- data/examples/SDK/01_basic_tracing.rb +1 -1
- data/ext/oboe_metal/extconf.rb +6 -2
- data/ext/oboe_metal/noop/noop.c +2 -1
- data/ext/oboe_metal/src/VERSION +1 -1
- data/lib/appoptics_apm.rb +1 -3
- data/lib/appoptics_apm/api.rb +0 -1
- data/lib/appoptics_apm/api/logging.rb +6 -2
- data/lib/appoptics_apm/api/tracing.rb +4 -0
- data/lib/appoptics_apm/api/util.rb +5 -7
- data/lib/appoptics_apm/config.rb +16 -5
- data/lib/appoptics_apm/frameworks/grape.rb +3 -2
- data/lib/appoptics_apm/frameworks/padrino.rb +7 -37
- data/lib/appoptics_apm/frameworks/rails.rb +0 -1
- data/lib/appoptics_apm/frameworks/rails/inst/action_controller.rb +1 -1
- data/lib/appoptics_apm/frameworks/rails/inst/action_view.rb +12 -25
- data/lib/appoptics_apm/frameworks/rails/inst/active_record.rb +1 -1
- data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/utils.rb +1 -1
- data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/utils5x.rb +1 -1
- data/lib/appoptics_apm/frameworks/sinatra.rb +4 -33
- data/lib/appoptics_apm/inst/curb.rb +6 -6
- data/lib/appoptics_apm/inst/faraday.rb +16 -4
- data/lib/appoptics_apm/inst/graphql.rb +240 -0
- data/lib/appoptics_apm/inst/grpc_client.rb +1 -1
- data/lib/appoptics_apm/inst/rack.rb +11 -11
- data/lib/appoptics_apm/oboe_init_options.rb +13 -3
- data/lib/appoptics_apm/sdk/custom_metrics.rb +2 -0
- data/lib/appoptics_apm/sdk/logging.rb +1 -1
- data/lib/appoptics_apm/sdk/tracing.rb +120 -2
- data/lib/appoptics_apm/support/transaction_metrics.rb +2 -1
- data/lib/appoptics_apm/support/transaction_settings.rb +40 -15
- data/lib/appoptics_apm/support/x_trace_options.rb +110 -0
- data/lib/appoptics_apm/version.rb +2 -2
- data/lib/appoptics_apm/xtrace.rb +7 -7
- data/lib/oboe_metal.rb +1 -1
- data/lib/rails/generators/appoptics_apm/install_generator.rb +23 -21
- data/lib/rails/generators/appoptics_apm/templates/appoptics_apm_initializer.rb +68 -30
- metadata +40 -21
- data/Rakefile +0 -234
- data/build_gem.sh +0 -15
- data/build_gem_upload_to_packagecloud.sh +0 -15
- data/lib/appoptics_apm/api/profiling.rb +0 -203
- data/lib/appoptics_apm/frameworks/rails/inst/action_controller3.rb +0 -55
- data/lib/appoptics_apm/frameworks/rails/inst/action_view_30.rb +0 -50
- data/lib/appoptics_apm/legacy_method_profiling.rb +0 -90
- data/lib/appoptics_apm/method_profiling.rb +0 -33
- data/lib/oboe/README +0 -2
- data/lib/oboe/backward_compatibility.rb +0 -80
- data/lib/oboe/inst/rack.rb +0 -11
@@ -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
|
|
@@ -150,7 +153,7 @@ module AppOpticsAPM
|
|
150
153
|
end
|
151
154
|
|
152
155
|
def validate_token(token)
|
153
|
-
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"
|
154
157
|
masked = "#{token[0..3]}...#{token[-4..-1]}"
|
155
158
|
AppOpticsAPM.logger.error "[appoptics_apm/oboe_options] APPOPTICS_SERVICE_KEY problem. API Token in wrong format. Masked token: #{masked}"
|
156
159
|
return false
|
@@ -178,6 +181,13 @@ module AppOpticsAPM
|
|
178
181
|
@service_name = service_name # instance variable used in testing
|
179
182
|
true
|
180
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
|
181
191
|
end
|
182
192
|
end
|
183
193
|
|
@@ -32,6 +32,7 @@ module AppOpticsAPM
|
|
32
32
|
# * true on success, false on failure
|
33
33
|
#
|
34
34
|
def increment_metric(name, count = 1, with_hostname = false, tags_kvs = {})
|
35
|
+
return true unless AppOpticsAPM.loaded
|
35
36
|
with_hostname = with_hostname ? 1 : 0
|
36
37
|
tags, tags_count = make_tags(tags_kvs)
|
37
38
|
AppOpticsAPM::CustomMetrics.increment(name.to_s, count, with_hostname, nil, tags, tags_count) == 1
|
@@ -64,6 +65,7 @@ module AppOpticsAPM
|
|
64
65
|
# * true on success, false on failure
|
65
66
|
#
|
66
67
|
def summary_metric(name, value, count = 1, with_hostname = false, tags_kvs = {})
|
68
|
+
return true unless AppOpticsAPM.loaded
|
67
69
|
with_hostname = with_hostname ? 1 : 0
|
68
70
|
tags, tags_count = make_tags(tags_kvs)
|
69
71
|
AppOpticsAPM::CustomMetrics.summary(name.to_s, value, count, with_hostname, nil, tags, tags_count) == 1
|
@@ -25,7 +25,7 @@ module AppOpticsAPM
|
|
25
25
|
# * +exception+ - an exception, must respond to :message and :backtrace
|
26
26
|
# * +opts+ - (optional) hash containing key/value pairs that will be reported with this span.
|
27
27
|
#
|
28
|
-
def log_exception(exception, opts)
|
28
|
+
def log_exception(exception, opts = {})
|
29
29
|
AppOpticsAPM::API.log_exception(AppOpticsAPM.layer, exception, opts)
|
30
30
|
end
|
31
31
|
|
@@ -32,6 +32,7 @@ module AppOpticsAPM
|
|
32
32
|
# * +AppOpticsAPM::SDK.start_trace+
|
33
33
|
# * +AppOpticsAPM::SDK.start_trace_with_target+
|
34
34
|
# * +AppOpticsAPM::SDK.trace+
|
35
|
+
# * +AppOpticsAPM::SDK.trace_method+
|
35
36
|
# * +AppOpticsAPM::SDK.tracing?+
|
36
37
|
#
|
37
38
|
# === Example:
|
@@ -207,6 +208,123 @@ module AppOpticsAPM
|
|
207
208
|
result
|
208
209
|
end
|
209
210
|
|
211
|
+
##
|
212
|
+
# Add tracing to a given method
|
213
|
+
#
|
214
|
+
# This instruments the given method so that every time it is called it
|
215
|
+
# will create a span depending on the current context.
|
216
|
+
#
|
217
|
+
# The method can be of any (accessible) type (instance, singleton,
|
218
|
+
# private, protected etc.).
|
219
|
+
#
|
220
|
+
# The motivating use case for this is MetalController methods in Rails,
|
221
|
+
# which can't be auto-instrumented.
|
222
|
+
#
|
223
|
+
# === Arguments:
|
224
|
+
# * +klass+ - The module/class the method belongs to.
|
225
|
+
# * +method+ - The method name as symbol
|
226
|
+
# * +config+ - (optional) possible keys are:
|
227
|
+
# :name the name of the span (default: the method name)
|
228
|
+
# :backtrace true/false (default: false) if true the backtrace will be added to the space
|
229
|
+
# * +opts+ - (optional) hash containing key/value pairs that will be reported with this span.
|
230
|
+
#
|
231
|
+
# === Example:
|
232
|
+
#
|
233
|
+
# module ExampleModule
|
234
|
+
# def do_sum(a, b)
|
235
|
+
# a + b
|
236
|
+
# end
|
237
|
+
# end
|
238
|
+
#
|
239
|
+
# AppOpticsAPM::SDK.trace_method(ExampleModule, :do_sum, {name: 'computation', backtrace: true}, { CustomKey: "some_info"})
|
240
|
+
#
|
241
|
+
def trace_method(klass, method, config = {}, opts = {})
|
242
|
+
# If we're on an unsupported platform (ahem Mac), just act
|
243
|
+
# like we did something to nicely play the no-op part.
|
244
|
+
return true unless AppOpticsAPM.loaded
|
245
|
+
|
246
|
+
if !klass.is_a?(Module)
|
247
|
+
AppOpticsAPM.logger.warn "[appoptics_apm/error] trace_method: Not sure what to do with #{klass}. Send a class or module."
|
248
|
+
return false
|
249
|
+
end
|
250
|
+
|
251
|
+
if method.is_a?(String)
|
252
|
+
method = method.to_sym
|
253
|
+
elsif !method.is_a?(Symbol)
|
254
|
+
AppOpticsAPM.logger.warn "[appoptics_apm/error] trace_method: Not sure what to do with #{method}. Send a string or symbol for method."
|
255
|
+
return false
|
256
|
+
end
|
257
|
+
|
258
|
+
instance_method = klass.instance_methods.include?(method) || klass.private_instance_methods.include?(method)
|
259
|
+
class_method = klass.singleton_methods.include?(method)
|
260
|
+
|
261
|
+
# Make sure the request klass::method exists
|
262
|
+
if !instance_method && !class_method
|
263
|
+
AppOpticsAPM.logger.warn "[appoptics_apm/error] trace_method: Can't instrument #{klass}.#{method} as it doesn't seem to exist."
|
264
|
+
AppOpticsAPM.logger.warn "[appoptics_apm/error] #{__FILE__}:#{__LINE__}"
|
265
|
+
return false
|
266
|
+
end
|
267
|
+
|
268
|
+
# Strip '!' or '?' from method if present
|
269
|
+
safe_method_name = method.to_s.chop if method.to_s =~ /\?$|\!$/
|
270
|
+
safe_method_name ||= method
|
271
|
+
|
272
|
+
without_appoptics = "#{safe_method_name}_without_appoptics"
|
273
|
+
with_appoptics = "#{safe_method_name}_with_appoptics"
|
274
|
+
|
275
|
+
# Check if already profiled
|
276
|
+
if instance_method && klass.instance_methods.include?(with_appoptics.to_sym) ||
|
277
|
+
class_method && klass.singleton_methods.include?(with_appoptics.to_sym)
|
278
|
+
AppOpticsAPM.logger.warn "[appoptics_apm/error] trace_method: #{klass}::#{method} already instrumented.\n#{__FILE__}:#{__LINE__}"
|
279
|
+
return false
|
280
|
+
end
|
281
|
+
|
282
|
+
report_kvs = opts.dup
|
283
|
+
if defined?(::AbstractController::Base) && klass.ancestors.include?(::AbstractController::Base)
|
284
|
+
report_kvs[:Controller] = klass.to_s
|
285
|
+
report_kvs[:Action] = method.to_s
|
286
|
+
else
|
287
|
+
klass.is_a?(Class) ? report_kvs[:Class] = klass.to_s : report_kvs[:Module] = klass.to_s
|
288
|
+
report_kvs[:MethodName] = safe_method_name
|
289
|
+
end
|
290
|
+
backtrace = config[:backtrace]
|
291
|
+
|
292
|
+
span = config[:name] || method
|
293
|
+
if instance_method
|
294
|
+
klass.class_eval do
|
295
|
+
define_method(with_appoptics) do |*args, &block|
|
296
|
+
# if this is a rails controller we want to set the transaction for the outbound metrics
|
297
|
+
if report_kvs[:Controller] && defined?(request) && defined?(request.env)
|
298
|
+
request.env['appoptics_apm.controller'] = report_kvs[:Controller]
|
299
|
+
request.env['appoptics_apm.action'] = report_kvs[:Action]
|
300
|
+
end
|
301
|
+
|
302
|
+
AppOpticsAPM::SDK.trace(span, report_kvs) do
|
303
|
+
report_kvs[:Backtrace] = AppOpticsAPM::API.backtrace if backtrace
|
304
|
+
send(without_appoptics, *args, &block)
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
alias_method without_appoptics, method.to_s
|
309
|
+
alias_method method.to_s, with_appoptics
|
310
|
+
end
|
311
|
+
elsif class_method
|
312
|
+
klass.define_singleton_method(with_appoptics) do |*args, &block|
|
313
|
+
AppOpticsAPM::SDK.trace(span, report_kvs) do
|
314
|
+
report_kvs[:Backtrace] = AppOpticsAPM::API.backtrace if backtrace
|
315
|
+
send(without_appoptics, *args, &block)
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
klass.singleton_class.class_eval do
|
320
|
+
alias_method without_appoptics, method.to_s
|
321
|
+
alias_method method.to_s, with_appoptics
|
322
|
+
end
|
323
|
+
end
|
324
|
+
true
|
325
|
+
end
|
326
|
+
|
327
|
+
##
|
210
328
|
# Provide a custom transaction name
|
211
329
|
#
|
212
330
|
# The AppOpticsAPM gem tries to create meaningful transaction names from controller+action
|
@@ -293,7 +411,7 @@ module AppOpticsAPM
|
|
293
411
|
#
|
294
412
|
# === Example:
|
295
413
|
#
|
296
|
-
# unless
|
414
|
+
# unless AppOpticsAPM::SDK.appoptics_ready?(10_000)
|
297
415
|
# Logger.info "AppOptics not ready after 10 seconds, no metrics will be sent"
|
298
416
|
# end
|
299
417
|
#
|
@@ -306,7 +424,7 @@ module AppOpticsAPM
|
|
306
424
|
# OBOE_SERVER_RESPONSE_LIMIT_EXCEEDED 3
|
307
425
|
# OBOE_SERVER_RESPONSE_INVALID_API_KEY 4
|
308
426
|
# OBOE_SERVER_RESPONSE_CONNECT_ERROR 5
|
309
|
-
|
427
|
+
AppOpticsAPM::Context.isReady(wait_milliseconds) == 1
|
310
428
|
end
|
311
429
|
end
|
312
430
|
|
@@ -15,6 +15,7 @@ module AppOpticsAPM
|
|
15
15
|
def metrics(env, settings)
|
16
16
|
if settings.do_metrics
|
17
17
|
req = ::Rack::Request.new(env)
|
18
|
+
# TODO rails 3x is not supported anymore ...
|
18
19
|
url = req.url # saving it here because rails3.2 overrides it when there is a 500 error
|
19
20
|
start = Time.now
|
20
21
|
|
@@ -63,4 +64,4 @@ module AppOpticsAPM
|
|
63
64
|
|
64
65
|
end
|
65
66
|
end
|
66
|
-
end
|
67
|
+
end
|
@@ -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,14 +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
|
-
if
|
56
|
-
AppOpticsAPM.logger.warn "[appoptics-apm/sample] Problem getting the sampling decisions
|
61
|
+
if @status > AO_TRACING_DECISIONS_OK
|
62
|
+
AppOpticsAPM.logger.warn "[appoptics-apm/sample] Problem getting the sampling decisions: #{@status_msg} code: #{@status}"
|
57
63
|
end
|
58
64
|
|
59
65
|
@do_metrics = metrics > 0
|
@@ -64,6 +70,20 @@ module AppOpticsAPM
|
|
64
70
|
"do_propagate: #{do_propagate}, do_sample: #{do_sample}, do_metrics: #{do_metrics} rate: #{rate}, source: #{source}"
|
65
71
|
end
|
66
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
|
+
|
67
87
|
private
|
68
88
|
|
69
89
|
##
|
@@ -103,6 +123,11 @@ module AppOpticsAPM
|
|
103
123
|
false
|
104
124
|
end
|
105
125
|
|
126
|
+
def trigger_tracing_mode_disabled?
|
127
|
+
AppOpticsAPM::Config[:trigger_tracing_mode] &&
|
128
|
+
AppOpticsAPM::Config[:trigger_tracing_mode] == :disabled
|
129
|
+
end
|
130
|
+
|
106
131
|
##
|
107
132
|
# asset?
|
108
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
|