newrelic_rpm 3.9.9.275 → 3.10.0.279

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. data.tar.gz.sig +0 -0
  2. data/CHANGELOG +51 -0
  3. data/config.dot +0 -3
  4. data/lib/new_relic/agent.rb +7 -5
  5. data/lib/new_relic/agent/agent.rb +4 -4
  6. data/lib/new_relic/agent/instrumentation/active_job.rb +5 -8
  7. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +17 -34
  8. data/lib/new_relic/agent/instrumentation/grape.rb +60 -23
  9. data/lib/new_relic/agent/instrumentation/merb/controller.rb +10 -2
  10. data/lib/new_relic/agent/instrumentation/middleware_tracing.rb +33 -21
  11. data/lib/new_relic/agent/instrumentation/rails/action_controller.rb +7 -3
  12. data/lib/new_relic/agent/instrumentation/rails3/action_controller.rb +0 -9
  13. data/lib/new_relic/agent/instrumentation/sinatra.rb +9 -12
  14. data/lib/new_relic/agent/javascript_instrumentor.rb +1 -0
  15. data/lib/new_relic/agent/new_relic_service.rb +9 -6
  16. data/lib/new_relic/agent/parameter_filtering.rb +37 -0
  17. data/lib/new_relic/agent/supported_versions.rb +7 -0
  18. data/lib/new_relic/agent/traced_method_stack.rb +1 -1
  19. data/lib/new_relic/agent/transaction.rb +182 -186
  20. data/lib/new_relic/agent/vm/rubinius_vm.rb +93 -3
  21. data/lib/new_relic/control/frameworks/rails.rb +1 -0
  22. data/lib/new_relic/rack/agent_hooks.rb +15 -23
  23. data/lib/new_relic/version.rb +2 -2
  24. data/newrelic_rpm.gemspec +12 -5
  25. data/test/agent_helper.rb +26 -14
  26. data/test/multiverse/lib/multiverse/suite.rb +1 -5
  27. data/test/multiverse/suites/activemerchant/Envfile +4 -1
  28. data/test/multiverse/suites/agent_only/cross_application_tracing_test.rb +2 -12
  29. data/test/multiverse/suites/agent_only/logging_test.rb +1 -1
  30. data/test/multiverse/suites/agent_only/set_transaction_name_test.rb +4 -0
  31. data/test/multiverse/suites/agent_only/synthetics_test.rb +1 -8
  32. data/test/multiverse/suites/agent_only/testing_app.rb +1 -7
  33. data/test/multiverse/suites/agent_only/xray_sessions_test.rb +11 -11
  34. data/test/multiverse/suites/deferred_instrumentation/sinatra_test.rb +4 -0
  35. data/test/multiverse/suites/grape/Envfile +1 -3
  36. data/test/multiverse/suites/grape/grape_test.rb +87 -6
  37. data/test/multiverse/suites/grape/grape_test_api.rb +5 -0
  38. data/test/multiverse/suites/grape/grape_versioning_test.rb +67 -0
  39. data/test/multiverse/suites/grape/grape_versioning_test_api.rb +72 -0
  40. data/test/multiverse/suites/rack/example_app.rb +31 -1
  41. data/test/multiverse/suites/rack/rack_auto_instrumentation_test.rb +11 -10
  42. data/test/multiverse/suites/rack/rack_cascade_test.rb +46 -0
  43. data/test/multiverse/suites/rack/rack_parameter_filtering_test.rb +40 -0
  44. data/test/multiverse/suites/rails/Envfile +8 -0
  45. data/test/multiverse/suites/rails/activejob_test.rb +16 -0
  46. data/test/multiverse/suites/rails/gc_instrumentation_test.rb +4 -2
  47. data/test/multiverse/suites/rails/parameter_capture_test.rb +49 -0
  48. data/test/multiverse/suites/rails/rails3_app/app_rails3_plus.rb +12 -1
  49. data/test/multiverse/suites/sinatra/sinatra_classic_test.rb +4 -0
  50. data/test/multiverse/suites/sinatra/sinatra_modular_test.rb +4 -0
  51. data/test/multiverse/suites/sinatra/sinatra_test_cases.rb +11 -0
  52. data/test/new_relic/agent/instrumentation/controller_instrumentation_test.rb +0 -58
  53. data/test/new_relic/agent/instrumentation/middleware_proxy_test.rb +49 -0
  54. data/test/new_relic/agent/instrumentation/middleware_tracing_test.rb +26 -14
  55. data/test/new_relic/agent/parameter_filtering_test.rb +39 -0
  56. data/test/new_relic/agent/stats_engine/gc_profiler_test.rb +1 -1
  57. data/test/new_relic/agent/transaction_test.rb +106 -2
  58. data/test/new_relic/agent/vm/rubinius_vm_test.rb +38 -37
  59. data/test/new_relic/agent_test.rb +8 -3
  60. data/test/new_relic/filtering_test_app.rb +18 -0
  61. data/test/new_relic/latest_changes_test.rb +1 -1
  62. data/test/new_relic/rack/browser_monitoring_test.rb +4 -4
  63. data/test/performance/lib/performance/instrumentation/gc_stats.rb +6 -4
  64. data/test/performance/lib/performance/platform.rb +1 -0
  65. data/test/performance/suites/thread_profiling.rb +12 -0
  66. metadata +38 -15
  67. metadata.gz.sig +2 -1
