appoptics_apm 4.5.2 → 4.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.travis.yml +5 -4
- data/Gemfile +3 -3
- data/README.md +3 -3
- data/Rakefile +9 -11
- data/appoptics_apm.gemspec +2 -4
- data/build_gem_upload_to_packagecloud.sh +2 -1
- data/examples/SDK/01_basic_tracing.rb +2 -1
- data/ext/oboe_metal/src/VERSION +1 -1
- data/lib/appoptics_apm.rb +2 -1
- data/lib/appoptics_apm/api/layerinit.rb +1 -1
- data/lib/appoptics_apm/api/logging.rb +75 -73
- data/lib/appoptics_apm/base.rb +13 -9
- data/lib/appoptics_apm/config.rb +33 -6
- data/lib/appoptics_apm/inst/curb.rb +2 -0
- data/lib/appoptics_apm/inst/rack.rb +68 -92
- data/lib/appoptics_apm/logger.rb +29 -27
- data/lib/appoptics_apm/sdk/tracing.rb +1 -0
- data/lib/appoptics_apm/support/transaction_metrics.rb +66 -0
- data/lib/appoptics_apm/support/transaction_settings.rb +186 -0
- data/lib/appoptics_apm/{support.rb → support_report.rb} +0 -0
- data/lib/appoptics_apm/test.rb +2 -2
- data/lib/appoptics_apm/util.rb +21 -15
- data/lib/appoptics_apm/version.rb +2 -2
- data/lib/appoptics_apm/xtrace.rb +2 -3
- data/lib/joboe_metal.rb +2 -2
- data/lib/oboe_metal.rb +5 -5
- data/lib/rails/generators/appoptics_apm/templates/appoptics_apm_initializer.rb +64 -25
- metadata +12 -24
data/lib/appoptics_apm/config.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# Copyright (c) 2016 SolarWinds, LLC.
|
2
2
|
# All rights reserved.
|
3
3
|
|
4
|
+
require_relative 'support/transaction_settings'
|
5
|
+
|
4
6
|
module AppOpticsAPM
|
5
7
|
##
|
6
8
|
# This module exposes a nested configuration hash that can be used to
|
@@ -48,8 +50,7 @@ module AppOpticsAPM
|
|
48
50
|
elsif File.exist?(File.join(ENV['APPOPTICS_APM_CONFIG_RUBY'], 'appoptics_apm_config.rb'))
|
49
51
|
config_files << File.join(ENV['APPOPTICS_APM_CONFIG_RUBY'], 'appoptics_apm_config.rb')
|
50
52
|
else
|
51
|
-
|
52
|
-
$stderr.puts "#{ENV['APPOPTICS_APM_CONFIG_RUBY']}"
|
53
|
+
AppOpticsAPM.logger.warn "[appoptics_apm/config] Could not find the configuration file set by the APPOPTICS_APM_CONFIG_RUBY environment variable: #{ENV['APPOPTICS_APM_CONFIG_RUBY']}"
|
53
54
|
end
|
54
55
|
end
|
55
56
|
|
@@ -60,8 +61,10 @@ module AppOpticsAPM
|
|
60
61
|
return if config_files.empty? # we use the defaults from the template in this case
|
61
62
|
|
62
63
|
if config_files.size > 1
|
63
|
-
|
64
|
-
|
64
|
+
AppOpticsAPM.logger.warn [
|
65
|
+
'[appoptics_apm/config] Multiple configuration files configured, using the first one listed: ',
|
66
|
+
config_files.join(', ')
|
67
|
+
].join(' ')
|
65
68
|
end
|
66
69
|
load(config_files[0])
|
67
70
|
check_env_vars
|
@@ -174,11 +177,12 @@ module AppOpticsAPM
|
|
174
177
|
#
|
175
178
|
# rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
176
179
|
def self.[]=(key, value)
|
177
|
-
|
180
|
+
key = key.to_sym
|
181
|
+
@@config[key] = value
|
178
182
|
|
179
183
|
if key == :sampling_rate
|
180
184
|
AppOpticsAPM.logger.warn '[appoptics_apm/config] sampling_rate is not a supported setting for AppOpticsAPM::Config. ' \
|
181
|
-
|
185
|
+
'Please use :sample_rate.'
|
182
186
|
|
183
187
|
elsif key == :sample_rate
|
184
188
|
unless value.is_a?(Integer) || value.is_a?(Float)
|
@@ -202,6 +206,27 @@ module AppOpticsAPM
|
|
202
206
|
elsif key == :action_blacklist
|
203
207
|
AppOpticsAPM.logger.warn "[appoptics_apm/config] :action_blacklist has been deprecated and no longer functions."
|
204
208
|
|
209
|
+
elsif key == :dnt_regexp
|
210
|
+
if value.nil? || value == ''
|
211
|
+
@@config[:dnt_compiled] = nil
|
212
|
+
else
|
213
|
+
@@config[:dnt_compiled] =
|
214
|
+
Regexp.new(AppOpticsAPM::Config[:dnt_regexp], AppOpticsAPM::Config[:dnt_opts] || nil)
|
215
|
+
end
|
216
|
+
|
217
|
+
elsif key == :dnt_opts
|
218
|
+
if AppOpticsAPM::Config[:dnt_regexp] && AppOpticsAPM::Config[:dnt_regexp] != ''
|
219
|
+
@@config[:dnt_compiled] =
|
220
|
+
Regexp.new(AppOpticsAPM::Config[:dnt_regexp], AppOpticsAPM::Config[:dnt_opts] || nil)
|
221
|
+
end
|
222
|
+
|
223
|
+
elsif key == :transaction_settings
|
224
|
+
if value.is_a?(Hash)
|
225
|
+
AppOpticsAPM::TransactionSettings.compile_url_settings(value[:url])
|
226
|
+
else
|
227
|
+
AppOpticsAPM::TransactionSettings.reset_url_regexps
|
228
|
+
end
|
229
|
+
|
205
230
|
elsif key == :resque
|
206
231
|
AppOpticsAPM.logger.warn "[appoptics_apm/config] :resque config is deprecated. It is now split into :resqueclient and :resqueworker."
|
207
232
|
AppOpticsAPM.logger.warn "[appoptics_apm/config] Called from #{Kernel.caller[0]}"
|
@@ -228,6 +253,8 @@ module AppOpticsAPM
|
|
228
253
|
end
|
229
254
|
# rubocop:enable Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
230
255
|
|
256
|
+
|
257
|
+
|
231
258
|
def self.method_missing(sym, *args)
|
232
259
|
class_var_name = "@@#{sym}"
|
233
260
|
|
@@ -291,6 +291,7 @@ module AppOpticsAPM
|
|
291
291
|
# If we're not tracing or we're already tracing curb, just do a fast return.
|
292
292
|
if !AppOpticsAPM.tracing? || [:curb, :curb_multi].include?(AppOpticsAPM.layer)
|
293
293
|
self.requests.each do |request|
|
294
|
+
request = request[1] if request.is_a?(Array)
|
294
295
|
unless AppOpticsAPM::API.blacklisted?(URI(request.url).hostname)
|
295
296
|
request.headers['X-Trace'] = AppOpticsAPM::Context.toString if AppOpticsAPM::Context.isValid
|
296
297
|
end
|
@@ -305,6 +306,7 @@ module AppOpticsAPM
|
|
305
306
|
AppOpticsAPM::API.log_entry(:curb_multi, kvs)
|
306
307
|
|
307
308
|
self.requests.each do |request|
|
309
|
+
request = request[1] if request.is_a?(Array)
|
308
310
|
unless AppOpticsAPM::API.blacklisted?(URI(request.url).hostname)
|
309
311
|
request.headers['X-Trace'] = AppOpticsAPM::Context.toString if AppOpticsAPM::Context.isValid
|
310
312
|
end
|
@@ -17,7 +17,7 @@ if AppOpticsAPM.loaded
|
|
17
17
|
# After the rack layer passes on to the following layers (Rails, Sinatra,
|
18
18
|
# Padrino, Grape), then the instrumentation downstream will
|
19
19
|
# automatically detect whether this is a sampled request or not
|
20
|
-
# and act accordingly.
|
20
|
+
# and act accordingly.
|
21
21
|
#
|
22
22
|
class Rack
|
23
23
|
attr_reader :app
|
@@ -27,17 +27,37 @@ if AppOpticsAPM.loaded
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def call(env)
|
30
|
+
incoming = AppOpticsAPM::Context.isValid
|
31
|
+
|
30
32
|
# In the case of nested Ruby apps such as Grape inside of Rails
|
31
33
|
# or Grape inside of Grape, each app has it's own instance
|
32
|
-
# of rack middleware.
|
33
|
-
|
34
|
-
return call_app(env) if AppOpticsAPM.tracing? && AppOpticsAPM.layer == :rack
|
34
|
+
# of rack middleware. We want to avoid tracing rack more than once
|
35
|
+
return @app.call(env) if AppOpticsAPM.tracing? && AppOpticsAPM.layer == :rack
|
35
36
|
|
36
|
-
|
37
|
-
return sampling_call(env) if AppOpticsAPM::Context.isValid
|
37
|
+
AppOpticsAPM.transaction_name = nil
|
38
38
|
|
39
|
-
|
40
|
-
|
39
|
+
url = env['PATH_INFO']
|
40
|
+
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} %%%"
|
45
|
+
|
46
|
+
response =
|
47
|
+
propagate_xtrace(env, settings, xtrace) do
|
48
|
+
sample(env, settings) do
|
49
|
+
AppOpticsAPM::TransactionMetrics.metrics(env, settings) do
|
50
|
+
@app.call(env)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end || [500, {}, nil]
|
54
|
+
AppOpticsAPM::Context.clear unless incoming
|
55
|
+
response
|
56
|
+
rescue
|
57
|
+
AppOpticsAPM::Context.clear unless incoming
|
58
|
+
raise
|
59
|
+
# can't use ensure for Context.clearing, because the Grape middleware
|
60
|
+
# needs the context in case of an error, it is somewhat convoluted ...
|
41
61
|
end
|
42
62
|
|
43
63
|
def self.noop?
|
@@ -46,7 +66,8 @@ if AppOpticsAPM.loaded
|
|
46
66
|
|
47
67
|
private
|
48
68
|
|
49
|
-
def collect(
|
69
|
+
def collect(env, settings)
|
70
|
+
req = ::Rack::Request.new(env)
|
50
71
|
report_kvs = {}
|
51
72
|
|
52
73
|
begin
|
@@ -61,7 +82,10 @@ if AppOpticsAPM.loaded
|
|
61
82
|
report_kvs[:'Query-String'] = ::CGI.unescape(req.query_string) unless req.query_string.empty?
|
62
83
|
end
|
63
84
|
|
64
|
-
report_kvs[:
|
85
|
+
report_kvs[:URL] = AppOpticsAPM::Config[:rack][:log_args] ? ::CGI.unescape(req.fullpath) : ::CGI.unescape(req.path)
|
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
|
65
89
|
|
66
90
|
# Report any request queue'ing headers. Report as 'Request-Start' or the summed Queue-Time
|
67
91
|
report_kvs[:'Request-Start'] = env['HTTP_X_REQUEST_START'] if env.key?('HTTP_X_REQUEST_START')
|
@@ -83,96 +107,48 @@ if AppOpticsAPM.loaded
|
|
83
107
|
report_kvs
|
84
108
|
end
|
85
109
|
|
86
|
-
def
|
87
|
-
|
88
|
-
@app.call(env)
|
89
|
-
end
|
110
|
+
def propagate_xtrace(env, settings, xtrace)
|
111
|
+
return yield unless settings.do_propagate
|
90
112
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
report_kvs[:URL] = AppOpticsAPM::Config[:rack][:log_args] ? ::CGI.unescape(req.fullpath) : ::CGI.unescape(req.path)
|
96
|
-
|
97
|
-
AppOpticsAPM::API.trace(:rack, report_kvs) do
|
98
|
-
report_kvs = collect(req, env)
|
99
|
-
|
100
|
-
# We log an info event with the HTTP KVs found in AppOpticsAPM::Rack.collect
|
101
|
-
# This is done here so in the case of stacks that try/catch/abort
|
102
|
-
# (looking at you Grape) we're sure the KVs get reported now as
|
103
|
-
# this code may not be returned to later.
|
104
|
-
AppOpticsAPM::API.log_info(:rack, report_kvs)
|
105
|
-
@app.call(env)
|
113
|
+
if xtrace
|
114
|
+
xtrace_local = xtrace.dup
|
115
|
+
AppOpticsAPM::XTrace.unset_sampled(xtrace_local) unless settings.do_sample
|
116
|
+
env['HTTP_X_TRACE'] = xtrace_local
|
106
117
|
end
|
107
|
-
end
|
108
|
-
|
109
|
-
def metrics_sampling_call(env)
|
110
|
-
start = Time.now
|
111
|
-
AppOpticsAPM.transaction_name = nil
|
112
|
-
req = ::Rack::Request.new(env)
|
113
|
-
req_url = req.url # saving it here because rails3.2 overrides it when there is a 500 error
|
114
|
-
status = 500 # initialize with 500
|
115
|
-
|
116
|
-
report_kvs = collect(req, env)
|
117
|
-
report_kvs[:URL] = AppOpticsAPM::Config[:rack][:log_args] ? ::CGI.unescape(req.fullpath) : ::CGI.unescape(req.path)
|
118
118
|
|
119
|
-
|
120
|
-
xtrace = env.is_a?(Hash) ? env['HTTP_X_TRACE'] : nil
|
121
|
-
xtrace = AppOpticsAPM::XTrace.valid?(xtrace) ? xtrace : nil
|
119
|
+
status, headers, response = yield
|
122
120
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
# AppOpticsAPM.has_xtrace_header = xtrace
|
128
|
-
# AppOpticsAPM.is_continued_trace = AppOpticsAPM.has_incoming_context || AppOpticsAPM.has_xtrace_header
|
129
|
-
|
130
|
-
AppOpticsAPM::API.log_start(:rack, xtrace, report_kvs) unless AppOpticsAPM::Util.static_asset?(env['PATH_INFO'])
|
131
|
-
|
132
|
-
status, headers, response = @app.call(env)
|
133
|
-
confirmed_transaction_name = send_metrics(env, req, req_url, start, status)
|
134
|
-
xtrace = AppOpticsAPM::API.log_end(:rack, :Status => status, :TransactionName => confirmed_transaction_name)
|
135
|
-
|
136
|
-
# TODO revisit this JRUBY condition
|
137
|
-
# headers['X-Trace'] = xtrace if headers.is_a?(Hash) unless defined?(JRUBY_VERSION) && AppOpticsAPM.is_continued_trace?
|
138
|
-
headers['X-Trace'] = xtrace if headers.is_a?(Hash)
|
121
|
+
headers ||= {}
|
122
|
+
headers['X-Trace'] = AppOpticsAPM::Context.toString if AppOpticsAPM::Context.isValid
|
123
|
+
headers['X-Trace'] ||= xtrace if xtrace
|
124
|
+
headers['X-Trace'] && AppOpticsAPM::XTrace.unset_sampled(headers['X-Trace']) unless settings.do_sample
|
139
125
|
|
140
126
|
[status, headers, response]
|
141
|
-
rescue Exception => e
|
142
|
-
# it is ok to rescue Exception here because we are reraising it (we just need a chance to log_end)
|
143
|
-
AppOpticsAPM::API.log_exception(:rack, e)
|
144
|
-
confirmed_transaction_name ||= send_metrics(env, req, req_url, start, status)
|
145
|
-
xtrace = AppOpticsAPM::API.log_end(:rack, :Status => status, :TransactionName => confirmed_transaction_name)
|
146
|
-
|
147
|
-
# TODO revisit this JRUBY condition
|
148
|
-
# headers['X-Trace'] = xtrace if headers.is_a?(Hash) unless defined?(JRUBY_VERSION) && AppOpticsAPM.is_continued_trace?
|
149
|
-
headers['X-Trace'] = xtrace if headers.is_a?(Hash)
|
150
|
-
|
151
|
-
raise
|
152
|
-
end
|
153
|
-
|
154
|
-
|
155
|
-
def send_metrics(env, req, req_url, start, status)
|
156
|
-
return if AppOpticsAPM::Util.static_asset?(env['PATH_INFO'])
|
157
|
-
|
158
|
-
status = status.to_i
|
159
|
-
error = status.between?(500,599) ? 1 : 0
|
160
|
-
duration =(1000 * 1000 * (Time.now - start)).round(0)
|
161
|
-
method = req.request_method
|
162
|
-
AppOpticsAPM::Span.createHttpSpan(transaction_name(env), req_url, domain(req), duration, status, method, error) || ''
|
163
127
|
end
|
164
128
|
|
165
|
-
def
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
129
|
+
def sample(env, settings)
|
130
|
+
xtrace = env['HTTP_X_TRACE']
|
131
|
+
if settings.do_sample
|
132
|
+
begin
|
133
|
+
report_kvs = collect(env, settings)
|
134
|
+
|
135
|
+
AppOpticsAPM::API.log_start(:rack, xtrace, report_kvs, settings)
|
136
|
+
|
137
|
+
status, headers, response = yield
|
138
|
+
|
139
|
+
AppOpticsAPM::API.log_exit(:rack, { Status: status,
|
140
|
+
TransactionName: AppOpticsAPM.transaction_name })
|
141
|
+
[status, headers, response]
|
142
|
+
rescue Exception => e
|
143
|
+
# it is ok to rescue Exception here because we are reraising it (we just need a chance to log_end)
|
144
|
+
AppOpticsAPM::API.log_exception(:rack, e)
|
145
|
+
AppOpticsAPM::API.log_exit(:rack, { Status: status,
|
146
|
+
TransactionName: AppOpticsAPM.transaction_name })
|
147
|
+
raise
|
148
|
+
end
|
149
|
+
else
|
150
|
+
AppOpticsAPM::API.create_nontracing_context(xtrace)
|
151
|
+
yield
|
176
152
|
end
|
177
153
|
end
|
178
154
|
|
data/lib/appoptics_apm/logger.rb
CHANGED
@@ -8,35 +8,37 @@ module AppOpticsAPM
|
|
8
8
|
attr_accessor :logger
|
9
9
|
end
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
11
|
+
# TODO ME currently unused, keeping it around for xtrace logging epic
|
12
|
+
# class Logger
|
13
|
+
# # Fatal message
|
14
|
+
# def fatal(msg, exception = nil)
|
15
|
+
# AppOpticsAPM.logger.fatal(msg) if AppOpticsAPM.logger
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# # Error message
|
19
|
+
# def error(msg, exception = nil)
|
20
|
+
# AppOpticsAPM.logger.error(msg) if AppOpticsAPM.logger
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# # Warn message
|
24
|
+
# def warn(msg, exception = nil)
|
25
|
+
# AppOpticsAPM.logger.warn(msg) if AppOpticsAPM.logger
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# # Info message
|
29
|
+
# def info(msg, exception = nil)
|
30
|
+
# AppOpticsAPM.logger.info(msg) if AppOpticsAPM.logger
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# # Debug message
|
34
|
+
# def debug(msg, exception = nil)
|
35
|
+
# AppOpticsAPM.logger.debug(msg) if AppOpticsAPM.logger
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# end
|
38
39
|
end
|
39
40
|
|
41
|
+
# Using the currently defined Logger, e.g. the Rails logger
|
40
42
|
AppOpticsAPM.logger = Logger.new(STDERR)
|
41
43
|
# set log level to INFO to be consistent with the c-lib, DEBUG would be default
|
42
44
|
AppOpticsAPM.logger.level = Logger::INFO
|
@@ -187,6 +187,7 @@ module AppOpticsAPM
|
|
187
187
|
|
188
188
|
# AppOpticsAPM::Event.startTrace creates an Event without an Edge
|
189
189
|
exit_evt = AppOpticsAPM::Event.startTrace(AppOpticsAPM::Context.get)
|
190
|
+
|
190
191
|
result = begin
|
191
192
|
AppOpticsAPM::API.send_metrics(span, opts) do
|
192
193
|
target['X-Trace'] = AppOpticsAPM::EventUtil.metadataString(exit_evt)
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# Copyright (c) 2018 SolarWinds, LLC.
|
2
|
+
# All rights reserved.
|
3
|
+
|
4
|
+
module AppOpticsAPM
|
5
|
+
##
|
6
|
+
# This module sends the duration of the call and
|
7
|
+
# sets the transaction_name
|
8
|
+
#
|
9
|
+
module TransactionMetrics
|
10
|
+
class << self
|
11
|
+
|
12
|
+
##
|
13
|
+
# sends the duration of the call and
|
14
|
+
# sets the transaction_name
|
15
|
+
def metrics(env, settings)
|
16
|
+
if settings.do_metrics
|
17
|
+
req = ::Rack::Request.new(env)
|
18
|
+
url = req.url # saving it here because rails3.2 overrides it when there is a 500 error
|
19
|
+
start = Time.now
|
20
|
+
|
21
|
+
begin
|
22
|
+
status, headers, response = yield
|
23
|
+
|
24
|
+
AppOpticsAPM.transaction_name = send_metrics(env, req, url, start, status)
|
25
|
+
rescue
|
26
|
+
AppOpticsAPM.transaction_name = send_metrics(env, req, url, start, status || '500')
|
27
|
+
raise
|
28
|
+
end
|
29
|
+
else
|
30
|
+
status, headers, response = yield
|
31
|
+
AppOpticsAPM.transaction_name = "#{domain(req)}#{transaction_name(env)}" if settings.do_sample
|
32
|
+
end
|
33
|
+
|
34
|
+
[status, headers, response]
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def send_metrics(env, req, url, start, status)
|
40
|
+
name = transaction_name(env)
|
41
|
+
|
42
|
+
status = status.to_i
|
43
|
+
error = status.between?(500,599) ? 1 : 0
|
44
|
+
duration =(1000 * 1000 * (Time.now - start)).round(0)
|
45
|
+
method = req.request_method
|
46
|
+
# AppOpticsAPM.logger.warn "%%% Sending metrics: #{name}, #{url}, #{status} %%%"
|
47
|
+
AppOpticsAPM::Span.createHttpSpan(name, url, domain(req), duration, status, method, error) || ''
|
48
|
+
end
|
49
|
+
|
50
|
+
def domain(req)
|
51
|
+
if AppOpticsAPM::Config['transaction_name']['prepend_domain']
|
52
|
+
[80, 443].include?(req.port) ? req.host : "#{req.host}:#{req.port}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def transaction_name(env)
|
57
|
+
return AppOpticsAPM.transaction_name if AppOpticsAPM.transaction_name
|
58
|
+
|
59
|
+
if env['appoptics_apm.controller'] && env['appoptics_apm.action']
|
60
|
+
[env['appoptics_apm.controller'], env['appoptics_apm.action']].join('.')
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
# Copyright (c) 2018 SolarWinds, LLC.
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
|
5
|
+
AO_TRACING_ENABLED = 1
|
6
|
+
AO_TRACING_DISABLED = 0
|
7
|
+
AO_TRACING_UNSET = -1
|
8
|
+
|
9
|
+
AO_TRACING_DECISIONS_TRACING_DISABLED = -2
|
10
|
+
AO_TRACING_DECISIONS_XTRACE_NOT_SAMPLED = -1
|
11
|
+
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
|
17
|
+
|
18
|
+
module AppOpticsAPM
|
19
|
+
##
|
20
|
+
# This module helps with setting up the filters and applying them
|
21
|
+
#
|
22
|
+
class TransactionSettings
|
23
|
+
|
24
|
+
attr_accessor :do_metrics, :do_sample
|
25
|
+
attr_reader :do_propagate, :rate, :source
|
26
|
+
|
27
|
+
def initialize(url = nil, xtrace = '')
|
28
|
+
@do_metrics = false
|
29
|
+
@do_sample = false
|
30
|
+
@do_propagate = true
|
31
|
+
tracing_mode = AO_TRACING_ENABLED
|
32
|
+
|
33
|
+
if AppOpticsAPM::Context.isValid
|
34
|
+
@do_sample = AppOpticsAPM.tracing?
|
35
|
+
return
|
36
|
+
end
|
37
|
+
|
38
|
+
if url && asset?(url)
|
39
|
+
@do_propagate = false
|
40
|
+
return
|
41
|
+
end
|
42
|
+
|
43
|
+
if AppOpticsAPM.tracing_disabled? && !tracing_enabled?(url) ||
|
44
|
+
tracing_disabled?(url)
|
45
|
+
tracing_mode = AO_TRACING_DISABLED
|
46
|
+
end
|
47
|
+
|
48
|
+
args = [xtrace || '']
|
49
|
+
args << tracing_mode
|
50
|
+
args << AppOpticsAPM::Config[:sample_rate] if AppOpticsAPM::Config[:sample_rate]&. >= 0
|
51
|
+
|
52
|
+
metrics, sample, @rate, @source, return_code = AppOpticsAPM::Context.getDecisions(*args)
|
53
|
+
|
54
|
+
puts "return_code class: #{return_code.class}" unless return_code.is_a? Integer
|
55
|
+
if return_code > AO_TRACING_DECISIONS_OK
|
56
|
+
AppOpticsAPM.logger.warn "[appoptics-apm/sample] Problem getting the sampling decisions, code: #{return_code}"
|
57
|
+
end
|
58
|
+
|
59
|
+
@do_metrics = metrics > 0
|
60
|
+
@do_sample = sample > 0
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_s
|
64
|
+
"do_propagate: #{do_propagate}, do_sample: #{do_sample}, do_metrics: #{do_metrics} rate: #{rate}, source: #{source}"
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
##
|
69
|
+
# tracing_enabled?
|
70
|
+
#
|
71
|
+
# Given a path, this method determines whether it matches any of the
|
72
|
+
# regexps to exclude it from metrics and traces
|
73
|
+
#
|
74
|
+
def tracing_enabled?(url)
|
75
|
+
return false unless AppOpticsAPM::Config[:url_enabled_regexps].is_a? Array
|
76
|
+
# once we only support Ruby >= 2.4.0 use `match?` instead of `=~`
|
77
|
+
return AppOpticsAPM::Config[:url_enabled_regexps].any? { |regex| regex =~ url }
|
78
|
+
rescue => e
|
79
|
+
AppOpticsAPM.logger.warn "[AppOpticsAPM/filter] Could not apply :enabled filter to path. #{e.inspect}"
|
80
|
+
true
|
81
|
+
end
|
82
|
+
|
83
|
+
##
|
84
|
+
# tracing_disabled?
|
85
|
+
#
|
86
|
+
# Given a path, this method determines whether it matches any of the
|
87
|
+
# regexps to exclude it from metrics and traces
|
88
|
+
#
|
89
|
+
def tracing_disabled?(url)
|
90
|
+
return false unless AppOpticsAPM::Config[:url_disabled_regexps].is_a? Array
|
91
|
+
# once we only support Ruby >= 2.4.0 use `match?` instead of `=~`
|
92
|
+
return AppOpticsAPM::Config[:url_disabled_regexps].any? { |regex| regex =~ url }
|
93
|
+
rescue => e
|
94
|
+
AppOpticsAPM.logger.warn "[AppOpticsAPM/filter] Could not apply :disabled filter to path. #{e.inspect}"
|
95
|
+
false
|
96
|
+
end
|
97
|
+
|
98
|
+
##
|
99
|
+
# asset?
|
100
|
+
#
|
101
|
+
# Given a path, this method determines whether it is a static asset
|
102
|
+
#
|
103
|
+
def asset?(path)
|
104
|
+
return false unless AppOpticsAPM::Config[:dnt_compiled]
|
105
|
+
# once we only support Ruby >= 2.4.0 use `match?` instead of `=~`
|
106
|
+
return AppOpticsAPM::Config[:dnt_compiled] =~ path
|
107
|
+
rescue => e
|
108
|
+
AppOpticsAPM.logger.warn "[AppOpticsAPM/filter] Could not apply do-not-trace filter to path. #{e.inspect}"
|
109
|
+
false
|
110
|
+
end
|
111
|
+
|
112
|
+
public
|
113
|
+
|
114
|
+
class << self
|
115
|
+
|
116
|
+
def asset?(path)
|
117
|
+
return false unless AppOpticsAPM::Config[:dnt_compiled]
|
118
|
+
# once we only support Ruby >= 2.4.0 use `match?` instead of `=~`
|
119
|
+
return AppOpticsAPM::Config[:dnt_compiled] =~ path
|
120
|
+
rescue => e
|
121
|
+
AppOpticsAPM.logger.warn "[AppOpticsAPM/filter] Could not apply do-not-trace filter to path. #{e.inspect}"
|
122
|
+
false
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
def compile_url_settings(settings)
|
127
|
+
if !settings.is_a?(Array) || settings.empty?
|
128
|
+
reset_url_regexps
|
129
|
+
return
|
130
|
+
end
|
131
|
+
|
132
|
+
# `tracing: disabled` is the default
|
133
|
+
disabled = settings.select { |v| !v.has_key?(:tracing) || v[:tracing] == :disabled }
|
134
|
+
enabled = settings.select { |v| v[:tracing] == :enabled }
|
135
|
+
|
136
|
+
AppOpticsAPM::Config[:url_enabled_regexps] = compile_regexp(enabled)
|
137
|
+
AppOpticsAPM::Config[:url_disabled_regexps] = compile_regexp(disabled)
|
138
|
+
end
|
139
|
+
|
140
|
+
def compile_regexp(settings)
|
141
|
+
regexp_regexp = compile_url_settings_regexp(settings)
|
142
|
+
extensions_regexp = compile_url_settings_extensions(settings)
|
143
|
+
|
144
|
+
regexps = [regexp_regexp, extensions_regexp].flatten.compact
|
145
|
+
|
146
|
+
regexps.empty? ? nil : regexps
|
147
|
+
end
|
148
|
+
|
149
|
+
def compile_url_settings_regexp(value)
|
150
|
+
regexps = value.select do |v|
|
151
|
+
v.key?(:regexp) &&
|
152
|
+
!(v[:regexp].is_a?(String) && v[:regexp].empty?) &&
|
153
|
+
!(v[:regexp].is_a?(Regexp) && v[:regexp].inspect == '//')
|
154
|
+
end
|
155
|
+
|
156
|
+
regexps.map! do |v|
|
157
|
+
begin
|
158
|
+
v[:regexp].is_a?(String) ? Regexp.new(v[:regexp], v[:opts]) : Regexp.new(v[:regexp])
|
159
|
+
rescue
|
160
|
+
AppOpticsAPM.logger.warn "[appoptics_apm/config] Problem compiling transaction_settings item #{v}, will ignore."
|
161
|
+
nil
|
162
|
+
end
|
163
|
+
end
|
164
|
+
regexps.keep_if { |v| !v.nil?}
|
165
|
+
regexps.empty? ? nil : regexps
|
166
|
+
end
|
167
|
+
|
168
|
+
def compile_url_settings_extensions(value)
|
169
|
+
extensions = value.select do |v|
|
170
|
+
v.key?(:extensions) &&
|
171
|
+
v[:extensions].is_a?(Array) &&
|
172
|
+
!v[:extensions].empty?
|
173
|
+
end
|
174
|
+
extensions = extensions.map { |v| v[:extensions] }.flatten
|
175
|
+
extensions.keep_if { |v| v.is_a?(String)}
|
176
|
+
|
177
|
+
extensions.empty? ? nil : Regexp.new("(#{Regexp.union(extensions).source})(\\?.+){0,1}$")
|
178
|
+
end
|
179
|
+
|
180
|
+
def reset_url_regexps
|
181
|
+
AppOpticsAPM::Config[:url_enabled_regexps] = nil
|
182
|
+
AppOpticsAPM::Config[:url_disabled_regexps] = nil
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|