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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -0
  3. data/Dockerfile_test +7 -6
  4. data/Rakefile +2 -0
  5. data/docker-compose.yml +22 -0
  6. data/gemfiles/noop.gemfile +17 -0
  7. data/lib/appoptics_apm.rb +2 -2
  8. data/lib/appoptics_apm/api.rb +1 -2
  9. data/lib/appoptics_apm/api/layerinit.rb +3 -1
  10. data/lib/appoptics_apm/api/logging.rb +25 -25
  11. data/lib/appoptics_apm/api/memcache.rb +3 -1
  12. data/lib/appoptics_apm/api/profiling.rb +39 -33
  13. data/lib/appoptics_apm/api/tracing.rb +23 -13
  14. data/lib/appoptics_apm/api/util.rb +1 -1
  15. data/lib/appoptics_apm/base.rb +1 -0
  16. data/lib/appoptics_apm/frameworks/sinatra/templates.rb +1 -1
  17. data/lib/appoptics_apm/inst/rack.rb +135 -111
  18. data/lib/appoptics_apm/instrumentation.rb +1 -1
  19. data/lib/appoptics_apm/version.rb +1 -1
  20. data/test/instrumentation/httpclient_test.rb +4 -20
  21. data/test/instrumentation/mongo_v1_test.rb +1 -1
  22. data/test/instrumentation/rack_test.rb +1 -1
  23. data/test/instrumentation/sequel_mysql2_test.rb +1 -1
  24. data/test/instrumentation/sequel_mysql_test.rb +1 -1
  25. data/test/instrumentation/sequel_pg_test.rb +1 -1
  26. data/test/instrumentation/twitter-cassandra_test.rb +1 -1
  27. data/test/minitest_helper.rb +1 -0
  28. data/test/mocked/curb_mocked_test.rb +14 -1
  29. data/test/mocked/excon_mocked_test.rb +12 -4
  30. data/test/mocked/faraday_mocked_test.rb +5 -1
  31. data/test/mocked/http_mocked_test.rb +6 -0
  32. data/test/mocked/httpclient_mocked_test.rb +14 -1
  33. data/test/mocked/rest_client_mocked_test.rb +7 -1
  34. data/test/mocked/typhoeus_mocked_test.rb +15 -0
  35. data/test/noop/noop_test.rb +145 -0
  36. metadata +5 -4
  37. data/test/support/noop_test.rb +0 -88
@@ -9,7 +9,7 @@ module AppOpticsAPM
9
9
  module API
10
10
  ##
11
11
  # General utility methods for the gem
12
- module Util
12
+ module Util #:nodoc:
13
13
  BACKTRACE_CUTOFF = 200
14
14
 
15
15
  # Internal: Check whether the provided key is reserved or not. Reserved
@@ -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
@@ -44,7 +44,7 @@ module AppOpticsAPM
44
44
  begin
45
45
  render_without_appoptics(engine, data, options, locals, &block)
46
46
  ensure
47
- ::AppOpticsAPM::API.log_exit(:render, report_kvs)
47
+ ::AppOpticsAPM::API.log_exit(:render, report_kvs, :render)
48
48
  end
49
49
  end
50
50
  else
@@ -4,142 +4,166 @@
4
4
  require 'uri'
5
5
  require 'cgi'
6
6
 
7
- module AppOpticsAPM
8
- ##
9
- # AppOpticsAPM::Rack
10
- #
11
- # The AppOpticsAPM::Rack middleware used to sample a subset of incoming
12
- # requests for instrumentation and reporting. Tracing context can
13
- # be received here (via the X-Trace HTTP header) or initiated here
14
- # based on configured tracing mode.
15
- #
16
- # After the rack layer passes on to the following layers (Rails, Sinatra,
17
- # Padrino, Grape), then the instrumentation downstream will
18
- # automatically detect whether this is a sampled request or not
19
- # and act accordingly. (to instrument or not)
20
- #
21
- class Rack
22
- attr_reader :app
23
-
24
- def initialize(app)
25
- @app = app
26
- end
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
- def collect(req, env)
29
- report_kvs = {}
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
- begin
32
- report_kvs[:'HTTP-Host'] = req.host
33
- report_kvs[:Port] = req.port
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
- if AppOpticsAPM::Config[:rack][:log_args]
40
- report_kvs[:'Query-String'] = ::CGI.unescape(req.query_string) unless req.query_string.empty?
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
- def call(env)
64
- start = Time.now
65
- status = 500
66
- req = ::Rack::Request.new(env)
67
- req_url = req.url # saving it here because rails3.2 overrides it when there is a 500 error
68
-
69
- # In the case of nested Ruby apps such as Grape inside of Rails
70
- # or Grape inside of Grape, each app has it's own instance
71
- # of rack middleware. We avoid tracing rack more than once and
72
- # instead start instrumenting from the first rack pass.
73
- if AppOpticsAPM.tracing? && AppOpticsAPM.layer == :rack
74
- AppOpticsAPM.logger.debug "[appoptics_apm/rack] Rack skipped!"
75
- return @app.call(env)
76
- end
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
- begin
79
- report_kvs = {}
79
+ begin
80
+ report_kvs = {}
80
81
 
