newrelic_rpm 6.15.0 → 7.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +164 -22
  3. data/CONTRIBUTING.md +13 -2
  4. data/README.md +2 -2
  5. data/lib/new_relic/agent.rb +5 -8
  6. data/lib/new_relic/agent/agent.rb +2 -1
  7. data/lib/new_relic/agent/autostart.rb +1 -2
  8. data/lib/new_relic/agent/configuration/default_source.rb +442 -227
  9. data/lib/new_relic/agent/configuration/manager.rb +3 -3
  10. data/lib/new_relic/agent/connect/request_builder.rb +4 -2
  11. data/lib/new_relic/agent/datastores/redis.rb +0 -4
  12. data/lib/new_relic/agent/distributed_tracing.rb +0 -66
  13. data/lib/new_relic/agent/error_collector.rb +52 -37
  14. data/lib/new_relic/agent/error_filter.rb +167 -0
  15. data/lib/new_relic/agent/instrumentation/active_record_notifications.rb +0 -16
  16. data/lib/new_relic/agent/instrumentation/active_record_subscriber.rb +1 -3
  17. data/lib/new_relic/agent/instrumentation/bunny.rb +10 -152
  18. data/lib/new_relic/agent/instrumentation/bunny/chain.rb +45 -0
  19. data/lib/new_relic/agent/instrumentation/bunny/instrumentation.rb +152 -0
  20. data/lib/new_relic/agent/instrumentation/bunny/prepend.rb +35 -0
  21. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +1 -0
  22. data/lib/new_relic/agent/instrumentation/curb.rb +9 -241
  23. data/lib/new_relic/agent/instrumentation/curb/chain.rb +93 -0
  24. data/lib/new_relic/agent/instrumentation/curb/instrumentation.rb +222 -0
  25. data/lib/new_relic/agent/instrumentation/curb/prepend.rb +63 -0
  26. data/lib/new_relic/agent/instrumentation/delayed_job/chain.rb +38 -0
  27. data/lib/new_relic/agent/instrumentation/delayed_job/instrumentation.rb +53 -0
  28. data/lib/new_relic/agent/instrumentation/delayed_job/prepend.rb +34 -0
  29. data/lib/new_relic/agent/instrumentation/delayed_job_instrumentation.rb +8 -50
  30. data/lib/new_relic/agent/instrumentation/excon.rb +2 -1
  31. data/lib/new_relic/agent/instrumentation/grape.rb +13 -113
  32. data/lib/new_relic/agent/instrumentation/grape/chain.rb +25 -0
  33. data/lib/new_relic/agent/instrumentation/grape/instrumentation.rb +100 -0
  34. data/lib/new_relic/agent/instrumentation/grape/prepend.rb +17 -0
  35. data/lib/new_relic/agent/instrumentation/httpclient.rb +8 -30
  36. data/lib/new_relic/agent/instrumentation/httpclient/chain.rb +25 -0
  37. data/lib/new_relic/agent/instrumentation/httpclient/instrumentation.rb +38 -0
  38. data/lib/new_relic/agent/instrumentation/httpclient/prepend.rb +17 -0
  39. data/lib/new_relic/agent/instrumentation/httprb.rb +29 -0
  40. data/lib/new_relic/agent/instrumentation/httprb/chain.rb +22 -0
  41. data/lib/new_relic/agent/instrumentation/httprb/instrumentation.rb +30 -0
  42. data/lib/new_relic/agent/instrumentation/httprb/prepend.rb +15 -0
  43. data/lib/new_relic/agent/instrumentation/memcache.rb +54 -69
  44. data/lib/new_relic/agent/instrumentation/memcache/chain.rb +16 -0
  45. data/lib/new_relic/agent/instrumentation/memcache/dalli.rb +38 -121
  46. data/lib/new_relic/agent/instrumentation/memcache/helper.rb +56 -0
  47. data/lib/new_relic/agent/instrumentation/memcache/instrumentation.rb +88 -0
  48. data/lib/new_relic/agent/instrumentation/memcache/prepend.rb +88 -0
  49. data/lib/new_relic/agent/instrumentation/middleware_proxy.rb +2 -0
  50. data/lib/new_relic/agent/instrumentation/mongo.rb +7 -0
  51. data/lib/new_relic/agent/instrumentation/net_http.rb +44 -0
  52. data/lib/new_relic/agent/instrumentation/net_http/chain.rb +25 -0
  53. data/lib/new_relic/agent/instrumentation/{net_prepend.rb → net_http/instrumentation.rb} +3 -3
  54. data/lib/new_relic/agent/instrumentation/net_http/prepend.rb +21 -0
  55. data/lib/new_relic/agent/instrumentation/padrino.rb +18 -53
  56. data/lib/new_relic/agent/instrumentation/padrino/chain.rb +34 -0
  57. data/lib/new_relic/agent/instrumentation/padrino/instrumentation.rb +27 -0
  58. data/lib/new_relic/agent/instrumentation/padrino/prepend.rb +20 -0
  59. data/lib/new_relic/agent/instrumentation/rack.rb +29 -139
  60. data/lib/new_relic/agent/instrumentation/rack/chain.rb +58 -0
  61. data/lib/new_relic/agent/instrumentation/rack/helpers.rb +32 -0
  62. data/lib/new_relic/agent/instrumentation/rack/instrumentation.rb +73 -0
  63. data/lib/new_relic/agent/instrumentation/rack/prepend.rb +37 -0
  64. data/lib/new_relic/agent/instrumentation/rake.rb +13 -154
  65. data/lib/new_relic/agent/instrumentation/rake/chain.rb +25 -0
  66. data/lib/new_relic/agent/instrumentation/rake/instrumentation.rb +144 -0
  67. data/lib/new_relic/agent/instrumentation/rake/prepend.rb +14 -0
  68. data/lib/new_relic/agent/instrumentation/redis.rb +12 -107
  69. data/lib/new_relic/agent/instrumentation/redis/chain.rb +34 -0
  70. data/lib/new_relic/agent/instrumentation/redis/instrumentation.rb +65 -0
  71. data/lib/new_relic/agent/instrumentation/redis/prepend.rb +24 -0
  72. data/lib/new_relic/agent/instrumentation/resque.rb +21 -32
  73. data/lib/new_relic/agent/instrumentation/resque/chain.rb +22 -0
  74. data/lib/new_relic/agent/instrumentation/resque/helper.rb +19 -0
  75. data/lib/new_relic/agent/instrumentation/resque/instrumentation.rb +35 -0
  76. data/lib/new_relic/agent/instrumentation/resque/prepend.rb +16 -0
  77. data/lib/new_relic/agent/instrumentation/sinatra.rb +20 -158
  78. data/lib/new_relic/agent/instrumentation/sinatra/chain.rb +55 -0
  79. data/lib/new_relic/agent/instrumentation/sinatra/ignorer.rb +29 -34
  80. data/lib/new_relic/agent/instrumentation/sinatra/instrumentation.rb +124 -0
  81. data/lib/new_relic/agent/instrumentation/sinatra/prepend.rb +33 -0
  82. data/lib/new_relic/agent/instrumentation/typhoeus.rb +10 -89
  83. data/lib/new_relic/agent/instrumentation/typhoeus/chain.rb +22 -0
  84. data/lib/new_relic/agent/instrumentation/typhoeus/instrumentation.rb +82 -0
  85. data/lib/new_relic/agent/instrumentation/typhoeus/prepend.rb +14 -0
  86. data/lib/new_relic/agent/javascript_instrumentor.rb +12 -7
  87. data/lib/new_relic/agent/new_relic_service.rb +15 -13
  88. data/lib/new_relic/agent/sql_sampler.rb +3 -3
  89. data/lib/new_relic/agent/transaction.rb +1 -4
  90. data/lib/new_relic/control/frameworks/rails.rb +11 -9
  91. data/lib/new_relic/control/instance_methods.rb +1 -0
  92. data/lib/new_relic/dependency_detection.rb +116 -10
  93. data/lib/new_relic/noticed_error.rb +4 -8
  94. data/lib/new_relic/supportability_helper.rb +1 -2
  95. data/lib/new_relic/version.rb +2 -2
  96. data/lib/tasks/config.html.erb +14 -25
  97. data/lib/tasks/config.rake +8 -7
  98. data/newrelic_rpm.gemspec +1 -1
  99. metadata +55 -8
  100. data/cert/cacert.pem +0 -1177
  101. data/lib/new_relic/agent/instrumentation/http.rb +0 -49
  102. data/lib/new_relic/agent/instrumentation/net.rb +0 -70
