appoptics_apm 4.0.2 → 4.0.3

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