data.tar.gz.sig CHANGED
Binary file
data/CHANGELOG CHANGED
@@ -1,5 +1,56 @@
1
1
  # New Relic Ruby Agent Release Notes #
2
2
 
3
+ ## v3.10.0 ##
4
+
5
+ * Support for the Grape framework
6
+
7
+ We now instrument the Grape REST API framework! To avoid conflicts with the
8
+ third-party newrelic-grape gem, our instrumentation will not be installed if
9
+ newrelic-grape is present in the Gemfile.
10
+
11
+ For more details, see https://docs.newrelic.com/docs/agents/ruby-agent/frameworks/grape-instrumentation
12
+
13
+ * Automatic Cross Application Tracing support for all Rack applications
14
+
15
+ Previously Rack apps not using Rails or Sinatra needed to include the
16
+ AgentHooks middleware to get Cross Application Tracing support. With
17
+ these changes, this is no longer necessary. Any explicit references to
18
+ AgentHooks can be removed unless the `disable_middleware_instrumentation`
19
+ setting is set to `true`.
20
+
21
+ * Metrics no longer reported from Puma master processes
22
+
23
+ When using Puma's cluster mode with the preload_app! configuration directive,
24
+ the agent will no longer start its reporting thread in the Puma master
25
+ process. This should result in more accurate instance counts, and more
26
+ accurate stats on the Ruby VMs page (since the master process will be
27
+ excluded).
28
+
29
+ * Better support for Sinatra apps used with Rack::Cascade
30
+
31
+ Previously, using a Sinatra application as part of a Rack::Cascade chain would
32
+ cause all transactions to be named after the Sinatra application, rather than
33
+ allowing downstream applications to set the transaction name when the Sinatra
34
+ application returned a 404 response. This has been fixed.
35
+
36
+ * Updated support for Rubinius 2.3+ metrics
37
+
38
+ Rubinius 2.3 introduced a new system for gathering metrics from the
39
+ underlying VM. Data capture for the Ruby VM's page has been updated to take
40
+ advantage of these. Thanks Yorick Peterse for the contribution!
41
+
42
+ * Fix for missing ActiveJob traced errors
43
+
44
+ ActiveJobs processed by backends where the Ruby agent lacked existing
45
+ instrumentation missed reporting traced errors. This did not impact
46
+ ActiveJobs used with Sidekiq or Resque, and has been fixed.
47
+
48
+ * Fix possible crash in middleware tracing
49
+
50
+ In rare circumstances, a failure in the agent early during tracing of a web
51
+ request could lead to a cascading error when trying to capture the HTTP status
52
+ code of the request. This has been fixed. Thanks to Michal Cichra for the fix!
53
+
3
54
  ## v3.9.9 ##
4
55
 
5
56
  * Support for Ruby 2.2
