newrelic_rpm 6.15.0 → 7.0.0
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/CHANGELOG.md +96 -22
- data/README.md +2 -2
- data/lib/new_relic/agent.rb +0 -6
- data/lib/new_relic/agent/autostart.rb +1 -2
- data/lib/new_relic/agent/configuration/default_source.rb +270 -104
- data/lib/new_relic/agent/configuration/manager.rb +2 -2
- data/lib/new_relic/agent/datastores/redis.rb +0 -4
- data/lib/new_relic/agent/distributed_tracing.rb +0 -66
- data/lib/new_relic/agent/instrumentation/active_record_notifications.rb +0 -16
- data/lib/new_relic/agent/instrumentation/bunny.rb +10 -152
- data/lib/new_relic/agent/instrumentation/bunny/chain.rb +45 -0
- data/lib/new_relic/agent/instrumentation/bunny/instrumentation.rb +152 -0
- data/lib/new_relic/agent/instrumentation/bunny/prepend.rb +35 -0
- data/lib/new_relic/agent/instrumentation/curb.rb +9 -241
- data/lib/new_relic/agent/instrumentation/curb/chain.rb +93 -0
- data/lib/new_relic/agent/instrumentation/curb/instrumentation.rb +222 -0
- data/lib/new_relic/agent/instrumentation/curb/prepend.rb +63 -0
- data/lib/new_relic/agent/instrumentation/delayed_job/chain.rb +38 -0
- data/lib/new_relic/agent/instrumentation/delayed_job/instrumentation.rb +53 -0
- data/lib/new_relic/agent/instrumentation/delayed_job/prepend.rb +34 -0
- data/lib/new_relic/agent/instrumentation/delayed_job_instrumentation.rb +8 -50
- data/lib/new_relic/agent/instrumentation/excon.rb +2 -1
- data/lib/new_relic/agent/instrumentation/grape.rb +13 -113
- data/lib/new_relic/agent/instrumentation/grape/chain.rb +25 -0
- data/lib/new_relic/agent/instrumentation/grape/instrumentation.rb +100 -0
- data/lib/new_relic/agent/instrumentation/grape/prepend.rb +17 -0
- data/lib/new_relic/agent/instrumentation/httpclient.rb +8 -30
- data/lib/new_relic/agent/instrumentation/httpclient/chain.rb +25 -0
- data/lib/new_relic/agent/instrumentation/httpclient/instrumentation.rb +38 -0
- data/lib/new_relic/agent/instrumentation/httpclient/prepend.rb +17 -0
- data/lib/new_relic/agent/instrumentation/httprb.rb +29 -0
- data/lib/new_relic/agent/instrumentation/httprb/chain.rb +22 -0
- data/lib/new_relic/agent/instrumentation/httprb/instrumentation.rb +30 -0
- data/lib/new_relic/agent/instrumentation/httprb/prepend.rb +15 -0
- data/lib/new_relic/agent/instrumentation/memcache.rb +54 -69
- data/lib/new_relic/agent/instrumentation/memcache/chain.rb +16 -0
- data/lib/new_relic/agent/instrumentation/memcache/dalli.rb +38 -121
- data/lib/new_relic/agent/instrumentation/memcache/helper.rb +56 -0
- data/lib/new_relic/agent/instrumentation/memcache/instrumentation.rb +88 -0
- data/lib/new_relic/agent/instrumentation/memcache/prepend.rb +88 -0
- data/lib/new_relic/agent/instrumentation/middleware_proxy.rb +2 -0
- data/lib/new_relic/agent/instrumentation/mongo.rb +7 -0
- data/lib/new_relic/agent/instrumentation/net_http.rb +39 -0
- data/lib/new_relic/agent/instrumentation/net_http/chain.rb +25 -0
- data/lib/new_relic/agent/instrumentation/{net_prepend.rb → net_http/instrumentation.rb} +3 -3
- data/lib/new_relic/agent/instrumentation/net_http/prepend.rb +21 -0
- data/lib/new_relic/agent/instrumentation/padrino.rb +18 -53
- data/lib/new_relic/agent/instrumentation/padrino/chain.rb +34 -0
- data/lib/new_relic/agent/instrumentation/padrino/instrumentation.rb +27 -0
- data/lib/new_relic/agent/instrumentation/padrino/prepend.rb +20 -0
- data/lib/new_relic/agent/instrumentation/rack.rb +29 -139
- data/lib/new_relic/agent/instrumentation/rack/chain.rb +57 -0
- data/lib/new_relic/agent/instrumentation/rack/helpers.rb +32 -0
- data/lib/new_relic/agent/instrumentation/rack/instrumentation.rb +73 -0
- data/lib/new_relic/agent/instrumentation/rack/prepend.rb +36 -0
- data/lib/new_relic/agent/instrumentation/rake.rb +13 -154
- data/lib/new_relic/agent/instrumentation/rake/chain.rb +25 -0
- data/lib/new_relic/agent/instrumentation/rake/instrumentation.rb +144 -0
- data/lib/new_relic/agent/instrumentation/rake/prepend.rb +14 -0
- data/lib/new_relic/agent/instrumentation/redis.rb +10 -109
- data/lib/new_relic/agent/instrumentation/redis/chain.rb +34 -0
- data/lib/new_relic/agent/instrumentation/redis/instrumentation.rb +65 -0
- data/lib/new_relic/agent/instrumentation/redis/prepend.rb +24 -0
- data/lib/new_relic/agent/instrumentation/resque.rb +8 -28
- data/lib/new_relic/agent/instrumentation/resque/chain.rb +22 -0
- data/lib/new_relic/agent/instrumentation/resque/instrumentation.rb +33 -0
- data/lib/new_relic/agent/instrumentation/resque/prepend.rb +16 -0
- data/lib/new_relic/agent/instrumentation/sinatra.rb +20 -158
- data/lib/new_relic/agent/instrumentation/sinatra/chain.rb +55 -0
- data/lib/new_relic/agent/instrumentation/sinatra/ignorer.rb +29 -34
- data/lib/new_relic/agent/instrumentation/sinatra/instrumentation.rb +118 -0
- data/lib/new_relic/agent/instrumentation/sinatra/prepend.rb +33 -0
- data/lib/new_relic/agent/instrumentation/typhoeus.rb +10 -89
- data/lib/new_relic/agent/instrumentation/typhoeus/chain.rb +22 -0
- data/lib/new_relic/agent/instrumentation/typhoeus/instrumentation.rb +82 -0
- data/lib/new_relic/agent/instrumentation/typhoeus/prepend.rb +14 -0
- data/lib/new_relic/agent/new_relic_service.rb +3 -12
- data/lib/new_relic/agent/sql_sampler.rb +1 -1
- data/lib/new_relic/agent/transaction.rb +1 -4
- data/lib/new_relic/control/frameworks/rails.rb +11 -9
- data/lib/new_relic/control/instance_methods.rb +1 -0
- data/lib/new_relic/dependency_detection.rb +116 -10
- data/lib/new_relic/noticed_error.rb +1 -5
- data/lib/new_relic/supportability_helper.rb +1 -2
- data/lib/new_relic/version.rb +2 -2
- data/newrelic_rpm.gemspec +1 -1
- metadata +53 -8
- data/cert/cacert.pem +0 -1177
- data/lib/new_relic/agent/instrumentation/http.rb +0 -49
- data/lib/new_relic/agent/instrumentation/net.rb +0 -70
@@ -0,0 +1,35 @@
|
|
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 Bunny::Prepend
|
7
|
+
module Exchange
|
8
|
+
include NewRelic::Agent::Instrumentation::Bunny::Exchange
|
9
|
+
|
10
|
+
def publish payload, opts = {}
|
11
|
+
publish_with_tracing(payload, opts) { super }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module Queue
|
16
|
+
include NewRelic::Agent::Instrumentation::Bunny::Queue
|
17
|
+
|
18
|
+
def pop(opts = {:manual_ack => false}, &block)
|
19
|
+
pop_with_tracing { super }
|
20
|
+
end
|
21
|
+
|
22
|
+
def purge *args
|
23
|
+
purge_with_tracing { super }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module Consumer
|
28
|
+
include NewRelic::Agent::Instrumentation::Bunny::Consumer
|
29
|
+
|
30
|
+
def call *args
|
31
|
+
call_with_tracing(*args) { super }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -2,6 +2,9 @@
|
|
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 'curb/chain'
|
6
|
+
require_relative 'curb/prepend'
|
7
|
+
|
5
8
|
DependencyDetection.defer do
|
6
9
|
named :curb
|
7
10
|
|
@@ -19,246 +22,11 @@ DependencyDetection.defer do
|
|
19
22
|
end
|
20
23
|
|
21
24
|
executes do
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
:_nr_original_on_header,
|
29
|
-
:_nr_original_on_complete,
|
30
|
-
:_nr_original_on_failure,
|
31
|
-
:_nr_serial
|
32
|
-
|
33
|
-
# We have to hook these three methods separately, as they don't use
|
34
|
-
# Curl::Easy#http
|
35
|
-
def http_head_with_newrelic(*args, &blk)
|
36
|
-
self._nr_http_verb = :HEAD
|
37
|
-
http_head_without_newrelic(*args, &blk)
|
38
|
-
end
|
39
|
-
|
40
|
-
|
41
|
-
alias_method :http_head_without_newrelic, :http_head
|
42
|
-
alias_method :http_head, :http_head_with_newrelic
|
43
|
-
|
44
|
-
def http_post_with_newrelic(*args, &blk)
|
45
|
-
self._nr_http_verb = :POST
|
46
|
-
http_post_without_newrelic(*args, &blk)
|
47
|
-
end
|
48
|
-
|
49
|
-
|
50
|
-
alias_method :http_post_without_newrelic, :http_post
|
51
|
-
alias_method :http_post, :http_post_with_newrelic
|
52
|
-
|
53
|
-
def http_put_with_newrelic(*args, &blk)
|
54
|
-
self._nr_http_verb = :PUT
|
55
|
-
http_put_without_newrelic(*args, &blk)
|
56
|
-
end
|
57
|
-
|
58
|
-
|
59
|
-
alias_method :http_put_without_newrelic, :http_put
|
60
|
-
alias_method :http_put, :http_put_with_newrelic
|
61
|
-
|
62
|
-
|
63
|
-
# Hook the #http method to set the verb.
|
64
|
-
def http_with_newrelic verb
|
65
|
-
self._nr_http_verb = verb.to_s.upcase
|
66
|
-
http_without_newrelic( verb )
|
67
|
-
end
|
68
|
-
|
69
|
-
alias_method :http_without_newrelic, :http
|
70
|
-
alias_method :http, :http_with_newrelic
|
71
|
-
|
72
|
-
# Hook the #perform method to mark the request as non-parallel.
|
73
|
-
def perform_with_newrelic
|
74
|
-
self._nr_http_verb ||= :GET
|
75
|
-
self._nr_serial = true
|
76
|
-
perform_without_newrelic
|
77
|
-
end
|
78
|
-
|
79
|
-
alias_method :perform_without_newrelic, :perform
|
80
|
-
alias_method :perform, :perform_with_newrelic
|
81
|
-
|
82
|
-
# Record the HTTP verb for future #perform calls
|
83
|
-
def method_with_newrelic verb
|
84
|
-
self._nr_http_verb = verb.upcase
|
85
|
-
method_without_newrelic(verb)
|
86
|
-
end
|
87
|
-
|
88
|
-
alias_method :method_without_newrelic, :method
|
89
|
-
alias_method :method, :method_with_newrelic
|
90
|
-
|
91
|
-
# We override this method in order to ensure access to header_str even
|
92
|
-
# though we use an on_header callback
|
93
|
-
def header_str_with_newrelic
|
94
|
-
if self._nr_serial
|
95
|
-
self._nr_header_str
|
96
|
-
else
|
97
|
-
# Since we didn't install a header callback for a non-serial request,
|
98
|
-
# just fall back to the original implementation.
|
99
|
-
header_str_without_newrelic
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
alias_method :header_str_without_newrelic, :header_str
|
104
|
-
alias_method :header_str, :header_str_with_newrelic
|
105
|
-
end # class Curl::Easy
|
106
|
-
|
107
|
-
|
108
|
-
class Curl::Multi
|
109
|
-
include NewRelic::Agent::MethodTracer
|
110
|
-
|
111
|
-
# Add CAT with callbacks if the request is serial
|
112
|
-
def add_with_newrelic(curl)
|
113
|
-
if curl.respond_to?(:_nr_serial) && curl._nr_serial
|
114
|
-
hook_pending_request(curl) if NewRelic::Agent::Tracer.tracing_enabled?
|
115
|
-
end
|
116
|
-
|
117
|
-
return add_without_newrelic curl
|
118
|
-
end
|
119
|
-
|
120
|
-
alias_method :add_without_newrelic, :add
|
121
|
-
alias_method :add, :add_with_newrelic
|
122
|
-
|
123
|
-
# Trace as an External/Multiple call if the first request isn't serial.
|
124
|
-
def perform_with_newrelic(&blk)
|
125
|
-
return perform_without_newrelic if first_request_is_serial?
|
126
|
-
|
127
|
-
trace_execution_scoped("External/Multiple/Curb::Multi/perform") do
|
128
|
-
perform_without_newrelic(&blk)
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
alias_method :perform_without_newrelic, :perform
|
133
|
-
alias_method :perform, :perform_with_newrelic
|
134
|
-
|
135
|
-
|
136
|
-
# Instrument the specified +request+ (a Curl::Easy object)
|
137
|
-
# and set up cross-application tracing if it's enabled.
|
138
|
-
def hook_pending_request(request)
|
139
|
-
wrapped_request, wrapped_response = wrap_request(request)
|
140
|
-
|
141
|
-
segment = NewRelic::Agent::Tracer.start_external_request_segment(
|
142
|
-
library: wrapped_request.type,
|
143
|
-
uri: wrapped_request.uri,
|
144
|
-
procedure: wrapped_request.method
|
145
|
-
)
|
146
|
-
|
147
|
-
segment.add_request_headers wrapped_request
|
148
|
-
|
149
|
-
# install all callbacks
|
150
|
-
unless request._nr_instrumented
|
151
|
-
install_header_callback(request, wrapped_response)
|
152
|
-
install_completion_callback(request, wrapped_response, segment)
|
153
|
-
install_failure_callback(request, wrapped_response, segment)
|
154
|
-
request._nr_instrumented = true
|
155
|
-
end
|
156
|
-
rescue => err
|
157
|
-
NewRelic::Agent.logger.error("Untrapped exception", err)
|
158
|
-
end
|
159
|
-
|
160
|
-
|
161
|
-
# Create request and response adapter objects for the specified +request+
|
162
|
-
# NOTE: Although strange to wrap request and response at once, it works
|
163
|
-
# because curb's callback mechanism updates the instantiated wrappers
|
164
|
-
# during the life-cycle of external request
|
165
|
-
def wrap_request(request)
|
166
|
-
return NewRelic::Agent::HTTPClients::CurbRequest.new(request),
|
167
|
-
NewRelic::Agent::HTTPClients::CurbResponse.new(request)
|
168
|
-
end
|
169
|
-
|
170
|
-
# Install a callback that will record the response headers
|
171
|
-
# to enable CAT linking
|
172
|
-
def install_header_callback(request, wrapped_response)
|
173
|
-
original_callback = request.on_header
|
174
|
-
request._nr_original_on_header = original_callback
|
175
|
-
request._nr_header_str = nil
|
176
|
-
request.on_header do |header_data|
|
177
|
-
if original_callback
|
178
|
-
original_callback.call header_data
|
179
|
-
else
|
180
|
-
wrapped_response.append_header_data header_data
|
181
|
-
header_data.length
|
182
|
-
end
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
# Install a callback that will finish the trace.
|
187
|
-
def install_completion_callback(request, wrapped_response, segment)
|
188
|
-
original_callback = request.on_complete
|
189
|
-
request._nr_original_on_complete = original_callback
|
190
|
-
request.on_complete do |finished_request|
|
191
|
-
begin
|
192
|
-
segment.process_response_headers wrapped_response
|
193
|
-
ensure
|
194
|
-
segment.finish if segment
|
195
|
-
# Make sure the existing completion callback is run, and restore the
|
196
|
-
# on_complete callback to how it was before.
|
197
|
-
original_callback.call(finished_request) if original_callback
|
198
|
-
remove_instrumentation_callbacks(request)
|
199
|
-
end
|
200
|
-
end
|
201
|
-
end
|
202
|
-
|
203
|
-
# Install a callback that will fire on failures
|
204
|
-
# NOTE: on_failure is not always called, so we're not always
|
205
|
-
# unhooking the callback. No harm/no foul in production, but
|
206
|
-
# definitely something to beware of if debugging callback issues
|
207
|
-
# _nr_failure_instrumented exists to prevent infinitely chaining
|
208
|
-
# our on_failure callback hook.
|
209
|
-
def install_failure_callback(request, wrapped_response, segment)
|
210
|
-
return if request._nr_failure_instrumented
|
211
|
-
original_callback = request.on_failure
|
212
|
-
request._nr_original_on_failure = original_callback
|
213
|
-
request.on_failure do |failed_request, error|
|
214
|
-
begin
|
215
|
-
if segment
|
216
|
-
noticible_error = NewRelic::Agent::NoticibleError.new error[0].name, error[-1]
|
217
|
-
segment.notice_error noticible_error
|
218
|
-
end
|
219
|
-
ensure
|
220
|
-
original_callback.call(failed_request, error) if original_callback
|
221
|
-
remove_failure_callback(failed_request)
|
222
|
-
end
|
223
|
-
request._nr_failure_instrumented = true
|
224
|
-
end
|
225
|
-
end
|
226
|
-
|
227
|
-
# on_failure callbacks cannot be removed in the on_complete
|
228
|
-
# callback where this method is invoked because on_complete
|
229
|
-
# fires before the on_failure!
|
230
|
-
def remove_instrumentation_callbacks(request)
|
231
|
-
request.on_complete(&request._nr_original_on_complete)
|
232
|
-
request.on_header(&request._nr_original_on_header)
|
233
|
-
request._nr_instrumented = false
|
234
|
-
end
|
235
|
-
|
236
|
-
# We execute customer's on_failure callback (if any) and
|
237
|
-
# uninstall our hook here since the on_complete callback
|
238
|
-
# fires before the on_failure callback.
|
239
|
-
def remove_failure_callback(request)
|
240
|
-
request.on_failure(&request._nr_original_on_failure)
|
241
|
-
request._nr_failure_instrumented = false
|
242
|
-
end
|
243
|
-
|
244
|
-
private
|
245
|
-
|
246
|
-
def first_request_is_serial?
|
247
|
-
return false unless (first = self.requests.first)
|
248
|
-
|
249
|
-
# Before curb 0.9.8, requests was an array of Curl::Easy
|
250
|
-
# instances. Starting with 0.9.8, it's a Hash where the
|
251
|
-
# values are Curl::Easy instances.
|
252
|
-
#
|
253
|
-
# So, requests.first will either be an_obj or [a_key, an_obj].
|
254
|
-
# We need to handle either case.
|
255
|
-
#
|
256
|
-
first = first[-1] if first.is_a?(Array)
|
257
|
-
|
258
|
-
first.respond_to?(:_nr_serial) && first._nr_serial
|
259
|
-
end
|
260
|
-
|
261
|
-
end # class Curl::Multi
|
262
|
-
|
25
|
+
if use_prepend?
|
26
|
+
prepend_instrument ::Curl::Easy, NewRelic::Agent::Instrumentation::Curb::Easy::Prepend
|
27
|
+
prepend_instrument ::Curl::Multi, NewRelic::Agent::Instrumentation::Curb::Multi::Prepend
|
28
|
+
else
|
29
|
+
chain_instrument ::NewRelic::Agent::Instrumentation::Curb::Chain
|
30
|
+
end
|
263
31
|
end
|
264
32
|
end
|
@@ -0,0 +1,93 @@
|
|
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
|
+
# frozen_string_literal: true
|
5
|
+
|
6
|
+
require_relative 'instrumentation'
|
7
|
+
|
8
|
+
module NewRelic::Agent::Instrumentation
|
9
|
+
module Curb
|
10
|
+
module Chain
|
11
|
+
def self.instrument!
|
12
|
+
Curl::Easy.class_eval do
|
13
|
+
include NewRelic::Agent::Instrumentation::Curb::Easy
|
14
|
+
|
15
|
+
def http_head_with_newrelic(*args, &blk)
|
16
|
+
http_head_with_tracing { http_head_without_newrelic(*args, &blk) }
|
17
|
+
end
|
18
|
+
|
19
|
+
alias_method :http_head_without_newrelic, :http_head
|
20
|
+
alias_method :http_head, :http_head_with_newrelic
|
21
|
+
|
22
|
+
def http_post_with_newrelic(*args, &blk)
|
23
|
+
http_post_with_tracing { http_post_without_newrelic(*args, &blk) }
|
24
|
+
end
|
25
|
+
|
26
|
+
alias_method :http_post_without_newrelic, :http_post
|
27
|
+
alias_method :http_post, :http_post_with_newrelic
|
28
|
+
|
29
|
+
def http_put_with_newrelic(*args, &blk)
|
30
|
+
http_put_with_tracing { http_put_without_newrelic(*args, &blk) }
|
31
|
+
end
|
32
|
+
|
33
|
+
alias_method :http_put_without_newrelic, :http_put
|
34
|
+
alias_method :http_put, :http_put_with_newrelic
|
35
|
+
|
36
|
+
# Hook the #http method to set the verb.
|
37
|
+
def http_with_newrelic verb
|
38
|
+
http_with_tracing(verb) { http_without_newrelic( verb ) }
|
39
|
+
end
|
40
|
+
|
41
|
+
alias_method :http_without_newrelic, :http
|
42
|
+
alias_method :http, :http_with_newrelic
|
43
|
+
|
44
|
+
# Hook the #perform method to mark the request as non-parallel.
|
45
|
+
def perform_with_newrelic
|
46
|
+
perform_with_tracing { perform_without_newrelic }
|
47
|
+
end
|
48
|
+
|
49
|
+
alias_method :perform_without_newrelic, :perform
|
50
|
+
alias_method :perform, :perform_with_newrelic
|
51
|
+
|
52
|
+
# Record the HTTP verb for future #perform calls
|
53
|
+
def method_with_newrelic verb
|
54
|
+
method_with_tracing { method_without_newrelic(verb) }
|
55
|
+
end
|
56
|
+
|
57
|
+
alias_method :method_without_newrelic, :method
|
58
|
+
alias_method :method, :method_with_newrelic
|
59
|
+
|
60
|
+
# We override this method in order to ensure access to header_str even
|
61
|
+
# though we use an on_header callback
|
62
|
+
def header_str_with_newrelic
|
63
|
+
header_str_with_tracing { header_str_without_newrelic }
|
64
|
+
end
|
65
|
+
|
66
|
+
alias_method :header_str_without_newrelic, :header_str
|
67
|
+
alias_method :header_str, :header_str_with_newrelic
|
68
|
+
end
|
69
|
+
|
70
|
+
Curl::Multi.class_eval do
|
71
|
+
include NewRelic::Agent::Instrumentation::Curb::Multi
|
72
|
+
|
73
|
+
# Add CAT with callbacks if the request is serial
|
74
|
+
def add_with_newrelic(curl)
|
75
|
+
add_with_tracing(curl) { add_without_newrelic curl }
|
76
|
+
end
|
77
|
+
|
78
|
+
alias_method :add_without_newrelic, :add
|
79
|
+
alias_method :add, :add_with_newrelic
|
80
|
+
|
81
|
+
# Trace as an External/Multiple call if the first request isn't serial.
|
82
|
+
def perform_with_newrelic(&blk)
|
83
|
+
perform_with_tracing { perform_without_newrelic(&blk) }
|
84
|
+
end
|
85
|
+
|
86
|
+
alias_method :perform_without_newrelic, :perform
|
87
|
+
alias_method :perform, :perform_with_newrelic
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,222 @@
|
|
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
|
+
# frozen_string_literal: true
|
5
|
+
|
6
|
+
module NewRelic
|
7
|
+
module Agent
|
8
|
+
module Instrumentation
|
9
|
+
module Curb
|
10
|
+
module Easy
|
11
|
+
attr_accessor :_nr_instrumented,
|
12
|
+
:_nr_failure_instrumented,
|
13
|
+
:_nr_http_verb,
|
14
|
+
:_nr_header_str,
|
15
|
+
:_nr_original_on_header,
|
16
|
+
:_nr_original_on_complete,
|
17
|
+
:_nr_original_on_failure,
|
18
|
+
:_nr_serial
|
19
|
+
|
20
|
+
# We have to hook these three methods separately, as they don't use
|
21
|
+
# Curl::Easy#http
|
22
|
+
def http_head_with_tracing
|
23
|
+
self._nr_http_verb = :HEAD
|
24
|
+
yield
|
25
|
+
end
|
26
|
+
|
27
|
+
def http_post_with_tracing
|
28
|
+
self._nr_http_verb = :POST
|
29
|
+
yield
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
def http_put_with_tracing
|
34
|
+
self._nr_http_verb = :PUT
|
35
|
+
yield
|
36
|
+
end
|
37
|
+
|
38
|
+
# Hook the #http method to set the verb.
|
39
|
+
def http_with_tracing verb
|
40
|
+
self._nr_http_verb = verb.to_s.upcase
|
41
|
+
yield
|
42
|
+
end
|
43
|
+
|
44
|
+
# Hook the #perform method to mark the request as non-parallel.
|
45
|
+
def perform_with_tracing
|
46
|
+
self._nr_http_verb ||= :GET
|
47
|
+
self._nr_serial = true
|
48
|
+
yield
|
49
|
+
end
|
50
|
+
|
51
|
+
# Record the HTTP verb for future #perform calls
|
52
|
+
def method_with_tracing verb
|
53
|
+
self._nr_http_verb = verb.upcase
|
54
|
+
yield
|
55
|
+
end
|
56
|
+
|
57
|
+
# We override this method in order to ensure access to header_str even
|
58
|
+
# though we use an on_header callback
|
59
|
+
def header_str_with_tracing
|
60
|
+
if self._nr_serial
|
61
|
+
self._nr_header_str
|
62
|
+
else
|
63
|
+
# Since we didn't install a header callback for a non-serial request,
|
64
|
+
# just fall back to the original implementation.
|
65
|
+
yield
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
####################################################
|
71
|
+
|
72
|
+
module Multi
|
73
|
+
include NewRelic::Agent::MethodTracer
|
74
|
+
|
75
|
+
# Add CAT with callbacks if the request is serial
|
76
|
+
def add_with_tracing(curl)
|
77
|
+
if curl.respond_to?(:_nr_serial) && curl._nr_serial
|
78
|
+
hook_pending_request(curl) if NewRelic::Agent::Tracer.tracing_enabled?
|
79
|
+
end
|
80
|
+
|
81
|
+
return yield
|
82
|
+
end
|
83
|
+
|
84
|
+
# Trace as an External/Multiple call if the first request isn't serial.
|
85
|
+
def perform_with_tracing
|
86
|
+
return yield if first_request_is_serial?
|
87
|
+
|
88
|
+
trace_execution_scoped("External/Multiple/Curb::Multi/perform") do
|
89
|
+
yield
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Instrument the specified +request+ (a Curl::Easy object)
|
94
|
+
# and set up cross-application tracing if it's enabled.
|
95
|
+
def hook_pending_request(request)
|
96
|
+
wrapped_request, wrapped_response = wrap_request(request)
|
97
|
+
|
98
|
+
segment = NewRelic::Agent::Tracer.start_external_request_segment(
|
99
|
+
library: wrapped_request.type,
|
100
|
+
uri: wrapped_request.uri,
|
101
|
+
procedure: wrapped_request.method
|
102
|
+
)
|
103
|
+
|
104
|
+
segment.add_request_headers wrapped_request
|
105
|
+
|
106
|
+
# install all callbacks
|
107
|
+
unless request._nr_instrumented
|
108
|
+
install_header_callback(request, wrapped_response)
|
109
|
+
install_completion_callback(request, wrapped_response, segment)
|
110
|
+
install_failure_callback(request, wrapped_response, segment)
|
111
|
+
request._nr_instrumented = true
|
112
|
+
end
|
113
|
+
rescue => err
|
114
|
+
NewRelic::Agent.logger.error("Untrapped exception", err)
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
# Create request and response adapter objects for the specified +request+
|
119
|
+
# NOTE: Although strange to wrap request and response at once, it works
|
120
|
+
# because curb's callback mechanism updates the instantiated wrappers
|
121
|
+
# during the life-cycle of external request
|
122
|
+
def wrap_request(request)
|
123
|
+
return NewRelic::Agent::HTTPClients::CurbRequest.new(request),
|
124
|
+
NewRelic::Agent::HTTPClients::CurbResponse.new(request)
|
125
|
+
end
|
126
|
+
|
127
|
+
# Install a callback that will record the response headers
|
128
|
+
# to enable CAT linking
|
129
|
+
def install_header_callback(request, wrapped_response)
|
130
|
+
original_callback = request.on_header
|
131
|
+
request._nr_original_on_header = original_callback
|
132
|
+
request._nr_header_str = nil
|
133
|
+
request.on_header do |header_data|
|
134
|
+
if original_callback
|
135
|
+
original_callback.call header_data
|
136
|
+
else
|
137
|
+
wrapped_response.append_header_data header_data
|
138
|
+
header_data.length
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# Install a callback that will finish the trace.
|
144
|
+
def install_completion_callback(request, wrapped_response, segment)
|
145
|
+
original_callback = request.on_complete
|
146
|
+
request._nr_original_on_complete = original_callback
|
147
|
+
request.on_complete do |finished_request|
|
148
|
+
begin
|
149
|
+
segment.process_response_headers wrapped_response
|
150
|
+
ensure
|
151
|
+
segment.finish if segment
|
152
|
+
# Make sure the existing completion callback is run, and restore the
|
153
|
+
# on_complete callback to how it was before.
|
154
|
+
original_callback.call(finished_request) if original_callback
|
155
|
+
remove_instrumentation_callbacks(request)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# Install a callback that will fire on failures
|
161
|
+
# NOTE: on_failure is not always called, so we're not always
|
162
|
+
# unhooking the callback. No harm/no foul in production, but
|
163
|
+
# definitely something to beware of if debugging callback issues
|
164
|
+
# _nr_failure_instrumented exists to prevent infinitely chaining
|
165
|
+
# our on_failure callback hook.
|
166
|
+
def install_failure_callback(request, wrapped_response, segment)
|
167
|
+
return if request._nr_failure_instrumented
|
168
|
+
original_callback = request.on_failure
|
169
|
+
request._nr_original_on_failure = original_callback
|
170
|
+
request.on_failure do |failed_request, error|
|
171
|
+
begin
|
172
|
+
if segment
|
173
|
+
noticible_error = NewRelic::Agent::NoticibleError.new error[0].name, error[-1]
|
174
|
+
segment.notice_error noticible_error
|
175
|
+
end
|
176
|
+
ensure
|
177
|
+
original_callback.call(failed_request, error) if original_callback
|
178
|
+
remove_failure_callback(failed_request)
|
179
|
+
end
|
180
|
+
request._nr_failure_instrumented = true
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
# on_failure callbacks cannot be removed in the on_complete
|
185
|
+
# callback where this method is invoked because on_complete
|
186
|
+
# fires before the on_failure!
|
187
|
+
def remove_instrumentation_callbacks(request)
|
188
|
+
request.on_complete(&request._nr_original_on_complete)
|
189
|
+
request.on_header(&request._nr_original_on_header)
|
190
|
+
request._nr_instrumented = false
|
191
|
+
end
|
192
|
+
|
193
|
+
# We execute customer's on_failure callback (if any) and
|
194
|
+
# uninstall our hook here since the on_complete callback
|
195
|
+
# fires before the on_failure callback.
|
196
|
+
def remove_failure_callback(request)
|
197
|
+
request.on_failure(&request._nr_original_on_failure)
|
198
|
+
request._nr_failure_instrumented = false
|
199
|
+
end
|
200
|
+
|
201
|
+
private
|
202
|
+
|
203
|
+
def first_request_is_serial?
|
204
|
+
return false unless (first = self.requests.first)
|
205
|
+
|
206
|
+
# Before curb 0.9.8, requests was an array of Curl::Easy
|
207
|
+
# instances. Starting with 0.9.8, it's a Hash where the
|
208
|
+
# values are Curl::Easy instances.
|
209
|
+
#
|
210
|
+
# So, requests.first will either be an_obj or [a_key, an_obj].
|
211
|
+
# We need to handle either case.
|
212
|
+
#
|
213
|
+
first = first[-1] if first.is_a?(Array)
|
214
|
+
|
215
|
+
first.respond_to?(:_nr_serial) && first._nr_serial
|
216
|
+
end
|
217
|
+
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|