@@ -0,0 +1,55 @@
1
+ # encoding: utf-8
2
+ # This file is distributed under New Relic's license terms.
3
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
4
+
5
+ module NewRelic::Agent::Instrumentation
6
+ module Sinatra
7
+ module Chain
8
+ def self.instrument!
9
+ ::Sinatra::Base.class_eval do
10
+ include ::NewRelic::Agent::Instrumentation::Sinatra::Tracer
11
+
12
+ def dispatch_with_newrelic
13
+ dispatch_with_tracing { dispatch_without_newrelic }
14
+ end
15
+ alias dispatch_without_newrelic dispatch!
16
+ alias dispatch! dispatch_with_newrelic
17
+
18
+ def process_route_with_newrelic(*args, &block)
19
+ process_route_with_tracing(*args) do
20
+ process_route_without_newrelic(*args, &block)
21
+ end
22
+ end
23
+ alias process_route_without_newrelic process_route
24
+ alias process_route process_route_with_newrelic
25
+
26
+ def route_eval_with_newrelic(*args, &block)
27
+ route_eval_with_tracing(*args) do
28
+ route_eval_without_newrelic(*args, &block)
29
+ end
30
+ end
31
+ alias route_eval_without_newrelic route_eval
32
+ alias route_eval route_eval_with_newrelic
33
+ end
34
+ end
35
+ end
36
+
37
+ module Build
38
+ module Chain
39
+ def self.instrument!
40
+ ::Sinatra::Base.class_eval do
41
+ class << self
42
+ def build_with_newrelic(*args, &block)
43
+ build_with_tracing(*args) do
44
+ build_without_newrelic(*args, &block)
45
+ end
46
+ end
47
+ alias build_without_newrelic build
48
+ alias build build_with_newrelic
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -2,49 +2,44 @@
2
2
  # This file is distributed under New Relic's license terms.