data/config.dot CHANGED
@@ -42,7 +42,6 @@ digraph AgentEnabled {
42
42
  "[log_file_path]"
43
43
  "[dispatcher]"
44
44
  "[force_send]"
45
- "[disable_mobile_headers]"
46
45
  "[textmate]"
47
46
  "[post_size_limit]"
48
47
  "[sync_startup]"
@@ -224,8 +223,6 @@ digraph AgentEnabled {
224
223
  "ErrorCollector#initialize" -> "[error_collector.ignore_errors]"
225
224
  "ErrorCollector#request_params_from_opts" -> "[capture_params]"
226
225
 
227
- "ControllerInstrumentation#perform_action_with_newrelic_trace" -> "[disable_mobile_headers]"
228
-
229
226
  "NewRelicService#initialize" -> "[timeout]"
230
227
  "NewRelicService#initialize" -> "[license_key]"
231
228
  "NewRelicService#initialize" -> "Control#server"
@@ -543,11 +543,13 @@ module NewRelic
543
543
  nil # don't return a noticed error datastructure. it can only hurt.
544
544
  end
545
545
 
546
- # Add parameters to any Transaction Trace, Error or Analytics Events that
547
- # are recorded during the current transaction.
546
+ # Add parameters to the transaction trace, Insights Transaction event, and
547
+ # any traced errors recorded for the current transaction.
548
548
  #
549
- # If configured to allow it, these custom parameters will also be present
550
- # in the RUM script injected into the page.
549
+ # If Browser Monitoring is enabled, and the
550
+ # browser_monitoring.capture_attributes configuration setting is enabled,
551
+ # these custom parameters will also be present in the RUM script injected
552
+ # into the response body, making them available on Insights PageView events.
551
553
  #
552
554
  # @api public
553
555
  #
@@ -590,7 +592,7 @@ module NewRelic
590
592
  # @api public
591
593
  #
592
594
  def set_transaction_name(name, options={})
593
- Transaction.set_overriding_transaction_name(name, options)
595
+ Transaction.set_overriding_transaction_name(name, options[:category])
594
596
  end
595
597
 
596
598
  # Get the name of the current running transaction. This is useful if you
@@ -403,7 +403,7 @@ module NewRelic
403
403
  true
404
404
  else
405
405
  ::NewRelic::Agent.logger.warn("No license key found. " +
406
- "This often means your newrelic.yml file was not found, or it lacks a section for the running environment, '#{NewRelic::Control.instance.env}'.")
406
+ "This often means your newrelic.yml file was not found, or it lacks a section for the running environment, '#{NewRelic::Control.instance.env}'. You may also want to try linting your newrelic.yml to ensure it is valid YML.")
407
407
  false
408
408
  end
409
409
  end
@@ -428,10 +428,10 @@ module NewRelic
428
428
 
429
429
  # If we're using a dispatcher that forks before serving
430
430
  # requests, we need to wait until the children are forked
431
- # before connecting, otherwise the parent process sends odd data
431
+ # before connecting, otherwise the parent process sends useless data
432
432
  def using_forking_dispatcher?
433
- if [:passenger, :rainbows, :unicorn].include? Agent.config[:dispatcher]
434
- ::NewRelic::Agent.logger.info 'Connecting workers after forking.'
433
+ if [:puma, :passenger, :rainbows, :unicorn].include? Agent.config[:dispatcher]
434
+ ::NewRelic::Agent.logger.info "Deferring startup of agent reporting thread because #{Agent.config[:dispatcher]} may fork."
435
435
  true
436
436
  else
437
437
  false
@@ -42,7 +42,7 @@ module NewRelic
42
42
  elsif state.in_background_transaction?
43
43
  ::NewRelic::Agent::Transaction.set_default_transaction_name(
44
44
  transaction_name_suffix_for_job(job),
45
- :category => transaction_category)
45
+ transaction_category)
46
46
  block.call
47
47
  else
48
48
  run_in_transaction(state, job, block)
@@ -56,13 +56,10 @@ module NewRelic
56
56
  end
57
57
 
58
58
  def self.run_in_transaction(state, job, block)
59
- begin
60
- ::NewRelic::Agent::Transaction.start(state, :other,
61
- :transaction_name => transaction_name_for_job(job))
62
- block.call
63
- ensure
64
- ::NewRelic::Agent::Transaction.stop(state)
65
- end
59
+ ::NewRelic::Agent::Transaction.wrap(state,
60
+ transaction_name_for_job(job),
61
+ :other,
62
+ &block)
66
63
  end
67
64
 
68
65
  def self.transaction_category
@@ -218,7 +218,7 @@ module NewRelic
218
218
  end
219
219
 
220
220
  def self.prefix_for_category(txn, category = nil)
