appoptics_apm 4.0.2 → 4.0.3
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/.travis.yml +1 -0
- data/Dockerfile_test +7 -6
- data/Rakefile +2 -0
- data/docker-compose.yml +22 -0
- data/gemfiles/noop.gemfile +17 -0
- data/lib/appoptics_apm.rb +2 -2
- data/lib/appoptics_apm/api.rb +1 -2
- data/lib/appoptics_apm/api/layerinit.rb +3 -1
- data/lib/appoptics_apm/api/logging.rb +25 -25
- data/lib/appoptics_apm/api/memcache.rb +3 -1
- data/lib/appoptics_apm/api/profiling.rb +39 -33
- data/lib/appoptics_apm/api/tracing.rb +23 -13
- data/lib/appoptics_apm/api/util.rb +1 -1
- data/lib/appoptics_apm/base.rb +1 -0
- data/lib/appoptics_apm/frameworks/sinatra/templates.rb +1 -1
- data/lib/appoptics_apm/inst/rack.rb +135 -111
- data/lib/appoptics_apm/instrumentation.rb +1 -1
- data/lib/appoptics_apm/version.rb +1 -1
- data/test/instrumentation/httpclient_test.rb +4 -20
- data/test/instrumentation/mongo_v1_test.rb +1 -1
- data/test/instrumentation/rack_test.rb +1 -1
- data/test/instrumentation/sequel_mysql2_test.rb +1 -1
- data/test/instrumentation/sequel_mysql_test.rb +1 -1
- data/test/instrumentation/sequel_pg_test.rb +1 -1
- data/test/instrumentation/twitter-cassandra_test.rb +1 -1
- data/test/minitest_helper.rb +1 -0
- data/test/mocked/curb_mocked_test.rb +14 -1
- data/test/mocked/excon_mocked_test.rb +12 -4
- data/test/mocked/faraday_mocked_test.rb +5 -1
- data/test/mocked/http_mocked_test.rb +6 -0
- data/test/mocked/httpclient_mocked_test.rb +14 -1
- data/test/mocked/rest_client_mocked_test.rb +7 -1
- data/test/mocked/typhoeus_mocked_test.rb +15 -0
- data/test/noop/noop_test.rb +145 -0
- metadata +5 -4
- data/test/support/noop_test.rb +0 -88
data/lib/appoptics_apm/base.rb
CHANGED
@@ -61,6 +61,7 @@ module AppOpticsAPMBase
|
|
61
61
|
|
62
62
|
# Occurs only on Jruby. Indicates that Joboe (the java instrumentation)
|
63
63
|
# has already started tracing before it hit the JRuby instrumentation.
|
64
|
+
# It is used in Rack#call if there is a context when entering rack
|
64
65
|
thread_local :has_incoming_context
|
65
66
|
|
66
67
|
# Indicates the existence of a valid X-Trace request header
|
@@ -4,142 +4,166 @@
|
|
4
4
|
require 'uri'
|
5
5
|
require 'cgi'
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
7
|
+
if AppOpticsAPM.loaded
|
8
|
+
module AppOpticsAPM
|
9
|
+
##
|
10
|
+
# AppOpticsAPM::Rack
|
11
|
+
#
|
12
|
+
# The AppOpticsAPM::Rack middleware used to sample a subset of incoming
|
13
|
+
# requests for instrumentation and reporting. Tracing context can
|
14
|
+
# be received here (via the X-Trace HTTP header) or initiated here
|
15
|
+
# based on configured tracing mode.
|
16
|
+
#
|
17
|
+
# After the rack layer passes on to the following layers (Rails, Sinatra,
|
18
|
+
# Padrino, Grape), then the instrumentation downstream will
|
19
|
+
# automatically detect whether this is a sampled request or not
|
20
|
+
# and act accordingly. (to instrument or not)
|
21
|
+
#
|
22
|
+
class Rack
|
23
|
+
attr_reader :app
|
24
|
+
|
25
|
+
def initialize(app)
|
26
|
+
@app = app
|
27
|
+
end
|
28
|
+
|
29
|
+
def collect(req, env)
|
30
|
+
report_kvs = {}
|
27
31
|
|
28
|
-
|
29
|
-
|
32
|
+
begin
|
33
|
+
report_kvs[:'HTTP-Host'] = req.host
|
34
|
+
report_kvs[:Port] = req.port
|
35
|
+
report_kvs[:Proto] = req.scheme
|
36
|
+
report_kvs[:Method] = req.request_method
|
37
|
+
report_kvs[:AJAX] = true if req.xhr?
|
38
|
+
report_kvs[:ClientIP] = req.ip
|
30
39
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
report_kvs[:Proto] = req.scheme
|
35
|
-
report_kvs[:Method] = req.request_method
|
36
|
-
report_kvs[:AJAX] = true if req.xhr?
|
37
|
-
report_kvs[:ClientIP] = req.ip
|
40
|
+
if AppOpticsAPM::Config[:rack][:log_args]
|
41
|
+
report_kvs[:'Query-String'] = ::CGI.unescape(req.query_string) unless req.query_string.empty?
|
42
|
+
end
|
38
43
|
|
39
|
-
|
40
|
-
report_kvs[:'
|
44
|
+
# Report any request queue'ing headers. Report as 'Request-Start' or the summed Queue-Time
|
45
|
+
report_kvs[:'Request-Start'] = env['HTTP_X_REQUEST_START'] if env.key?('HTTP_X_REQUEST_START')
|
46
|
+
report_kvs[:'Request-Start'] = env['HTTP_X_QUEUE_START'] if env.key?('HTTP_X_QUEUE_START')
|
47
|
+
report_kvs[:'Queue-Time'] = env['HTTP_X_QUEUE_TIME'] if env.key?('HTTP_X_QUEUE_TIME')
|
48
|
+
|
49
|
+
report_kvs[:'Forwarded-For'] = env['HTTP_X_FORWARDED_FOR'] if env.key?('HTTP_X_FORWARDED_FOR')
|
50
|
+
report_kvs[:'Forwarded-Host'] = env['HTTP_X_FORWARDED_HOST'] if env.key?('HTTP_X_FORWARDED_HOST')
|
51
|
+
report_kvs[:'Forwarded-Proto'] = env['HTTP_X_FORWARDED_PROTO'] if env.key?('HTTP_X_FORWARDED_PROTO')
|
52
|
+
report_kvs[:'Forwarded-Port'] = env['HTTP_X_FORWARDED_PORT'] if env.key?('HTTP_X_FORWARDED_PORT')
|
53
|
+
|
54
|
+
report_kvs[:'Ruby.AppOpticsAPM.Version'] = ::AppOpticsAPM::Version::STRING
|
55
|
+
report_kvs[:ProcessID] = Process.pid
|
56
|
+
report_kvs[:ThreadID] = Thread.current.to_s[/0x\w*/]
|
57
|
+
rescue StandardError => e
|
58
|
+
# Discard any potential exceptions. Debug log and report whatever we can.
|
59
|
+
AppOpticsAPM.logger.debug "[appoptics_apm/debug] Rack KV collection error: #{e.inspect}"
|
41
60
|
end
|
42
|
-
|
43
|
-
# Report any request queue'ing headers. Report as 'Request-Start' or the summed Queue-Time
|
44
|
-
report_kvs[:'Request-Start'] = env['HTTP_X_REQUEST_START'] if env.key?('HTTP_X_REQUEST_START')
|
45
|
-
report_kvs[:'Request-Start'] = env['HTTP_X_QUEUE_START'] if env.key?('HTTP_X_QUEUE_START')
|
46
|
-
report_kvs[:'Queue-Time'] = env['HTTP_X_QUEUE_TIME'] if env.key?('HTTP_X_QUEUE_TIME')
|
47
|
-
|
48
|
-
report_kvs[:'Forwarded-For'] = env['HTTP_X_FORWARDED_FOR'] if env.key?('HTTP_X_FORWARDED_FOR')
|
49
|
-
report_kvs[:'Forwarded-Host'] = env['HTTP_X_FORWARDED_HOST'] if env.key?('HTTP_X_FORWARDED_HOST')
|
50
|
-
report_kvs[:'Forwarded-Proto'] = env['HTTP_X_FORWARDED_PROTO'] if env.key?('HTTP_X_FORWARDED_PROTO')
|
51
|
-
report_kvs[:'Forwarded-Port'] = env['HTTP_X_FORWARDED_PORT'] if env.key?('HTTP_X_FORWARDED_PORT')
|
52
|
-
|
53
|
-
report_kvs[:'Ruby.AppOpticsAPM.Version'] = ::AppOpticsAPM::Version::STRING
|
54
|
-
report_kvs[:ProcessID] = Process.pid
|
55
|
-
report_kvs[:ThreadID] = Thread.current.to_s[/0x\w*/]
|
56
|
-
rescue StandardError => e
|
57
|
-
# Discard any potential exceptions. Debug log and report whatever we can.
|
58
|
-
AppOpticsAPM.logger.debug "[appoptics_apm/debug] Rack KV collection error: #{e.inspect}"
|
61
|
+
report_kvs
|
59
62
|
end
|
60
|
-
report_kvs
|
61
|
-
end
|
62
63
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
64
|
+
def call(env)
|
65
|
+
start = Time.now
|
66
|
+
status = 500
|
67
|
+
req = ::Rack::Request.new(env)
|
68
|
+
req_url = req.url # saving it here because rails3.2 overrides it when there is a 500 error
|
69
|
+
|
70
|
+
# In the case of nested Ruby apps such as Grape inside of Rails
|
71
|
+
# or Grape inside of Grape, each app has it's own instance
|
72
|
+
# of rack middleware. We avoid tracing rack more than once and
|
73
|
+
# instead start instrumenting from the first rack pass.
|
74
|
+
if AppOpticsAPM.tracing? && AppOpticsAPM.layer == :rack
|
75
|
+
AppOpticsAPM.logger.debug "[appoptics_apm/rack] Rack skipped!"
|
76
|
+
return @app.call(env)
|
77
|
+
end
|
77
78
|
|
78
|
-
|
79
|
-
|
79
|
+
begin
|
80
|
+
report_kvs = {}
|
80
81
|
|
81
|
-
|
82
|
+
report_kvs[:URL] = AppOpticsAPM::Config[:rack][:log_args] ? ::CGI.unescape(req.fullpath) : ::CGI.unescape(req.path)
|
82
83
|
|
83
|
-
|
84
|
-
|
85
|
-
|
84
|
+
# Check for and validate X-Trace request header to pick up tracing context
|
85
|
+
xtrace = env.is_a?(Hash) ? env['HTTP_X_TRACE'] : nil
|
86
|
+
xtrace_header = AppOpticsAPM::XTrace.valid?(xtrace) ? xtrace : nil
|
86
87
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
88
|
+
# Under JRuby, JAppOpticsAPM may have already started a trace. Make note of this
|
89
|
+
# if so and don't clear context on log_end (see appoptics_apm/api/logging.rb)
|
90
|
+
AppOpticsAPM.has_incoming_context = AppOpticsAPM.tracing?
|
91
|
+
AppOpticsAPM.has_xtrace_header = xtrace_header
|
92
|
+
AppOpticsAPM.is_continued_trace = AppOpticsAPM.has_incoming_context || AppOpticsAPM.has_xtrace_header
|
92
93
|
|
93
|
-
|
94
|
+
xtrace = AppOpticsAPM::API.log_start(:rack, xtrace_header, report_kvs)
|
94
95
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
96
|
+
# We only trace a subset of requests based off of sample rate so if
|
97
|
+
# AppOpticsAPM::API.log_start really did start a trace, we act accordingly here.
|
98
|
+
if AppOpticsAPM.tracing?
|
99
|
+
report_kvs = collect(req, env)
|
99
100
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
101
|
+
# We log an info event with the HTTP KVs found in AppOpticsAPM::Rack.collect
|
102
|
+
# This is done here so in the case of stacks that try/catch/abort
|
103
|
+
# (looking at you Grape) we're sure the KVs get reported now as
|
104
|
+
# this code may not be returned to later.
|
105
|
+
AppOpticsAPM::API.log_info(:rack, report_kvs)
|
105
106
|
|
106
|
-
|
107
|
+
status, headers, response = @app.call(env)
|
107
108
|
|
108
|
-
|
109
|
+
xtrace = AppOpticsAPM::API.log_end(:rack, :Status => status)
|
109
110
|
|
110
|
-
|
111
|
-
|
112
|
-
|
111
|
+
else
|
112
|
+
status, headers, response = @app.call(env)
|
113
|
+
end
|
113
114
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
115
|
+
[status, headers, response]
|
116
|
+
rescue Exception => e
|
117
|
+
# it is ok to rescue Exception here because we are reraising it (we just need a chance to log_end)
|
118
|
+
if AppOpticsAPM.tracing?
|
119
|
+
AppOpticsAPM::API.log_exception(:rack, e)
|
120
|
+
xtrace = AppOpticsAPM::API.log_end(:rack, :Status => 500, 'TransactionName' => transaction_name(env))
|
121
|
+
end
|
122
|
+
raise
|
123
|
+
ensure
|
124
|
+
if headers && AppOpticsAPM::XTrace.valid?(xtrace)
|
125
|
+
unless defined?(JRUBY_VERSION) && AppOpticsAPM.is_continued_trace?
|
126
|
+
headers['X-Trace'] = xtrace if headers.is_a?(Hash)
|
127
|
+
end
|
128
|
+
end
|
120
129
|
end
|
121
|
-
raise
|
122
130
|
ensure
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
131
|
+
unless ::AppOpticsAPM::Util.static_asset?(env['PATH_INFO'])
|
132
|
+
status = status.to_i
|
133
|
+
error = status.between?(500,599) ? 1 : 0
|
134
|
+
duration =(1000 * 1000 * (Time.now - start)).round(0)
|
135
|
+
AppOpticsAPM::Span.createHttpSpan(transaction_name(env), req_url, duration, status, req.request_method, error)
|
127
136
|
end
|
128
137
|
end
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
138
|
+
|
139
|
+
def self.noop?
|
140
|
+
false
|
141
|
+
end
|
142
|
+
|
143
|
+
private
|
144
|
+
|
145
|
+
def transaction_name(env)
|
146
|
+
if env['appoptics_apm.controller'] || env['appoptics_apm.action']
|
147
|
+
[env['appoptics_apm.controller'], env['appoptics_apm.action']].join('.')
|
148
|
+
end
|
135
149
|
end
|
136
150
|
end
|
151
|
+
end
|
152
|
+
else
|
153
|
+
module AppOpticsAPM
|
154
|
+
class Rack
|
155
|
+
attr_reader :app
|
137
156
|
|
138
|
-
|
157
|
+
def initialize(app)
|
158
|
+
@app = app
|
159
|
+
end
|
160
|
+
|
161
|
+
def call(env)
|
162
|
+
@app.call(env)
|
163
|
+
end
|
139
164
|
|
140
|
-
|
141
|
-
|
142
|
-
[env['appoptics_apm.controller'], env['appoptics_apm.action']].join('.')
|
165
|
+
def self.noop?
|
166
|
+
true
|
143
167
|
end
|
144
168
|
end
|
145
169
|
end
|
@@ -4,7 +4,7 @@
|
|
4
4
|
module AppOpticsAPM
|
5
5
|
##
|
6
6
|
# The Inst module holds all of the instrumentation extensions for various
|
7
|
-
# libraries
|
7
|
+
# libraries such as Redis, Dalli and Resque.
|
8
8
|
module Inst
|
9
9
|
def self.load_instrumentation
|
10
10
|
# Load the general instrumentation
|
@@ -18,6 +18,10 @@ unless defined?(JRUBY_VERSION)
|
|
18
18
|
AppOpticsAPM::Config[:tracing_mode] = :always
|
19
19
|
end
|
20
20
|
|
21
|
+
def teardown
|
22
|
+
clear_all_traces
|
23
|
+
end
|
24
|
+
|
21
25
|
def test_reports_version_init
|
22
26
|
init_kvs = ::AppOpticsAPM::Util.build_init_report
|
23
27
|
assert init_kvs.key?('Ruby.httpclient.Version')
|
@@ -25,8 +29,6 @@ unless defined?(JRUBY_VERSION)
|
|
25
29
|
end
|
26
30
|
|
27
31
|
def test_get_request
|
28
|
-
clear_all_traces
|
29
|
-
|
30
32
|
response = nil
|
31
33
|
|
32
34
|
AppOpticsAPM::API.start_trace('httpclient_tests') do
|
@@ -55,8 +57,6 @@ unless defined?(JRUBY_VERSION)
|
|
55
57
|
end
|
56
58
|
|
57
59
|
def test_get_with_header_hash
|
58
|
-
clear_all_traces
|
59
|
-
|
60
60
|
response = nil
|
61
61
|
|
62
62
|
AppOpticsAPM::API.start_trace('httpclient_tests') do
|
@@ -85,8 +85,6 @@ unless defined?(JRUBY_VERSION)
|
|
85
85
|
end
|
86
86
|
|
87
87
|
def test_get_with_header_array
|
88
|
-
clear_all_traces
|
89
|
-
|
90
88
|
response = nil
|
91
89
|
|
92
90
|
AppOpticsAPM::API.start_trace('httpclient_tests') do
|
@@ -115,8 +113,6 @@ unless defined?(JRUBY_VERSION)
|
|
115
113
|
end
|
116
114
|
|
117
115
|
def test_post_request
|
118
|
-
clear_all_traces
|
119
|
-
|
120
116
|
response = nil
|
121
117
|
|
122
118
|
AppOpticsAPM::API.start_trace('httpclient_tests') do
|
@@ -145,8 +141,6 @@ unless defined?(JRUBY_VERSION)
|
|
145
141
|
end
|
146
142
|
|
147
143
|
def test_async_get
|
148
|
-
clear_all_traces
|
149
|
-
|
150
144
|
conn = nil
|
151
145
|
|
152
146
|
AppOpticsAPM::API.start_trace('httpclient_tests') do
|
@@ -180,8 +174,6 @@ unless defined?(JRUBY_VERSION)
|
|
180
174
|
end
|
181
175
|
|
182
176
|
def test_cross_app_tracing
|
183
|
-
clear_all_traces
|
184
|
-
|
185
177
|
response = nil
|
186
178
|
|
187
179
|
AppOpticsAPM::API.start_trace('httpclient_tests') do
|
@@ -217,8 +209,6 @@ unless defined?(JRUBY_VERSION)
|
|
217
209
|
end
|
218
210
|
|
219
211
|
def test_requests_with_errors
|
220
|
-
clear_all_traces
|
221
|
-
|
222
212
|
result = nil
|
223
213
|
begin
|
224
214
|
AppOpticsAPM::API.start_trace('httpclient_tests') do
|
@@ -249,8 +239,6 @@ unless defined?(JRUBY_VERSION)
|
|
249
239
|
end
|
250
240
|
|
251
241
|
def test_log_args_when_true
|
252
|
-
clear_all_traces
|
253
|
-
|
254
242
|
@log_args = AppOpticsAPM::Config[:httpclient][:log_args]
|
255
243
|
AppOpticsAPM::Config[:httpclient][:log_args] = true
|
256
244
|
|
@@ -276,8 +264,6 @@ unless defined?(JRUBY_VERSION)
|
|
276
264
|
end
|
277
265
|
|
278
266
|
def test_log_args_when_false
|
279
|
-
clear_all_traces
|
280
|
-
|
281
267
|
@log_args = AppOpticsAPM::Config[:httpclient][:log_args]
|
282
268
|
AppOpticsAPM::Config[:httpclient][:log_args] = false
|
283
269
|
|
@@ -303,8 +289,6 @@ unless defined?(JRUBY_VERSION)
|
|
303
289
|
end
|
304
290
|
|
305
291
|
def test_without_tracing
|
306
|
-
clear_all_traces
|
307
|
-
|
308
292
|
clnt = HTTPClient.new
|
309
293
|
clnt.get('http://127.0.0.1:8101/', :query => { :keyword => 'ruby', :lang => 'en' })
|
310
294
|
|
@@ -344,7 +344,7 @@ if Gem.loaded_specs['mongo'].version.to_s < '2.0.0'
|
|
344
344
|
validate_event_keys(traces[1], @entry_kvs)
|
345
345
|
validate_event_keys(traces[2], @exit_kvs)
|
346
346
|
|
347
|
-
result.
|
347
|
+
result.must_be_nil
|
348
348
|
traces[1]['Collection'].must_equal "testCollection"
|
349
349
|
traces[1].has_key?('Backtrace').must_equal AppOpticsAPM::Config[:mongo][:collect_backtraces]
|
350
350
|
traces[1]['QueryOp'].must_equal "find"
|
@@ -139,7 +139,7 @@ class RackTestApp < Minitest::Test
|
|
139
139
|
|
140
140
|
get "/no/controller/here"
|
141
141
|
|
142
|
-
|
142
|
+
assert_nil test_action
|
143
143
|
assert_equal "http://example.org/no/controller/here", test_url
|
144
144
|
assert_equal 404, test_status
|
145
145
|
assert_equal "GET", test_method
|
@@ -344,7 +344,7 @@ if defined?(::Sequel) && !defined?(JRUBY_VERSION)
|
|
344
344
|
traces[1]['Query'].must_equal "select_by_name"
|
345
345
|
end
|
346
346
|
|
347
|
-
traces[1]['QueryArgs'].
|
347
|
+
traces[1]['QueryArgs'].must_be_nil
|
348
348
|
traces[1]['IsPreparedStatement'].must_equal "true"
|
349
349
|
traces[1].has_key?('Backtrace').must_equal AppOpticsAPM::Config[:sequel][:collect_backtraces]
|
350
350
|
validate_event_keys(traces[2], @exit_kvs)
|