3
3
  # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
4
4
 
5
- module NewRelic
6
- module Agent
7
- module Instrumentation
8
- module Sinatra
9
- module Ignorer
5
+ module NewRelic::Agent::Instrumentation
6
+ module Sinatra
7
+ module Ignorer
10
8
 
11
- def self.should_ignore?(app, type)
12
- return false if !app.settings.respond_to?(:newrelic_ignores)
9
+ def self.should_ignore?(app, type)
10
+ return false if !app.settings.respond_to?(:newrelic_ignores)
13
11
 
14
- app.settings.newrelic_ignores[type].any? do |pattern|
15
- pattern.match(app.request.path_info)
16
- end
17
- end
18
-
19
- def newrelic_ignore(*routes)
20
- set_newrelic_ignore(:routes, *routes)
21
- end
12
+ app.settings.newrelic_ignores[type].any? do |pattern|
13
+ pattern.match(app.request.path_info)
14
+ end
15
+ end
22
16
 
23
- def newrelic_ignore_apdex(*routes)
24
- set_newrelic_ignore(:apdex, *routes)
25
- end
17
+ def newrelic_ignore(*routes)
18
+ set_newrelic_ignore(:routes, *routes)
19
+ end
26
20
 
27
- def newrelic_ignore_enduser(*routes)
28
- set_newrelic_ignore(:enduser, *routes)
29
- end
21
+ def newrelic_ignore_apdex(*routes)
22
+ set_newrelic_ignore(:apdex, *routes)
23
+ end
30
24
 