221
- category ||= (txn && txn.best_category)
221
+ category ||= (txn && txn.category)
222
222
  case category
223
223
  when :controller then ::NewRelic::Agent::Transaction::CONTROLLER_PREFIX
224
224
  when :task then ::NewRelic::Agent::Transaction::TASK_PREFIX
@@ -226,6 +226,7 @@ module NewRelic
226
226
  when :uri then ::NewRelic::Agent::Transaction::CONTROLLER_PREFIX
227
227
  when :sinatra then ::NewRelic::Agent::Transaction::SINATRA_PREFIX
228
228
  when :middleware then ::NewRelic::Agent::Transaction::MIDDLEWARE_PREFIX
229
+ when :grape then ::NewRelic::Agent::Transaction::GRAPE_PREFIX
229
230
  else "#{category.to_s}/" # for internal use only
230
231
  end
231
232
  end
@@ -331,42 +332,25 @@ module NewRelic
331
332
  state.request = newrelic_request(args)
332
333
 
333
334
  skip_tracing = do_not_trace? || !state.is_execution_traced?
335
+
334
336
  if skip_tracing
335
- if block_given?
336
- return yield
337
- else
338
- state.current_transaction.ignore! if state.current_transaction
339
- NewRelic::Agent.disable_all_tracing do
340
- return perform_action_without_newrelic_trace(*args)
341
- end
342
- end
337
+ state.current_transaction.ignore! if state.current_transaction
338
+ NewRelic::Agent.disable_all_tracing { return yield }
343
339
  end
344
340
 
345
- # If a block was passed in, then the arguments represent options for
346
- # the instrumentation, not app method arguments.
347
- trace_options = NR_DEFAULT_OPTIONS
341
+ # This method has traditionally taken a variable number of arguments, but the
342
+ # only one that is expected / used is a single options hash. We are preserving
343
+ # the *args method signature to ensure backwards compatibility.
348
344
 
349
- if block_given?
350
- trace_options = args.last if args.last.is_a?(Hash)
351
- available_params = trace_options[:params]
352
- else
353
- available_params = respond_to?(:params) && params
354
- end
355
-
356
- category = trace_options[:category] || :controller
357
- txn_options = create_transaction_options(trace_options, available_params)
358
- txn_options[:transaction_name] = TransactionNamer.name_for(nil, self, category, trace_options)
359
- txn_options[:apdex_start_time] = detect_queue_start_time(state)
345
+ trace_options = args.last.is_a?(Hash) ? args.last : NR_DEFAULT_OPTIONS
346
+ category = trace_options[:category] || :controller
347
+ txn_options = create_transaction_options(trace_options, category, state)
360
348
 
361
349
  begin
362
350
  txn = Transaction.start(state, category, txn_options)
363
351
 
364
352
  begin
365
- if block_given?
366
- yield
367
- else
368
- perform_action_without_newrelic_trace(*args)
369
- end
353
+ yield
370
354
  rescue => e
371
355
  NewRelic::Agent.notice_error(e)
372
356
  raise
@@ -429,15 +413,14 @@ module NewRelic
429
413
 
430
414
  private
431
415
 
432
- def create_transaction_options(trace_options, available_params)
416
+ def create_transaction_options(trace_options, category, state)
433
417
  txn_options = {}
434
418
  txn_options[:request] = trace_options[:request]
435
419
  txn_options[:request] ||= request if respond_to?(:request)
436
-
437
- if available_params
438
- txn_options[:filtered_params] = (respond_to?(:filter_parameters)) ? filter_parameters(available_params) : available_params
439
- end
440
-
420
+ # params should have been filtered before calling perform_action_with_newrelic_trace
421
+ txn_options[:filtered_params] = trace_options[:params]
422
+ txn_options[:transaction_name] = TransactionNamer.name_for(nil, self, category, trace_options)
423
+ txn_options[:apdex_start_time] = detect_queue_start_time(state)
441
424
  txn_options
442
425
  end
443
426
 
@@ -2,13 +2,52 @@
2
2
  # This file is distributed under New Relic's license terms.
3
3
  # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
4
4
 
5
+ require 'new_relic/agent/parameter_filtering'
6
+
5
7
  module NewRelic
6
8
  module Agent