81
- report_kvs[:URL] = AppOpticsAPM::Config[:rack][:log_args] ? ::CGI.unescape(req.fullpath) : ::CGI.unescape(req.path)
82
+ report_kvs[:URL] = AppOpticsAPM::Config[:rack][:log_args] ? ::CGI.unescape(req.fullpath) : ::CGI.unescape(req.path)
82
83
 
83
- # Check for and validate X-Trace request header to pick up tracing context
84
- xtrace = env.is_a?(Hash) ? env['HTTP_X_TRACE'] : nil
85
- xtrace_header = AppOpticsAPM::XTrace.valid?(xtrace) ? xtrace : nil
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
- # Under JRuby, JAppOpticsAPM may have already started a trace. Make note of this
88
- # if so and don't clear context on log_end (see appoptics_apm/api/logging.rb)
89
- AppOpticsAPM.has_incoming_context = AppOpticsAPM.tracing?
90
- AppOpticsAPM.has_xtrace_header = xtrace_header
91
- AppOpticsAPM.is_continued_trace = AppOpticsAPM.has_incoming_context || AppOpticsAPM.has_xtrace_header
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
- xtrace = AppOpticsAPM::API.log_start(:rack, xtrace_header, report_kvs)
94
+ xtrace = AppOpticsAPM::API.log_start(:rack, xtrace_header, report_kvs)
94
95
 
95
- # We only trace a subset of requests based off of sample rate so if
96
- # AppOpticsAPM::API.log_start really did start a trace, we act accordingly here.
97
- if AppOpticsAPM.tracing?
98
- report_kvs = collect(req, env)
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
- # 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)
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
- status, headers, response = @app.call(env)
107
+ status, headers, response = @app.call(env)
107
108
 
108
- xtrace = AppOpticsAPM::API.log_end(:rack, :Status => status)
109
+ xtrace = AppOpticsAPM::API.log_end(:rack, :Status => status)
109
110
 
110
- else
111
- status, headers, response = @app.call(env)
112
- end
111
+ else
112
+ status, headers, response = @app.call(env)
113
+ end
113
114
 
114
- [status, headers, response]
115
- rescue Exception => e
116
- # it is ok to rescue Exception here because we are reraising it (we just need a chance to log_end)
117
- if AppOpticsAPM.tracing?
118
- AppOpticsAPM::API.log_exception(:rack, e)
119
- xtrace = AppOpticsAPM::API.log_end(:rack, :Status => 500, 'TransactionName' => transaction_name(env))
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
- if headers && AppOpticsAPM::XTrace.valid?(xtrace)
124
- unless defined?(JRUBY_VERSION) && AppOpticsAPM.is_continued_trace?
125
- headers['X-Trace'] = xtrace if headers.is_a?(Hash)
126
- end
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
- ensure
130
- unless ::AppOpticsAPM::Util.static_asset?(env['PATH_INFO'])
131
- status = status.to_i
132
- error = status.between?(500,599) ? 1 : 0
133
- duration =(1000 * 1000 * (Time.now - start)).round(0)
134
- AppOpticsAPM::Span.createHttpSpan(transaction_name(env), req_url, duration, status, req.request_method, error)
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
- private
157
+ def initialize(app)
158
+ @app = app
159
+ end
160
+
161
+ def call(env)
162
+ @app.call(env)
163
+ end
139
164
 
140
- def transaction_name(env)
141
- if env['appoptics_apm.controller'] || env['appoptics_apm.action']
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 suchs as Redis, Dalli and Resque.
7
+ # libraries such as Redis, Dalli and Resque.
8
8
  module Inst
9
9
  def self.load_instrumentation
10
10
  # Load the general instrumentation
@@ -8,7 +8,7 @@ module AppOpticsAPM
8
8
  module Version
9
9
  MAJOR = 4
10
10
  MINOR = 0
11
- PATCH = 2
11
+ PATCH = 3
12
12
 
13
13
  STRING = [MAJOR, MINOR, PATCH].compact.join('.')
14
14
  end
@@ -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.must_equal nil
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
- assert_equal nil, test_action
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'].must_equal nil
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)