31
- private
25
+ def newrelic_ignore_enduser(*routes)
26
+ set_newrelic_ignore(:enduser, *routes)
27
+ end
32
28
 
33
- def set_newrelic_ignore(type, *routes)
34
- # Important to default this in the context of the actual app
35
- # If it's done at register time, ignores end up shared between apps.
36
- set :newrelic_ignores, Hash.new([]) if !respond_to?(:newrelic_ignores)
29
+ private
37
30
 
38
- # If we call an ignore without a route, it applies to the whole app
39
- routes = ["*"] if routes.empty?
31
+ def set_newrelic_ignore(type, *routes)
32
+ # Important to default this in the context of the actual app
33
+ # If it's done at register time, ignores end up shared between apps.
34
+ set :newrelic_ignores, Hash.new([]) if !respond_to?(:newrelic_ignores)
40
35
 
41
- settings.newrelic_ignores[type] += routes.map do |r|
42
- # Ugly sending to private Base#compile, but we want to mimic
43
- # exactly Sinatra's mapping of route text to regex
44
- Array(send(:compile, r)).first
45
- end
46
- end
36
+ # If we call an ignore without a route, it applies to the whole app
37
+ routes = ["*"] if routes.empty?
47
38
 
39
+ settings.newrelic_ignores[type] += routes.map do |r|
40
+ # Ugly sending to private Base#compile, but we want to mimic
41
+ # exactly Sinatra's mapping of route text to regex
42
+ Array(send(:compile, r)).first
48
43
  end
49
44
  end
50
45
  end
