hoodoo 1.7.0 → 1.8.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 +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
|