7
- module GrapeInstrumentation
8
- API_ENDPOINT = 'api.endpoint'.freeze
9
- FORMAT = '(.:format)'.freeze
10
- EMPTY_STRING = ''.freeze
11
- MIN_VERSION = ::NewRelic::VersionNumber.new("0.2.0")
9
+ module Instrumentation
10
+ module GrapeInstrumentation
11
+ extend self
12
+
13
+ API_ENDPOINT = 'api.endpoint'.freeze
14
+ FORMAT_REGEX = /\(\/?\.:format\)/.freeze
15
+ VERSION_REGEX = /:version(\/|$)/.freeze
16
+ EMPTY_STRING = ''.freeze
17
+ MIN_VERSION = VersionNumber.new("0.2.0")
18
+
19
+ def handle_transaction(endpoint, class_name)
20
+ return unless endpoint && route = endpoint.route
21
+ name_transaction(route, class_name)
22
+ capture_params(endpoint) if Agent.config[:capture_params]
23
+ end
24
+
25
+ def name_transaction(route, class_name)
26
+ txn_name = name_for_transaction(route, class_name)
27
+ segment_name = "Middleware/Grape/#{class_name}/call"
28
+ Transaction.set_default_transaction_name(txn_name, :grape, segment_name)
29
+ end
30
+
31
+ def name_for_transaction(route, class_name)
32
+ action_name = route.route_path.sub(FORMAT_REGEX, EMPTY_STRING)
33
+ method_name = route.route_method
34
+
35
+ if route.route_version
36
+ action_name = action_name.sub(VERSION_REGEX, EMPTY_STRING)
37
+ "#{class_name}-#{route.route_version}#{action_name} (#{method_name})"
38
+ else
39
+ "#{class_name}#{action_name} (#{method_name})"
40
+ end
41
+ end
42
+
43
+ def capture_params(endpoint)
44
+ txn = Transaction.tl_current
45
+ env = endpoint.request.env
46
+ params = ParameterFiltering::apply_filters(env, endpoint.params)
47
+ params.delete("route_info")
48
+ txn.filtered_params = params
49
+ end
50
+ end
12
51
  end
13
52
  end
14
53
  end
@@ -24,15 +63,25 @@ DependencyDetection.defer do
24
63
 
25
64
  depends_on do
26
65
  defined?(::Grape::VERSION) &&
27
- ::NewRelic::VersionNumber.new(::Grape::VERSION) >= ::NewRelic::Agent::GrapeInstrumentation::MIN_VERSION
66
+ ::NewRelic::VersionNumber.new(::Grape::VERSION) >= ::NewRelic::Agent::Instrumentation::GrapeInstrumentation::MIN_VERSION
28
67
  end
29
68
 
30
69
  depends_on do
31
- false
70
+ begin
71
+ if defined?(Bundler) && Bundler.rubygems.all_specs.map(&:name).include?("newrelic-grape")
72
+ ::NewRelic::Agent.logger.info("Not installing New Relic supported Grape instrumentation because the third party newrelic-grape gem is present")
73
+ false
74
+ else
75
+ true
76
+ end
77
+ rescue => e
78
+ ::NewRelic::Agent.logger.info("Could not determine if third party newrelic-grape gem is installed")
79
+ true
80
+ end
32
81
  end
33
82
 
34
83
  executes do
35
- NewRelic::Agent.logger.info 'Installing Grape instrumentation'
84
+ NewRelic::Agent.logger.info 'Installing New Relic supported Grape instrumentation'
36
85
  instrument_call
37
86
  end
38
87
 
@@ -42,23 +91,11 @@ DependencyDetection.defer do
42
91
  begin
43
92
  response = call_without_new_relic(env)
44
93
  ensure
45
- # We don't want an error in our transaction naming to kill the request.
46
94
  begin
47
- endpoint = env[::NewRelic::Agent::GrapeInstrumentation::API_ENDPOINT]
48
-
49
- if endpoint
50
- route_obj = endpoint.route
51
- if route_obj
52
- action_name = route_obj.route_path.sub(::NewRelic::Agent::GrapeInstrumentation::FORMAT,
53
- ::NewRelic::Agent::GrapeInstrumentation::EMPTY_STRING)
54
- method_name = route_obj.route_method
55
-
56
- txn_name = "#{self.class.name}#{action_name} (#{method_name})"
57
- ::NewRelic::Agent.set_transaction_name(txn_name)
58
- end
59
- end
95
+ endpoint = env[::NewRelic::Agent::Instrumentation::GrapeInstrumentation::API_ENDPOINT]
96
+ ::NewRelic::Agent::Instrumentation::GrapeInstrumentation.handle_transaction(endpoint, self.class.name)
60
97
  rescue => e