@@ -0,0 +1,124 @@
1
+ # encoding: utf-8
2
+ # This file is distributed under New Relic's license terms.
3
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
4
+
5
+ # NewRelic instrumentation for Sinatra applications. Sinatra actions will
6
+ # appear in the UI similar to controller actions, and have breakdown charts
7
+ # and transaction traces.
8
+ #
9
+ # The actions in the UI will correspond to the pattern expression used
10
+ # to match them, not directly to full URL's.
11
+ module NewRelic::Agent::Instrumentation
12
+ module Sinatra
13
+ module Tracer
14
+ include ::NewRelic::Agent::Instrumentation::ControllerInstrumentation
15
+
16
+ def self.included(clazz)
17
+ clazz.extend(self)
18
+ end
19
+
20
+ # Expected method for supporting ControllerInstrumentation
21
+ def newrelic_request_headers(_)
22
+ request.env
23
+ end
24
+
25
+ def newrelic_middlewares
26
+ middlewares = [NewRelic::Rack::BrowserMonitoring]
27
+ if NewRelic::Rack::AgentHooks.needed?
28
+ middlewares << NewRelic::Rack::AgentHooks
29
+ end
30
+ middlewares
31
+ end
32
+
33
+ def build_with_tracing(*args, &block)
34
+ unless NewRelic::Agent.config[:disable_sinatra_auto_middleware]
35
+ newrelic_middlewares.each do |middleware_class|
36
+ try_to_use(self, middleware_class)
37
+ end
38
+ end
39
+ yield
40
+ end
41
+
42
+ def install_lock
43
+ @install_lock ||= Mutex.new
44
+ end
45
+
46
+ def try_to_use(app, clazz)
47
+ install_lock.synchronize do
48
+ has_middleware = app.middleware && app.middleware.any? { |info| info && info[0] == clazz }
49
+ app.use(clazz) unless has_middleware
50
+ end
51
+ end
52
+
53
+ # Capture last route we've seen. Will set for transaction on route_eval
54
+ def process_route_with_tracing(*args)
55
+ begin
56
+ env["newrelic.last_route"] = args[0]
57
+ rescue => e
58
+ ::NewRelic::Agent.logger.debug("Failed determining last route in Sinatra", e)
59
+ end
60
+ yield
61
+ end
62
+
63
+ # If a transaction name is already set, this call will tromple over it.
64
+ # This is intentional, as typically passing to a separate route is like
65
+ # an entirely separate transaction, so we pick up the new name.
66
+ #
67
+ # If we're ignored, this remains safe, since set_transaction_name
68
+ # care for the gating on the transaction's existence for us.
69
+ def route_eval_with_tracing(*args)
70
+ begin
71
+ if txn_name = TransactionNamer.transaction_name_for_route(env, request)
72
+ ::NewRelic::Agent::Transaction.set_default_transaction_name(
73
+ "#{self.class.name}/#{txn_name}", :sinatra)
74
+ end
75
+ rescue => e
76
+ ::NewRelic::Agent.logger.debug("Failed during route_eval to set transaction name", e)
77
+ end
78
+ yield
79
+ end
80
+
81
+ def get_request_params
82
+ begin
83
+ @request.params
84
+ rescue => e
85
+ NewRelic::Agent.logger.debug("Failed to get params from Rack request.", e)
86
+ nil
87
+ end
88
+ end
89
+
90
+ def dispatch_with_tracing
91
+ request_params = get_request_params
92
+ filtered_params = ::NewRelic::Agent::ParameterFiltering::apply_filters(request.env, request_params || {})
93
+
94
+ name = TransactionNamer.initial_transaction_name(request)
95
+ perform_action_with_newrelic_trace(:category => :sinatra,
96
+ :name => name,
97
+ :params => filtered_params) do
98
+ begin
99
+ yield
100
+ ensure
101
+ # Will only see an error raised if :show_exceptions is true, but
102
+ # will always see them in the env hash if they occur
103
+ had_error = env.has_key?('sinatra.error')
104
+ ::NewRelic::Agent.notice_error(env['sinatra.error']) if had_error
105
+ end
106
+ end
107
+ end
108
+
109
+ def do_not_trace?
110
+ Ignorer.should_ignore?(self, :routes)
111
+ end
112
+
113
+ # Overrides ControllerInstrumentation implementation
114
+ def ignore_apdex?
115
+ Ignorer.should_ignore?(self, :apdex)
116
+ end
117
+
118
+ # Overrides ControllerInstrumentation implementation
119
+ def ignore_enduser?
120
+ Ignorer.should_ignore?(self, :enduser)
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,33 @@
1
+ # encoding: utf-8
2
+ # This file is distributed under New Relic's license terms.
3
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
4
+
5
+ module NewRelic::Agent::Instrumentation
6
+ module Sinatra
7
+ module Prepend
8
+ include ::NewRelic::Agent::Instrumentation::Sinatra::Tracer
9
+
10
+ def dispatch!
11
+ dispatch_with_tracing { super }
12
+ end
13
+
14
+ def process_route(*args, &block)
15
+ process_route_with_tracing(*args) { super }
16
+ end
17
+
18
+ def route_eval(*args, &block)
19
+ route_eval_with_tracing(*args) { super }
20
+ end
21
+ end
22
+
23
+ module Build
24
+ module Prepend
25
+ include ::NewRelic::Agent::Instrumentation::Sinatra::Tracer
26
+
27
+ def build(*args, &block)
28
+ build_with_tracing(*args) { super }
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -2,6 +2,10 @@
2
2
  # This file is distributed under New Relic's license terms.
3
3
  # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
4
4
 
5
+ require_relative 'typhoeus/instrumentation'
6
+ require_relative 'typhoeus/chain'
7
+ require_relative 'typhoeus/prepend'
8
+
5
9
  DependencyDetection.defer do
6
10
  named :typhoeus
7
11
 
@@ -10,7 +14,7 @@ DependencyDetection.defer do
10
14
  end
11
15
 
12
16
  depends_on do
