hoodoo 1.7.0 → 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +13 -5
- data/lib/hoodoo.rb +3 -0
- data/lib/hoodoo/client/client.rb +7 -3
- data/lib/hoodoo/client/endpoint/endpoints/amqp.rb +24 -1
- data/lib/hoodoo/monkey.rb +13 -0
- data/lib/hoodoo/monkey/monkey.rb +308 -0
- data/lib/hoodoo/monkey/patch/newrelic_traced_amqp.rb +195 -0
- data/lib/hoodoo/version.rb +1 -1
- data/spec/client/client_spec.rb +17 -0
- data/spec/monkey/monkey_spec.rb +265 -0
- data/spec/monkey/patch/newrelic_traced_amqp_spec.rb +160 -0
- data/spec/newrelic_rpm.rb +17 -0
- data/spec/services/middleware/middleware_exotic_communication_spec.rb +5 -456
- data/spec/shared_examples/middleware_amqp.rb +448 -0
- data/spec/spec_helper.rb +14 -0
- metadata +52 -41
checksums.yaml
CHANGED
@@ -1,7 +1,15 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
MzkyZDI3OGE3NDg2ZDBiODFkZGZlNzhiZGY0YTgzN2FhZDU5OTg4ZA==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NTQzZGI1NjFhMjU3Y2Y4NTM4MDM0YWJiNGI2MDQyZTQ1NDdmYzIzNQ==
|
5
7
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
ZjA5YzEwZmFlYWNhMjU0YjcyZTAzMTMzODA5MDZiMzk3NDc3ODBlYWViMGRh
|
10
|
+
MjQ3MWQ2NTA2NDAxYmUyNzBjYjBhNjIzMzNkMGI2NDZlY2ZlZWU3NDk2MDky
|
11
|
+
ZGI0MDVlZmRhZjM4MWI4ZGY4NDNjOTIwODQwOWM3YmYyOTAyNTg=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
MDAyNmY0YzY4MDZiMjY4ZDlkODg3MzRhNzFhNzFjNjEwZjNmZmI3ZDMyM2U3
|
14
|
+
N2E4YWU4YTA3OTM4ZjJlYzhiODM3NWRiYzFlYzgxNWYzZjE3OGY1MTIyOGQ2
|
15
|
+
ZTRlZGUzOTkxNGM3ZmVjNTA3NTQ2NzljNTMwYmNjNDVjZjI0MDM=
|
data/lib/hoodoo.rb
CHANGED
data/lib/hoodoo/client/client.rb
CHANGED
@@ -262,9 +262,13 @@ module Hoodoo
|
|
262
262
|
@discoverer = if discoverer != nil
|
263
263
|
discoverer
|
264
264
|
elsif @base_uri != nil
|
265
|
-
Hoodoo::Services::Discovery::ByConvention
|
266
|
-
|
267
|
-
|
265
|
+
if defined?( Hoodoo::Services::Discovery::ByConvention )
|
266
|
+
Hoodoo::Services::Discovery::ByConvention.new(
|
267
|
+
:base_uri => @base_uri
|
268
|
+
)
|
269
|
+
else
|
270
|
+
raise 'Hoodoo::Client: The constructor parameters indicate the use of a "by convention" discoverer. This discoverer requires ActiveSupport; ensure the ActiveSupport gem is present and "require"-able.'
|
271
|
+
end
|
268
272
|
elsif @drb_uri != nil || @drb_port != nil
|
269
273
|
Hoodoo::Services::Discovery::ByDRb.new(
|
270
274
|
:drb_uri => @drb_uri,
|
@@ -113,6 +113,28 @@ module Hoodoo
|
|
113
113
|
return do_amqp( d )
|
114
114
|
end
|
115
115
|
|
116
|
+
# Ask Alchemy Flux to send a given HTTP message to a resource.
|
117
|
+
#
|
118
|
+
# This method is available for Hoodoo monkey patching but must not
|
119
|
+
# be called by third party code; it's a private method exposed in
|
120
|
+
# the public <tt>monkey_</tt> namespace for patching only. For more,
|
121
|
+
# see:
|
122
|
+
#
|
123
|
+
# * Hoodoo::Monkey
|
124
|
+
# * Hoodoo::Monkey::Patch::NewRelicTracedAMQP
|
125
|
+
#
|
126
|
+
# +http_message+:: Hash describing the message to send.
|
127
|
+
# +full_uri+:: Equivalent full URI of the request (information
|
128
|
+
# only; the +http_message+ tells Alchemy Flux how
|
129
|
+
# to route the message; it does not consult this
|
130
|
+
# parameter).
|
131
|
+
#
|
132
|
+
# The return value is an Alchemy Flux response object.
|
133
|
+
#
|
134
|
+
def monkey_send_request( http_message, full_uri )
|
135
|
+
self.alchemy().send_request_to_resource( http_message )
|
136
|
+
end
|
137
|
+
|
116
138
|
private
|
117
139
|
|
118
140
|
# Call Alchemy to make an HTTP simulated request over AMQP to a
|
@@ -164,7 +186,7 @@ module Hoodoo
|
|
164
186
|
http_message[ 'session_id' ] = self.session_id()
|
165
187
|
end
|
166
188
|
|
167
|
-
amqp_response =
|
189
|
+
amqp_response = monkey_send_request( http_message, data.full_uri )
|
168
190
|
|
169
191
|
description_of_response = DescriptionOfResponse.new
|
170
192
|
description_of_response.action = action
|
@@ -193,6 +215,7 @@ module Hoodoo
|
|
193
215
|
end
|
194
216
|
|
195
217
|
end
|
218
|
+
|
196
219
|
end
|
197
220
|
end
|
198
221
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
########################################################################
|
2
|
+
# File:: monkey.rb
|
3
|
+
# (C):: Loyalty New Zealand 2016
|
4
|
+
#
|
5
|
+
# Purpose:: Require the Hoodoo monkey patching engine and any built-in
|
6
|
+
# patches, which will enable themselves (or not) according to
|
7
|
+
# their individual RDoc-documented descriptions.
|
8
|
+
# ----------------------------------------------------------------------
|
9
|
+
# 12-Apr-2016 (ADH): Created.
|
10
|
+
########################################################################
|
11
|
+
|
12
|
+
require 'hoodoo/monkey/monkey'
|
13
|
+
require 'hoodoo/monkey/patch/newrelic_traced_amqp'
|
@@ -0,0 +1,308 @@
|
|
1
|
+
########################################################################
|
2
|
+
# File:: monkey.rb
|
3
|
+
# (C):: Loyalty New Zealand 2016
|
4
|
+
#
|
5
|
+
# Purpose:: Official, reversible monkey patching.
|
6
|
+
# ----------------------------------------------------------------------
|
7
|
+
# 11-Apr-2016 (ADH): Created.
|
8
|
+
########################################################################
|
9
|
+
|
10
|
+
module Hoodoo
|
11
|
+
|
12
|
+
# Hoodoo provides monkey patching hook points as first class citizens and
|
13
|
+
# includes a registration, enabling and disabling mechanism through the
|
14
|
+
# Hoodoo::Monkey class.
|
15
|
+
#
|
16
|
+
# You encapsulate monkey patch code inside a module. This module will be
|
17
|
+
# used to patch one or more target other classes or modules. Usually, one
|
18
|
+
# module will only be used to patch one other kind of class or module;
|
19
|
+
# re-use of a patch module usually only makes sense when patching one
|
20
|
+
# or more subclasses from a common ancestor where some, but not all of
|
21
|
+
# the subclass types are to be patched (if you wanted to patch all of them
|
22
|
+
# you'd just patch the base class).
|
23
|
+
#
|
24
|
+
# Inside your module, you write one or two sub-modules. One of these
|
25
|
+
# patches instance methods in the target, the other patches class methods.
|
26
|
+
# The mechanism used to patch instance or class methods is different in
|
27
|
+
# Ruby, thus the distinct module use; it also helps keep your code clear
|
28
|
+
# of distracting syntax and make it very obvious what kind of "thing" is
|
29
|
+
# being replaced.
|
30
|
+
#
|
31
|
+
# Monkey patch methods are sent to the patch target using `prepend`, the
|
32
|
+
# Ruby 2 mechanism which means the original overriden implementation can
|
33
|
+
# be called via +super+, just as if you were writing a subclass.
|
34
|
+
#
|
35
|
+
# For examples, see method Hoodoo::Monkey::register.
|
36
|
+
#
|
37
|
+
# Any public method in the API can be patched, since the public API is by
|
38
|
+
# definition public and stable. Sometimes, normally-private methods are
|
39
|
+
# exposed for monkey patching as public methods with the name prefix of
|
40
|
+
# "<tt>monkey_</tt>" - such methods are *NOT* intended to be called by
|
41
|
+
# client code in general, but can be patched. It is only completely safe to
|
42
|
+
# to patch a method in a wrapper fashion, e.g. to filter inputs or outputs;
|
43
|
+
# thus whenever possible, always call +super+ at some point within your
|
44
|
+
# replacement implementation. If you completely replace an implementation
|
45
|
+
# with a custom version, you risk your code breaking even with patch level
|
46
|
+
# changes to Hoodoo, since only the public _interface_ is guaranteed; the
|
47
|
+
# way in which it is _implemented_ is not.
|
48
|
+
#
|
49
|
+
# You tell the monkey patching system about the outer container module, the
|
50
|
+
# instance and/or class patch modules and the target entity via a call to
|
51
|
+
# Hoodoo::Monkey::register. See this for more details. Use
|
52
|
+
# Hoodoo::Monkey::enable to actually 'switch on' the patch and
|
53
|
+
# Hoodoo::Monkey::disable to 'switch off' the patch again.
|
54
|
+
#
|
55
|
+
# The patch engine is "require'd" by Hoodoo as the very last thing in all of
|
56
|
+
# its other inclusion steps when 'hoodoo.rb' ("everything") is included by
|
57
|
+
# code. If individual sub-modules of Hoodoo are included by client code, it
|
58
|
+
# will be up to them when (and if) the monkey patch engine is brought in.
|
59
|
+
#
|
60
|
+
# Hoodoo authors should note namespaces Hoodoo::Monkey::Patch and
|
61
|
+
# Hoodoo::Monkey::Chaos inside which out-of-the-box Hoodoo patch code should
|
62
|
+
# be defined. Third party patches must use their own namespaces to avoid any
|
63
|
+
# potential for collision with future new Hoodoo patch modules.
|
64
|
+
#
|
65
|
+
module Monkey
|
66
|
+
@@modules = {}
|
67
|
+
|
68
|
+
# Register a set of monkey patch modules with Hoodoo::Monkey - see the
|
69
|
+
# top-level Hoodoo::Monkey documentation for an introduction and some
|
70
|
+
# high level guidelines for monkey patch code.
|
71
|
+
#
|
72
|
+
# _Named_ parameters are:
|
73
|
+
#
|
74
|
+
# +target_unit+:: The Class or Module to be patched.
|
75
|
+
# +extension_module+:: The module that identifies the collection of
|
76
|
+
# instance and/or class methods to overwrite
|
77
|
+
# inside the targeted unit. This MUST define
|
78
|
+
# a nested module called "InstanceExtensions"
|
79
|
+
# containing method definitions that will
|
80
|
+
# override same-name instance methods in the
|
81
|
+
# targeted unit, or a nested module called
|
82
|
+
# "ClassExtensions" to override class methods,
|
83
|
+
# or both.
|
84
|
+
#
|
85
|
+
# For example, suppose we have this class:
|
86
|
+
#
|
87
|
+
# class Foo
|
88
|
+
# def bar
|
89
|
+
# 2 * 2
|
90
|
+
# end
|
91
|
+
#
|
92
|
+
# def self.bar
|
93
|
+
# 3 * 3
|
94
|
+
# end
|
95
|
+
# end
|
96
|
+
#
|
97
|
+
# Foo.new.bar
|
98
|
+
# # => 4
|
99
|
+
# Foo.bar
|
100
|
+
# # => 9
|
101
|
+
#
|
102
|
+
# Next define modules which extend/override methods in the above class:
|
103
|
+
#
|
104
|
+
# module ExtendedFoo
|
105
|
+
# module InstanceExtensions
|
106
|
+
# def bar
|
107
|
+
# 5 * 5
|
108
|
+
# end
|
109
|
+
# end
|
110
|
+
#
|
111
|
+
# module ClassExtensions
|
112
|
+
#
|
113
|
+
# # Even though this module will be used to override class methods
|
114
|
+
# # in the target, we define the module methods with "def bar", not
|
115
|
+
# # "def self.bar".
|
116
|
+
# #
|
117
|
+
# def bar
|
118
|
+
# 7 * 7
|
119
|
+
# end
|
120
|
+
# end
|
121
|
+
# end
|
122
|
+
#
|
123
|
+
# At this point, the extension is defined, but not registered with Hoodoo
|
124
|
+
# and not yet enabled. Register it with:
|
125
|
+
#
|
126
|
+
# Hoodoo::Monkey.register(
|
127
|
+
# target_unit: Foo,
|
128
|
+
# extension_module: ExtendedFoo
|
129
|
+
# )
|
130
|
+
#
|
131
|
+
# The code is now registered so that it can be easily enabled or disabled
|
132
|
+
# via the given +extension_module+ value:
|
133
|
+
#
|
134
|
+
# Hoodoo::Monkey.enable( ExtendedFoo )
|
135
|
+
#
|
136
|
+
# Foo.new.bar
|
137
|
+
# # => 25
|
138
|
+
# Foo.bar
|
139
|
+
# # => 49
|
140
|
+
#
|
141
|
+
# Hoodoo::Monkey.disable( ExtendedFoo )
|
142
|
+
#
|
143
|
+
# Foo.new.bar
|
144
|
+
# # => 4
|
145
|
+
# Foo.bar
|
146
|
+
# # => 9
|
147
|
+
#
|
148
|
+
# You can register the same extension modules for multiple target units,
|
149
|
+
# but it can only be enabled or disabled all in one go for all targets.
|
150
|
+
#
|
151
|
+
def self.register( target_unit:, extension_module: )
|
152
|
+
|
153
|
+
if extension_module.const_defined?( 'InstanceExtensions', false )
|
154
|
+
instance_methods_module = extension_module.const_get( 'InstanceExtensions' )
|
155
|
+
end
|
156
|
+
|
157
|
+
if extension_module.const_defined?( 'ClassExtensions', false )
|
158
|
+
class_methods_module = extension_module.const_get( 'ClassExtensions' )
|
159
|
+
end
|
160
|
+
|
161
|
+
if instance_methods_module.nil? && class_methods_module.nil?
|
162
|
+
raise "Hoodoo::Monkey::register: You must define either an InstanceExtensions module ClassExtensions module or both inside '#{ extension_module.inspect }'"
|
163
|
+
end
|
164
|
+
|
165
|
+
@@modules[ extension_module ] ||= {}
|
166
|
+
@@modules[ extension_module ][ target_unit ] =
|
167
|
+
[
|
168
|
+
{
|
169
|
+
:patch_module => instance_methods_module,
|
170
|
+
:patch_target => target_unit
|
171
|
+
},
|
172
|
+
{
|
173
|
+
:patch_module => class_methods_module,
|
174
|
+
:patch_target => target_unit.singleton_class
|
175
|
+
}
|
176
|
+
]
|
177
|
+
|
178
|
+
end
|
179
|
+
|
180
|
+
# Enable a given monkey patch, using the extension module parameter value
|
181
|
+
# given to a prior call to ::register (see there for more information).
|
182
|
+
#
|
183
|
+
# The initial patch installation is done via <tt>Module#prepend</tt>, so
|
184
|
+
# you are able to call +super+ to invoke the original implementation from
|
185
|
+
# the overriding implementation, as if you were writing a subclass.
|
186
|
+
#
|
187
|
+
# Instance and class method monkey patches should try very hard to always
|
188
|
+
# call "super" so that an overridden/patched public API method will still
|
189
|
+
# call back to its original implementation; the wrapper just filters
|
190
|
+
# inputs and outputs or adds additional behaviour. This way, changes to
|
191
|
+
# the Hoodoo implementation will not break the patch.
|
192
|
+
#
|
193
|
+
# Patching is global; it is not lexically scoped. Use Ruby refinements
|
194
|
+
# manually if you want lexically scoped patches.
|
195
|
+
#
|
196
|
+
# _Named_ parameters are:
|
197
|
+
#
|
198
|
+
# +extension_module+:: A module previously passed in the same-named
|
199
|
+
# parameter to ::register. The instance and/or class
|
200
|
+
# methods defined therein will be applied to the
|
201
|
+
# previously registered target.
|
202
|
+
#
|
203
|
+
# Enabling the same extension multiple times has no side effects.
|
204
|
+
#
|
205
|
+
def self.enable( extension_module: )
|
206
|
+
if ( target_units_hash = @@modules[ extension_module ] ).nil?
|
207
|
+
raise "Hoodoo::Monkey::enable: Extension module '#{ extension_module.inspect }' is not registered"
|
208
|
+
end
|
209
|
+
|
210
|
+
target_units_hash.each_value do | target_and_module_array |
|
211
|
+
target_and_module_array.each do | target_and_module_array_entry |
|
212
|
+
patch_module = target_and_module_array_entry[ :patch_module ]
|
213
|
+
patch_target = target_and_module_array_entry[ :patch_target ]
|
214
|
+
|
215
|
+
next if patch_module.nil?
|
216
|
+
|
217
|
+
# If the patch contains a target-based collection of unbound
|
218
|
+
# methods, it was disabled previously (see the 'disable' code).
|
219
|
+
# Re-enable by re-building the module's methods.
|
220
|
+
#
|
221
|
+
if target_and_module_array_entry.has_key?( :unbound_methods )
|
222
|
+
|
223
|
+
target_and_module_array_entry[ :unbound_methods ].each do | method_name, unbound_method |
|
224
|
+
patch_module.send( :define_method, method_name, unbound_method )
|
225
|
+
end
|
226
|
+
|
227
|
+
# Discard the references to the now-unneeded unbound methods.
|
228
|
+
#
|
229
|
+
target_and_module_array_entry.delete( :unbound_methods )
|
230
|
+
|
231
|
+
end
|
232
|
+
|
233
|
+
# *Always* call "prepend". If the same patch modules are being used
|
234
|
+
# against multiple targets, the fact that the code above saw that a
|
235
|
+
# module had been disabled for one particular target doesn't mean
|
236
|
+
# that the module had previously been inserted into the ancestors
|
237
|
+
# for "this" target. It might have been registered later.
|
238
|
+
#
|
239
|
+
# This is safe as repeat calls do nothing; they don't even reorder
|
240
|
+
# the ancestor chain.
|
241
|
+
#
|
242
|
+
patch_target.prepend( patch_module )
|
243
|
+
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
# Disable a patch previously enabled with ::enable (see there for more
|
249
|
+
# information).
|
250
|
+
#
|
251
|
+
# A disabled patch will still be present in a target unit's +ancestors+
|
252
|
+
# list, but has no performance impact. Repeated enable/disable cycles
|
253
|
+
# incur no additional runtime performance penalties.
|
254
|
+
#
|
255
|
+
# _Named_ parameters are:
|
256
|
+
#
|
257
|
+
# +extension_module+:: A module previously passed in the same-named
|
258
|
+
# parameter to ::register. The instance and/or class
|
259
|
+
# methods defined therein will be removed from the
|
260
|
+
# previously registered target.
|
261
|
+
#
|
262
|
+
# Disabling the same extension multiple times has no side effects.
|
263
|
+
#
|
264
|
+
def self.disable( target_unit: nil, extension_module: )
|
265
|
+
if ( target_units_hash = @@modules[ extension_module ] ).nil?
|
266
|
+
raise "Hoodoo::Monkey::disable: Extension module '#{ extension_module.inspect }' is not registered"
|
267
|
+
end
|
268
|
+
|
269
|
+
target_units_hash.each_value do | target_and_module_array |
|
270
|
+
target_and_module_array.each do | target_and_module_array_entry |
|
271
|
+
patch_module = target_and_module_array_entry[ :patch_module ]
|
272
|
+
patch_target = target_and_module_array_entry[ :patch_target ]
|
273
|
+
|
274
|
+
next if patch_module.nil? || target_and_module_array_entry.has_key?( :unbound_methods )
|
275
|
+
|
276
|
+
target_and_module_array_entry[ :unbound_methods ] = {}
|
277
|
+
|
278
|
+
# We take unbound method references to every patch module method,
|
279
|
+
# then remove the originals. In the re-enable code, the methods
|
280
|
+
# are redefined in the module. This approach means that any
|
281
|
+
# target unit with the module in its ancestors chain will see the
|
282
|
+
# change immediately. We don't need to iterate over them.
|
283
|
+
#
|
284
|
+
patch_module.instance_methods( false ).each do | method_name |
|
285
|
+
unbound_method = patch_module.instance_method( method_name )
|
286
|
+
target_and_module_array_entry[ :unbound_methods ][ method_name ] = unbound_method
|
287
|
+
patch_module.send( :remove_method, method_name )
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
# Out-of-the-box regular monkey patches (versus chaos monkeys) for Hoodoo.
|
294
|
+
# These typically predictably extend or modify existing Hoodoo behaviour.
|
295
|
+
# See Hoodoo::Monkey for details.
|
296
|
+
#
|
297
|
+
module Patch
|
298
|
+
end
|
299
|
+
|
300
|
+
# Out-of-the-box chaos monkey patches (versus regular monkeys) for Hoodoo.
|
301
|
+
# These typically provoke unpredictable states inside existing Hoodoo
|
302
|
+
# behaviour to exercise "unhappy path" code paths. See Hoodoo::Monkey for
|
303
|
+
# details.
|
304
|
+
#
|
305
|
+
module Chaos
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
@@ -0,0 +1,195 @@
|
|
1
|
+
########################################################################
|
2
|
+
# File:: newrelic_traced_amqp.rb
|
3
|
+
# (C):: Loyalty New Zealand 2015
|
4
|
+
#
|
5
|
+
# Purpose:: Extend the AMQP endpoint to support NewRelic cross-app
|
6
|
+
# transaction tracing. Only defined and registered if the
|
7
|
+
# NewRelic gem is available and Hoodooo Client is in scope.
|
8
|
+
#
|
9
|
+
# See Hoodoo::Monkey::Patch::NewRelicTracedAMQP for more.
|
10
|
+
# ----------------------------------------------------------------------
|
11
|
+
# 08-Apr-2016 (RJS): Created.
|
12
|
+
########################################################################
|
13
|
+
|
14
|
+
module Hoodoo
|
15
|
+
module Monkey
|
16
|
+
module Patch
|
17
|
+
|
18
|
+
begin
|
19
|
+
require 'newrelic_rpm' # Raises LoadError if NewRelic is absent
|
20
|
+
|
21
|
+
# Wrap Hoodoo::Client::Endpoint::AMQP using NewRelic transaction
|
22
|
+
# tracing so that over-queue inter-resource calls get connected
|
23
|
+
# together in NewRelic's view of the world.
|
24
|
+
#
|
25
|
+
# This module self-registers with Hooodoo::Monkey and, provided
|
26
|
+
# that Hoodoo::Services::Middleware is defined at parse-time and
|
27
|
+
# Hoodoo::Services::Middleware.environment.production? reports
|
28
|
+
# +false+, will be enabled by default.
|
29
|
+
#
|
30
|
+
module NewRelicTracedAMQP
|
31
|
+
|
32
|
+
# Instance methods to patch over Hoodoo::Client::Endpoint::AMQP.
|
33
|
+
#
|
34
|
+
module InstanceExtensions
|
35
|
+
|
36
|
+
# Wrap the request with NewRelic's cross-app transaction tracing.
|
37
|
+
# This adds headers to the request and extracts header data from
|
38
|
+
# the response. It calls the original implementation via +super+.
|
39
|
+
#
|
40
|
+
# +http_message+:: Hash describing the message to send.
|
41
|
+
#
|
42
|
+
# +full_uri+:: URI being sent to. This is somewhat meaningless
|
43
|
+
# when using AMQP but NewRelic requires it.
|
44
|
+
#
|
45
|
+
def monkey_send_request( http_message, full_uri )
|
46
|
+
amqp_response = nil
|
47
|
+
newrelic_request = ::Hoodoo::Monkey::Patch::NewRelicTracedAMQP::AMQPNewRelicRequestWrapper.new(
|
48
|
+
http_message,
|
49
|
+
full_uri
|
50
|
+
)
|
51
|
+
|
52
|
+
::NewRelic::Agent::CrossAppTracing.tl_trace_http_request( newrelic_request ) do
|
53
|
+
|
54
|
+
# Disable further tracing in request to avoid double counting
|
55
|
+
# if connection wasn't started (which calls request again).
|
56
|
+
#
|
57
|
+
::NewRelic::Agent.disable_all_tracing do
|
58
|
+
|
59
|
+
amqp_response = super( http_message, full_uri )
|
60
|
+
|
61
|
+
# The outer block extracts required information from the
|
62
|
+
# object returned by this block. Need to wrap it match the
|
63
|
+
# expected interface.
|
64
|
+
#
|
65
|
+
::Hoodoo::Monkey::Patch::NewRelicTracedAMQP::AMQPNewRelicResponseWrapper.new(
|
66
|
+
amqp_response
|
67
|
+
)
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
return amqp_response
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Wrapper class for an AMQP request which conforms to the API that
|
77
|
+
# NewRelic expects.
|
78
|
+
#
|
79
|
+
class AMQPNewRelicRequestWrapper
|
80
|
+
|
81
|
+
# Wrap the Alchemy Flux +http_message+ aimed at the specified
|
82
|
+
# +full_uri+.
|
83
|
+
#
|
84
|
+
# +http_message+:: Hash describing the request for Alchemy Flux.
|
85
|
+
# +full_uri+:: Full target URI, as a String.
|
86
|
+
#
|
87
|
+
def initialize( http_message, full_uri )
|
88
|
+
@http_message = http_message
|
89
|
+
@full_uri = full_uri
|
90
|
+
end
|
91
|
+
|
92
|
+
# String describing what kind of request this is.
|
93
|
+
#
|
94
|
+
def type
|
95
|
+
self.class.to_s()
|
96
|
+
end
|
97
|
+
|
98
|
+
# String describing this request's intended host.
|
99
|
+
#
|
100
|
+
def host
|
101
|
+
@http_message[ 'host' ]
|
102
|
+
end
|
103
|
+
|
104
|
+
# String describing this request's HTTP verb (GET, POST and
|
105
|
+
# so-on). String case is undefined, so perform case-insensitive
|
106
|
+
# comparisions.
|
107
|
+
#
|
108
|
+
def method
|
109
|
+
@http_message[ 'verb' ]
|
110
|
+
end
|
111
|
+
|
112
|
+
# Key lookup is delegated to the headers Hash per NewRelic's
|
113
|
+
# expectations of how a request behaves.
|
114
|
+
#
|
115
|
+
# +key+:: Hash key to look up.
|
116
|
+
#
|
117
|
+
def []( key )
|
118
|
+
@http_message[ 'headers' ][ key ]
|
119
|
+
end
|
120
|
+
|
121
|
+
# Key setting is delegated to the headers Hash per NewRelic's
|
122
|
+
# expectations of how a request behaves.
|
123
|
+
#
|
124
|
+
# +key+:: Key of Hash entry to modify.
|
125
|
+
# +value+:: New or replacement value for identified Hash entry.
|
126
|
+
#
|
127
|
+
def []=( key, value )
|
128
|
+
@http_message[ 'headers' ][ key ] = value
|
129
|
+
end
|
130
|
+
|
131
|
+
# String describing the full request URI.
|
132
|
+
#
|
133
|
+
def uri
|
134
|
+
@full_uri
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
# Wrapper class for an AMQP request which conforms to the API that
|
140
|
+
# NewRelic expects.
|
141
|
+
#
|
142
|
+
class AMQPNewRelicResponseWrapper
|
143
|
+
|
144
|
+
# The +response_hash+ to be wrapped.
|
145
|
+
#
|
146
|
+
# +response_hash+:: Hash describing the response returned from
|
147
|
+
# Alchemy Flux.
|
148
|
+
#
|
149
|
+
def initialize( response_hash )
|
150
|
+
@response_hash = response_hash
|
151
|
+
end
|
152
|
+
|
153
|
+
# If the NewRelic cross-app tracing header is the +key+, return the
|
154
|
+
# value of the header that matches that key. Otherwise look up the
|
155
|
+
# key like normal.
|
156
|
+
#
|
157
|
+
# +key+:: Hash key to look up.
|
158
|
+
#
|
159
|
+
def []( key )
|
160
|
+
if key == ::NewRelic::Agent::CrossAppTracing::NR_APPDATA_HEADER
|
161
|
+
@response_hash[ 'headers' ][ key ]
|
162
|
+
else
|
163
|
+
@response_hash[ key ]
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# Register this class with the Hoodoo monkey patch engine in any
|
170
|
+
# non-production environment.
|
171
|
+
#
|
172
|
+
if defined?( Hoodoo::Client ) &&
|
173
|
+
defined?( Hoodoo::Client::Endpoint ) &&
|
174
|
+
defined?( Hoodoo::Client::Endpoint::AMQP )
|
175
|
+
|
176
|
+
Hoodoo::Monkey.register(
|
177
|
+
target_unit: Hoodoo::Client::Endpoint::AMQP,
|
178
|
+
extension_module: Hoodoo::Monkey::Patch::NewRelicTracedAMQP
|
179
|
+
)
|
180
|
+
|
181
|
+
if defined?( Hoodoo::Services ) &&
|
182
|
+
defined?( Hoodoo::Services::Middleware) &&
|
183
|
+
Hoodoo::Services::Middleware.environment.production? != true
|
184
|
+
|
185
|
+
Hoodoo::Monkey.enable( extension_module: Hoodoo::Monkey::Patch::NewRelicTracedAMQP )
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
rescue LoadError
|
190
|
+
# No NewRelic => do nothing
|
191
|
+
end
|
192
|
+
|
193
|
+
end # module Patch
|
194
|
+
end # module Monkey
|
195
|
+
end # module Hoodoo
|