61
- ::NewRelic::Agent.logger.warn("Error in Grape transaction naming", e)
98
+ ::NewRelic::Agent.logger.warn("Error in Grape instrumentation", e)
62
99
  end
63
100
  end
64
101
 
@@ -29,8 +29,16 @@ DependencyDetection.defer do
29
29
 
30
30
  protected
31
31
 
32
- alias_method :perform_action_without_newrelic_trace, :_dispatch
33
- alias_method :_dispatch, :perform_action_with_newrelic_trace
32
+ def _dispatch_with_newrelic_trace(*args)
33
+ options = {}
34
+ options[:params] = params
35
+ perform_action_with_newrelic_trace(options) do
36
+ _dispatch_without_newrelic_trace(*args)
37
+ end
38
+ end
39
+
40
+ alias_method :_dispatch_without_newrelic_trace, :_dispatch
41
+ alias_method :_dispatch, :_dispatch_with_newrelic_trace
34
42
  end
35
43
  end
36
44
  end
@@ -26,40 +26,48 @@ module NewRelic
26
26
  module Agent
27
27
  module Instrumentation
28
28
  module MiddlewareTracing
29
- CAPTURED_REQUEST_KEY = 'newrelic.captured_request'.freeze unless defined?(CAPTURED_REQUEST_KEY)
29
+ TXN_STARTED_KEY = 'newrelic.transaction_started'.freeze unless defined?(TXN_STARTED_KEY)
30
30
 
31
31
  def _nr_has_middleware_tracing
32
32
  true
33
33
  end
34
34
 
35
- def build_transaction_options(env)
36
- if env[CAPTURED_REQUEST_KEY]
37
- transaction_options
38
- else
39
- env[CAPTURED_REQUEST_KEY] = true
40
- queue_timefrontend_timestamp = QueueTime.parse_frontend_timestamp(env)
41
- transaction_options.merge(
42
- :request => ::Rack::Request.new(env),
43
- :apdex_start_time => queue_timefrontend_timestamp
44
- )
35
+ def build_transaction_options(env, first_middleware)
36
+ opts = transaction_options
37
+ opts = merge_first_middleware_options(opts, env) if first_middleware
38
+ opts
39
+ end
40
+
41
+ def merge_first_middleware_options(opts, env)
42
+ opts.merge(
43
+ :request => ::Rack::Request.new(env),
44
+ :apdex_start_time => QueueTime.parse_frontend_timestamp(env)
45
+ )
46
+ end
47
+
48
+ def note_transaction_started(env)
49
+ env[TXN_STARTED_KEY] = true unless env[TXN_STARTED_KEY]
50
+ end
51
+
52
+ def capture_http_response_code(state, result)
53
+ if result.is_a?(Array) && state.current_transaction
54
+ state.current_transaction.http_response_code = result[0]
45
55
  end
46
56
  end
47
57
 
48
58
  def call(env)
49
- opts = build_transaction_options(env)
59
+ first_middleware = note_transaction_started(env)
60
+
50
61
  state = NewRelic::Agent::TransactionState.tl_get
51
62
 
52
63
  begin
53
- Transaction.start(state, category, opts)
54
- if target == self
55
- result = traced_call(env)
56
- else
57
- result = target.call(env)
58
- end
64
+ Transaction.start(state, category, build_transaction_options(env, first_middleware))
65
+ events.notify(:before_call, env) if first_middleware
66
+
67
+ result = (target == self) ? traced_call(env) : target.call(env)
59
68
 
60
- if result.is_a?(Array)
61
- state.current_transaction.http_response_code = result[0]
62
- end
69
+ capture_http_response_code(state, result)
70
+ events.notify(:after_call, env, result) if first_middleware
63
71
 
64
72
  result
65
73
  rescue Exception => e
@@ -69,6 +77,10 @@ module NewRelic
69
77
  Transaction.stop(state)
70
78
  end
71
79
  end
80
+
81
+ def events
82
+ NewRelic::Agent.instance.events
83
+ end
72
84
  end
73
85
  end
74
86
  end