13
- NewRelic::Agent::Instrumentation::TyphoeusTracing.is_supported_version?
17
+ NewRelic::Agent::Instrumentation::Typhoeus.is_supported_version?
14
18
  end
15
19
 
16
20
  executes do
@@ -22,7 +26,7 @@ DependencyDetection.defer do
22
26
  # Basic request tracing
23
27
  executes do
24
28
  Typhoeus.before do |request|
25
- NewRelic::Agent::Instrumentation::TyphoeusTracing.trace(request)
29
+ NewRelic::Agent::Instrumentation::Typhoeus.trace(request)
26
30
 
27
31
  # Ensure that we always return a truthy value from the before block,
28
32
  # otherwise Typhoeus will bail out of the instrumentation.
@@ -32,93 +36,10 @@ DependencyDetection.defer do
32
36
 
33
37
  # Apply single TT node for Hydra requests until async support
34
38
  executes do
35
- class Typhoeus::Hydra
36
-
37
- def run_with_newrelic(*args)
38
- segment = NewRelic::Agent::Tracer.start_segment(
39
- name: NewRelic::Agent::Instrumentation::TyphoeusTracing::HYDRA_SEGMENT_NAME
40
- )
41
-
42
- instance_variable_set :@__newrelic_hydra_segment, segment
43
-
44
- begin
45
- run_without_newrelic(*args)
46
- ensure
47
- segment.finish if segment
48
- end
49
- end
50
-
51
- alias run_without_newrelic run
52
- alias run run_with_newrelic
53
- end
54
- end
55
- end
56
-
57
-
58
- module NewRelic
59
- module Agent
60
- module Instrumentation
61
- module TyphoeusTracing
62
-
63
- HYDRA_SEGMENT_NAME = "External/Multiple/Typhoeus::Hydra/run"
64
- NOTICIBLE_ERROR_CLASS = "Typhoeus::Errors::TyphoeusError"
65
-
66
- EARLIEST_VERSION = Gem::Version.new("0.5.3")
67
- def self.is_supported_version?
68
- Gem::Version.new(Typhoeus::VERSION) >= NewRelic::Agent::Instrumentation::TyphoeusTracing::EARLIEST_VERSION
69
- end
70
-
71
- def self.request_is_hydra_enabled?(request)
72
- request.respond_to?(:hydra) && request.hydra
73
- end
74
-
75
- def self.response_message(response)
76
- if response.respond_to?(:response_message)
77
- response.response_message
78
- elsif response.respond_to?(:return_message)
79
- response.return_message
80
- else
81
- # 0.5.4 seems to have lost xxxx_message methods altogether.
82
- "timeout"
83
- end
84
- end
85
-
86
- def self.trace(request)
87
- state = NewRelic::Agent::Tracer.state
88
- return unless state.is_execution_traced?
89
-
90
- wrapped_request = ::NewRelic::Agent::HTTPClients::TyphoeusHTTPRequest.new(request)
91
-
92
- parent = if request_is_hydra_enabled?(request)
93
- request.hydra.instance_variable_get(:@__newrelic_hydra_segment)
94
- end
95
-
96
- segment = NewRelic::Agent::Tracer.start_external_request_segment(
97
- library: wrapped_request.type,
98
- uri: wrapped_request.uri,
99
- procedure: wrapped_request.method,
100
- parent: parent
101
- )
102
-
103
- segment.add_request_headers wrapped_request
104
-
105
- callback = Proc.new do
106
- wrapped_response = HTTPClients::TyphoeusHTTPResponse.new(request.response)
107
-
108
- segment.process_response_headers wrapped_response
109
-
110
- if request.response.code == 0
111
- segment.notice_error NoticibleError.new NOTICIBLE_ERROR_CLASS, response_message(request.response)
112
- end
113
-
114
- segment.finish if segment
115
- end
116
- request.on_complete.unshift(callback)
117
-
118
- rescue => e
119
- NewRelic::Agent.logger.error("Exception during trace setup for Typhoeus request", e)
120
- end
121
- end
39
+ if use_prepend?
40
+ prepend_instrument Typhoeus::Hydra, NewRelic::Agent::Instrumentation::Typhoeus::Prepend
41
+ else
42
+ chain_instrument NewRelic::Agent::Instrumentation::Typhoeus::Chain
122
43
  end
123
44
  end
124
45
  end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+ # This file is distributed under New Relic's license terms.
3
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
4
+
5
+ module NewRelic::Agent::Instrumentation
6
+ module Typhoeus
7
+ module Chain
8
+ def self.instrument!
9
+ ::Typhoeus::Hydra.class_eval do
10
+ include NewRelic::Agent::Instrumentation::Typhoeus
11
+
12
+ def run_with_newrelic(*args)
13
+ with_tracing { run_without_newrelic(*args) }
14
+ end
15
+
16
+ alias run_without_newrelic run
17
+ alias run run_with_newrelic
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,82 @@
1
+ # encoding: utf-8
2
+ # This file is distributed under New Relic's license terms.
3
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
4
+
5
+ module NewRelic
6
+ module Agent
7
+ module Instrumentation
8
+ module Typhoeus
9
+
10
+ HYDRA_SEGMENT_NAME = "External/Multiple/Typhoeus::Hydra/run"
11
+ NOTICIBLE_ERROR_CLASS = "Typhoeus::Errors::TyphoeusError"
12
+
13
+ EARLIEST_VERSION = Gem::Version.new("0.5.3")
14
+
15
+ def self.is_supported_version?
16
+ Gem::Version.new(::Typhoeus::VERSION) >= EARLIEST_VERSION
17
+ end
18
+
19
+ def self.request_is_hydra_enabled?(request)
20
+ request.respond_to?(:hydra) && request.hydra
21
+ end
22
+
23
+ def self.response_message(response)
24
+ if response.respond_to?(:response_message)
25
+ response.response_message
26
+ elsif response.respond_to?(:return_message)
27
+ response.return_message
28
+ else
29
+ # 0.5.4 seems to have lost xxxx_message methods altogether.
30
+ "timeout"
31
+ end
32
+ end
33
+
34
+ def with_tracing
35
+ segment = NewRelic::Agent::Tracer.start_segment name: HYDRA_SEGMENT_NAME
36
+ instance_variable_set :@__newrelic_hydra_segment, segment
37
+ begin
38
+ yield
39
+ ensure
40
+ segment.finish if segment
41
+ end
42
+ end
43
+
44
+ def self.trace(request)
45
+ state = NewRelic::Agent::Tracer.state
46
+ return unless state.is_execution_traced?
47
+
48
+ wrapped_request = ::NewRelic::Agent::HTTPClients::TyphoeusHTTPRequest.new(request)
49
+
50
+ parent = if request_is_hydra_enabled?(request)
51
+ request.hydra.instance_variable_get(:@__newrelic_hydra_segment)
52
+ end
53
+
54
+ segment = NewRelic::Agent::Tracer.start_external_request_segment(
55
+ library: wrapped_request.type,
56
+ uri: wrapped_request.uri,
57
+ procedure: wrapped_request.method,
58
+ parent: parent
59
+ )
60
+
61
+ segment.add_request_headers wrapped_request
62
+
63
+ callback = Proc.new do
64
+ wrapped_response = HTTPClients::TyphoeusHTTPResponse.new(request.response)
65
+
66
+ segment.process_response_headers wrapped_response
67
+
68
+ if request.response.code == 0
69
+ segment.notice_error NoticibleError.new NOTICIBLE_ERROR_CLASS, response_message(request.response)
70
+ end
71
+
72
+ segment.finish if segment
73
+ end
74
+ request.on_complete.unshift(callback)
75
+
76
+ rescue => e
77
+ NewRelic::Agent.logger.error("Exception during trace setup for Typhoeus request", e)
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end