ddtrace 0.11.2 → 0.11.3

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.
@@ -37,6 +37,10 @@ module Datadog
37
37
  request_span = tracer.trace('rack.request', trace_options)
38
38
  env[:datadog_rack_request_span] = request_span
39
39
 
40
+ # Copy the original env, before the rest of the stack executes.
41
+ # Values may change; we want values before that happens.
42
+ original_env = env.dup
43
+
40
44
  # call the rest of the stack
41
45
  status, headers, response = @app.call(env)
42
46
  [status, headers, response]
@@ -60,7 +64,7 @@ module Datadog
60
64
  # the result for this request; `resource` and `tags` are expected to
61
65
  # be set in another level but if they're missing, reasonable defaults
62
66
  # are used.
63
- set_request_tags!(request_span, env, status, headers, response)
67
+ set_request_tags!(request_span, env, status, headers, response, original_env)
64
68
 
65
69
  # ensure the request_span is finished and the context reset;
66
70
  # this assumes that the Rack middleware creates a root span
@@ -80,13 +84,20 @@ module Datadog
80
84
  end
81
85
  end
82
86
 
83
- def set_request_tags!(request_span, env, status, headers, response)
84
- # the source of truth in Rack is the PATH_INFO key that holds the
85
- # URL for the current request; some framework may override that
86
- # value, especially during exception handling and because of that
87
- # we prefer using the `REQUEST_URI` if this is available.
88
- # NOTE: `REQUEST_URI` is Rails specific and may not apply for other frameworks
89
- url = env['REQUEST_URI'] || env['PATH_INFO']
87
+ def set_request_tags!(request_span, env, status, headers, response, original_env)
88
+ # http://www.rubydoc.info/github/rack/rack/file/SPEC
89
+ # The source of truth in Rack is the PATH_INFO key that holds the
90
+ # URL for the current request; but some frameworks may override that
91
+ # value, especially during exception handling.
92
+ #
93
+ # Because of this, we prefer to use REQUEST_URI, if available, which is the
94
+ # relative path + query string, and doesn't mutate.
95
+ #
96
+ # REQUEST_URI is only available depending on what web server is running though.
97
+ # So when its not available, we want the original, unmutated PATH_INFO, which
98
+ # is just the relative path without query strings.
99
+ url = env['REQUEST_URI'] || original_env['PATH_INFO']
100
+ request_id = get_request_id(headers, env)
90
101
 
91
102
  request_span.resource ||= resource_name_for(env, status)
92
103
  if request_span.get_tag(Datadog::Ext::HTTP::METHOD).nil?
@@ -110,6 +121,9 @@ module Datadog
110
121
  if request_span.get_tag(Datadog::Ext::HTTP::STATUS_CODE).nil? && status
111
122
  request_span.set_tag(Datadog::Ext::HTTP::STATUS_CODE, status)
112
123
  end
124
+ if request_span.get_tag(Datadog::Ext::HTTP::REQUEST_ID).nil? && request_id
125
+ request_span.set_tag(Datadog::Ext::HTTP::REQUEST_ID, request_id)
126
+ end
113
127
 
114
128
  # detect if the status code is a 5xx and flag the request span as an error
115
129
  # unless it has been already set by the underlying framework
@@ -117,6 +131,14 @@ module Datadog
117
131
  request_span.status = 1
118
132
  end
119
133
  end
134
+
135
+ # If Rails is present, it will sanitize & use the Request ID header,
136
+ # or generate a UUID if no request ID header is present, then set that as headers['X-Request-Id'].
137
+ # Othewise use whatever Rack variables are present (they should all be the same.)
138
+ def get_request_id(headers, env)
139
+ headers ||= {}
140
+ headers['X-Request-Id'] || headers['X-Request-ID'] || env['HTTP_X_REQUEST_ID']
141
+ end
120
142
  end
121
143
  end
122
144
  end
@@ -6,9 +6,13 @@ module Datadog
6
6
  module Rails
7
7
  # Code used to create and handle 'rails.action_controller' spans.
8
8
  module ActionController
9
+ include Datadog::Patcher
10
+
9
11
  def self.instrument
10
12
  # patch Rails core components
11
- Datadog::RailsActionPatcher.patch_action_controller
13
+ do_once(:instrument) do
14
+ Datadog::RailsActionPatcher.patch_action_controller
15
+ end
12
16
  end
13
17
 
14
18
  def self.start_processing(payload)
@@ -46,20 +50,14 @@ module Datadog
46
50
  span.set_tag('rails.route.action', payload.fetch(:action))
47
51
  span.set_tag('rails.route.controller', payload.fetch(:controller))
48
52
 
49
- if payload[:exception].nil?
53
+ exception = payload[:exception_object]
54
+ if exception.nil?
50
55
  # [christian] in some cases :status is not defined,
51
56
  # rather than firing an error, simply acknowledge we don't know it.
52
57
  status = payload.fetch(:status, '?').to_s
53
58
  span.status = 1 if status.starts_with?('5')
54
- else
55
- error = payload[:exception]
56
- if defined?(::ActionDispatch::ExceptionWrapper)
57
- status = ::ActionDispatch::ExceptionWrapper.status_code_for_exception(error[0])
58
- status = status ? status.to_s : '?'
59
- else
60
- status = '500'
61
- end
62
- span.set_error(error) if status.starts_with?('5')
59
+ elsif Utils.exception_is_error?(exception)
60
+ span.set_error(exception)
63
61
  end
64
62
  ensure
65
63
  span.finish()
@@ -5,9 +5,13 @@ module Datadog
5
5
  module Rails
6
6
  # Code used to create and handle 'rails.render_template' and 'rails.render_partial' spans.
7
7
  module ActionView
8
+ include Datadog::Patcher
9
+
8
10
  def self.instrument
9
11
  # patch Rails core components
10
- Datadog::RailsRendererPatcher.patch_renderer
12
+ do_once(:instrument) do
13
+ Datadog::RailsRendererPatcher.patch_renderer
14
+ end
11
15
  end
12
16
 
13
17
  def self.start_render_template(payload)
@@ -7,13 +7,17 @@ module Datadog
7
7
  module Rails
8
8
  # Code used to create and handle 'mysql.query', 'postgres.query', ... spans.
9
9
  module ActiveRecord
10
+ include Datadog::Patcher
11
+
10
12
  def self.instrument
11
13
  # ActiveRecord is instrumented only if it's available
12
14
  return unless defined?(::ActiveRecord)
13
15
 
14
- # subscribe when the active record query has been processed
15
- ::ActiveSupport::Notifications.subscribe('sql.active_record') do |*args|
16
- sql(*args)
16
+ do_once(:instrument) do
17
+ # subscribe when the active record query has been processed
18
+ ::ActiveSupport::Notifications.subscribe('sql.active_record') do |*args|
19
+ sql(*args)
20
+ end
17
21
  end
18
22
  end
19
23
 
@@ -6,9 +6,13 @@ module Datadog
6
6
  module Rails
7
7
  # Code used to create and handle 'rails.cache' spans.
8
8
  module ActiveSupport
9
+ include Datadog::Patcher
10
+
9
11
  def self.instrument
10
- # patch Rails core components
11
- Datadog::RailsCachePatcher.patch_cache_store
12
+ do_once(:instrument) do
13
+ # patch Rails core components
14
+ Datadog::RailsCachePatcher.patch_cache_store
15
+ end
12
16
  end
13
17
 
14
18
  def self.start_trace_cache(payload)
@@ -4,193 +4,211 @@ module Datadog
4
4
  # rubocop:disable Metrics/MethodLength
5
5
  # rubocop:disable Metrics/ModuleLength
6
6
  module RailsRendererPatcher
7
+ include Datadog::Patcher
8
+
7
9
  module_function
8
10
 
9
11
  def patch_renderer
10
- if defined?(::ActionView::TemplateRenderer) && defined?(::ActionView::PartialRenderer)
11
- patch_template_renderer(::ActionView::TemplateRenderer)
12
- patch_partial_renderer(::ActionView::PartialRenderer)
13
- elsif defined?(::ActionView::Rendering) && defined?(::ActionView::Partials::PartialRenderer)
14
- # NOTE: Rails < 3.1 compatibility: different classes are used
15
- patch_template_renderer(::ActionView::Rendering)
16
- patch_partial_renderer(::ActionView::Partials::PartialRenderer)
17
- else
18
- Datadog::Tracer.log.debug('Expected Template/Partial classes not found; template rendering disabled')
12
+ do_once(:patch_renderer) do
13
+ if defined?(::ActionView::TemplateRenderer) && defined?(::ActionView::PartialRenderer)
14
+ patch_template_renderer(::ActionView::TemplateRenderer)
15
+ patch_partial_renderer(::ActionView::PartialRenderer)
16
+ elsif defined?(::ActionView::Rendering) && defined?(::ActionView::Partials::PartialRenderer)
17
+ # NOTE: Rails < 3.1 compatibility: different classes are used
18
+ patch_template_renderer(::ActionView::Rendering)
19
+ patch_partial_renderer(::ActionView::Partials::PartialRenderer)
20
+ else
21
+ Datadog::Tracer.log.debug('Expected Template/Partial classes not found; template rendering disabled')
22
+ end
19
23
  end
20
24
  end
21
25
 
22
26
  def patch_template_renderer(klass)
23
- klass.class_eval do
24
- def render_with_datadog(*args, &block)
25
- # create a tracing context and start the rendering span
26
- # NOTE: Rails < 3.1 compatibility: preserve the tracing
27
- # context when a partial is rendered
28
- @tracing_context ||= {}
29
- if @tracing_context.empty?
30
- Datadog::Contrib::Rails::ActionView.start_render_template(tracing_context: @tracing_context)
27
+ do_once(:patch_template_renderer) do
28
+ klass.class_eval do
29
+ def render_with_datadog(*args, &block)
30
+ # create a tracing context and start the rendering span
31
+ # NOTE: Rails < 3.1 compatibility: preserve the tracing
32
+ # context when a partial is rendered
33
+ @tracing_context ||= {}
34
+ if @tracing_context.empty?
35
+ Datadog::Contrib::Rails::ActionView.start_render_template(tracing_context: @tracing_context)
36
+ end
37
+
38
+ render_without_datadog(*args, &block)
39
+ rescue Exception => e
40
+ # attach the exception to the tracing context if any
41
+ @tracing_context[:exception] = e
42
+ raise e
43
+ ensure
44
+ # ensure that the template `Span` is finished even during exceptions
45
+ Datadog::Contrib::Rails::ActionView.finish_render_template(tracing_context: @tracing_context)
31
46
  end
32
47
 
33
- render_without_datadog(*args)
34
- rescue Exception => e
35
- # attach the exception to the tracing context if any
36
- @tracing_context[:exception] = e
37
- raise e
38
- ensure
39
- # ensure that the template `Span` is finished even during exceptions
40
- Datadog::Contrib::Rails::ActionView.finish_render_template(tracing_context: @tracing_context)
41
- end
42
-
43
- def render_template_with_datadog(*args)
44
- begin
45
- # arguments based on render_template signature (stable since Rails 3.2)
46
- template = args[0]
47
- layout_name = args[1]
48
-
49
- # update the tracing context with computed values before the rendering
50
- template_name = template.try('identifier')
51
- template_name = Datadog::Contrib::Rails::Utils.normalize_template_name(template_name)
52
- layout = if layout_name.is_a?(String)
53
- # NOTE: Rails < 3.1 compatibility: the second argument is the layout name
54
- layout_name
55
- else
56
- layout_name.try(:[], 'virtual_path')
57
- end
58
- @tracing_context[:template_name] = template_name
59
- @tracing_context[:layout] = layout
60
- rescue StandardError => e
61
- Datadog::Tracer.log.debug(e.message)
48
+ def render_template_with_datadog(*args)
49
+ begin
50
+ # arguments based on render_template signature (stable since Rails 3.2)
51
+ template = args[0]
52
+ layout_name = args[1]
53
+
54
+ # update the tracing context with computed values before the rendering
55
+ template_name = template.try('identifier')
56
+ template_name = Datadog::Contrib::Rails::Utils.normalize_template_name(template_name)
57
+ layout = if layout_name.is_a?(String)
58
+ # NOTE: Rails < 3.1 compatibility: the second argument is the layout name
59
+ layout_name
60
+ else
61
+ layout_name.try(:[], 'virtual_path')
62
+ end
63
+ @tracing_context[:template_name] = template_name
64
+ @tracing_context[:layout] = layout
65
+ rescue StandardError => e
66
+ Datadog::Tracer.log.debug(e.message)
67
+ end
68
+
69
+ # execute the original function anyway
70
+ render_template_without_datadog(*args)
62
71
  end
63
72
 
64
- # execute the original function anyway
65
- render_template_without_datadog(*args)
66
- end
73
+ # method aliasing to patch the class
74
+ alias_method :render_without_datadog, :render
75
+ alias_method :render, :render_with_datadog
67
76
 
68
- # method aliasing to patch the class
69
- alias_method :render_without_datadog, :render
70
- alias_method :render, :render_with_datadog
71
-
72
- if klass.private_method_defined?(:render_template) || klass.method_defined?(:render_template)
73
- alias_method :render_template_without_datadog, :render_template
74
- alias_method :render_template, :render_template_with_datadog
75
- else
76
- # NOTE: Rails < 3.1 compatibility: the method name is different
77
- alias_method :render_template_without_datadog, :_render_template
78
- alias_method :_render_template, :render_template_with_datadog
77
+ if klass.private_method_defined?(:render_template) || klass.method_defined?(:render_template)
78
+ alias_method :render_template_without_datadog, :render_template
79
+ alias_method :render_template, :render_template_with_datadog
80
+ else
81
+ # NOTE: Rails < 3.1 compatibility: the method name is different
82
+ alias_method :render_template_without_datadog, :_render_template
83
+ alias_method :_render_template, :render_template_with_datadog
84
+ end
79
85
  end
80
86
  end
81
87
  end
82
88
 
83
89
  def patch_partial_renderer(klass)
84
- klass.class_eval do
85
- def render_with_datadog(*args, &block)
86
- # Create a tracing context and start the rendering span
87
- tracing_context = {}
88
- Datadog::Contrib::Rails::ActionView.start_render_partial(tracing_context: tracing_context)
89
- tracing_contexts[current_span_id] = tracing_context
90
-
91
- render_without_datadog(*args)
92
- rescue Exception => e
93
- # attach the exception to the tracing context if any
94
- tracing_contexts[current_span_id][:exception] = e
95
- raise e
96
- ensure
97
- # Ensure that the template `Span` is finished even during exceptions
98
- # Remove the existing tracing context (to avoid leaks)
99
- tracing_contexts.delete(current_span_id)
100
-
101
- # Then finish the span associated with the context
102
- Datadog::Contrib::Rails::ActionView.finish_render_partial(tracing_context: tracing_context)
103
- end
90
+ do_once(:patch_partial_renderer) do
91
+ klass.class_eval do
92
+ def render_with_datadog(*args, &block)
93
+ # Create a tracing context and start the rendering span
94
+ tracing_context = {}
95
+ Datadog::Contrib::Rails::ActionView.start_render_partial(tracing_context: tracing_context)
96
+ tracing_contexts[current_span_id] = tracing_context
97
+
98
+ render_without_datadog(*args)
99
+ rescue Exception => e
100
+ # attach the exception to the tracing context if any
101
+ tracing_contexts[current_span_id][:exception] = e
102
+ raise e
103
+ ensure
104
+ # Ensure that the template `Span` is finished even during exceptions
105
+ # Remove the existing tracing context (to avoid leaks)
106
+ tracing_contexts.delete(current_span_id)
104
107
 
105
- def render_partial_with_datadog(*args)
106
- begin
107
- # update the tracing context with computed values before the rendering
108
- template_name = Datadog::Contrib::Rails::Utils.normalize_template_name(@template.try('identifier'))
109
- tracing_contexts[current_span_id][:template_name] = template_name
110
- rescue StandardError => e
111
- Datadog::Tracer.log.debug(e.message)
108
+ # Then finish the span associated with the context
109
+ Datadog::Contrib::Rails::ActionView.finish_render_partial(tracing_context: tracing_context)
112
110
  end
113
111
 
114
- # execute the original function anyway
115
- render_partial_without_datadog(*args)
116
- end
112
+ def render_partial_with_datadog(*args)
113
+ begin
114
+ # update the tracing context with computed values before the rendering
115
+ template_name = Datadog::Contrib::Rails::Utils.normalize_template_name(@template.try('identifier'))
116
+ tracing_contexts[current_span_id][:template_name] = template_name
117
+ rescue StandardError => e
118
+ Datadog::Tracer.log.debug(e.message)
119
+ end
120
+
121
+ # execute the original function anyway
122
+ render_partial_without_datadog(*args)
123
+ end
117
124
 
118
- # Table of tracing contexts, one per partial/span, keyed by span_id
119
- # because there will be multiple concurrent contexts, depending on how
120
- # many partials are nested within one another.
121
- def tracing_contexts
122
- @tracing_contexts ||= {}
123
- end
125
+ # Table of tracing contexts, one per partial/span, keyed by span_id
126
+ # because there will be multiple concurrent contexts, depending on how
127
+ # many partials are nested within one another.
128
+ def tracing_contexts
129
+ @tracing_contexts ||= {}
130
+ end
124
131
 
125
- def current_span_id
126
- Datadog.configuration[:rails][:tracer].call_context.current_span.span_id
127
- end
132
+ def current_span_id
133
+ Datadog.configuration[:rails][:tracer].call_context.current_span.span_id
134
+ end
128
135
 
129
- # method aliasing to patch the class
130
- alias_method :render_without_datadog, :render
131
- alias_method :render, :render_with_datadog
132
- alias_method :render_partial_without_datadog, :render_partial
133
- alias_method :render_partial, :render_partial_with_datadog
136
+ # method aliasing to patch the class
137
+ alias_method :render_without_datadog, :render
138
+ alias_method :render, :render_with_datadog
139
+ alias_method :render_partial_without_datadog, :render_partial
140
+ alias_method :render_partial, :render_partial_with_datadog
141
+ end
134
142
  end
135
143
  end
136
144
  end
137
145
 
138
146
  # RailsActionPatcher contains functions to patch Rails action controller instrumentation
139
147
  module RailsActionPatcher
148
+ include Datadog::Patcher
149
+
140
150
  module_function
141
151
 
142
152
  def patch_action_controller
143
- patch_process_action
153
+ do_once(:patch_action_controller) do
154
+ patch_process_action
155
+ end
144
156
  end
145
157
 
146
158
  def patch_process_action
147
- ::ActionController::Instrumentation.class_eval do
148
- def process_action_with_datadog(*args)
149
- # mutable payload with a tracing context that is used in two different
150
- # signals; it propagates the request span so that it can be finished
151
- # no matter what
152
- payload = {
153
- controller: self.class,
154
- action: action_name,
155
- headers: {
156
- # The exception this controller was given in the request,
157
- # which is typical if the controller is configured to handle exceptions.
158
- request_exception: request.headers['action_dispatch.exception']
159
- },
160
- tracing_context: {}
161
- }
162
-
163
- begin
164
- # process and catch request exceptions
165
- Datadog::Contrib::Rails::ActionController.start_processing(payload)
166
- result = process_action_without_datadog(*args)
167
- payload[:status] = response.status
168
- result
169
- rescue Exception => e
170
- payload[:exception] = [e.class.name, e.message]
171
- payload[:exception_object] = e
172
- raise e
159
+ do_once(:patch_process_action) do
160
+ ::ActionController::Instrumentation.class_eval do
161
+ def process_action_with_datadog(*args)
162
+ # mutable payload with a tracing context that is used in two different
163
+ # signals; it propagates the request span so that it can be finished
164
+ # no matter what
165
+ payload = {
166
+ controller: self.class,
167
+ action: action_name,
168
+ headers: {
169
+ # The exception this controller was given in the request,
170
+ # which is typical if the controller is configured to handle exceptions.
171
+ request_exception: request.headers['action_dispatch.exception']
172
+ },
173
+ tracing_context: {}
174
+ }
175
+
176
+ begin
177
+ # process and catch request exceptions
178
+ Datadog::Contrib::Rails::ActionController.start_processing(payload)
179
+ result = process_action_without_datadog(*args)
180
+ payload[:status] = response.status
181
+ result
182
+ rescue Exception => e
183
+ payload[:exception] = [e.class.name, e.message]
184
+ payload[:exception_object] = e
185
+ raise e
186
+ end
187
+ ensure
188
+ Datadog::Contrib::Rails::ActionController.finish_processing(payload)
173
189
  end
174
- ensure
175
- Datadog::Contrib::Rails::ActionController.finish_processing(payload)
176
- end
177
190
 
178
- alias_method :process_action_without_datadog, :process_action
179
- alias_method :process_action, :process_action_with_datadog
191
+ alias_method :process_action_without_datadog, :process_action
192
+ alias_method :process_action, :process_action_with_datadog
193
+ end
180
194
  end
181
195
  end
182
196
  end
183
197
 
184
198
  # RailsCachePatcher contains function to patch Rails caching libraries.
185
199
  module RailsCachePatcher
200
+ include Datadog::Patcher
201
+
186
202
  module_function
187
203
 
188
204
  def patch_cache_store
189
- patch_cache_store_read
190
- patch_cache_store_fetch
191
- patch_cache_store_write
192
- patch_cache_store_delete
193
- reload_cache_store
205
+ do_once(:patch_cache_store) do
206
+ patch_cache_store_read
207
+ patch_cache_store_fetch
208
+ patch_cache_store_write
209
+ patch_cache_store_delete
210
+ reload_cache_store
211
+ end
194
212
  end
195
213
 
196
214
  def cache_store_class(k)
@@ -210,107 +228,116 @@ module Datadog
210
228
  end
211
229
 
212
230
  def patch_cache_store_read
213
- cache_store_class(:read).class_eval do
214
- alias_method :read_without_datadog, :read
215
- def read(*args, &block)
216
- payload = {
217
- action: 'GET',
218
- key: args[0],
219
- tracing_context: {}
220
- }
221
-
222
- begin
223
- # process and catch cache exceptions
224
- Datadog::Contrib::Rails::ActiveSupport.start_trace_cache(payload)
225
- read_without_datadog(*args, &block)
226
- rescue Exception => e
227
- payload[:exception] = [e.class.name, e.message]
228
- payload[:exception_object] = e
229
- raise e
231
+ do_once(:patch_cache_store_read) do
232
+ cache_store_class(:read).class_eval do
233
+ alias_method :read_without_datadog, :read
234
+ def read(*args, &block)
235
+ payload = {
236
+ action: 'GET',
237
+ key: args[0],
238
+ tracing_context: {}
239
+ }
240
+
241
+ begin
242
+ # process and catch cache exceptions
243
+ Datadog::Contrib::Rails::ActiveSupport.start_trace_cache(payload)
244
+ read_without_datadog(*args, &block)
245
+ rescue Exception => e
246
+ payload[:exception] = [e.class.name, e.message]
247
+ payload[:exception_object] = e
248
+ raise e
249
+ end
250
+ ensure
251
+ Datadog::Contrib::Rails::ActiveSupport.finish_trace_cache(payload)
230
252
  end
231
- ensure
232
- Datadog::Contrib::Rails::ActiveSupport.finish_trace_cache(payload)
233
253
  end
234
254
  end
235
255
  end
236
256
 
237
257
  def patch_cache_store_fetch
238
- cache_store_class(:fetch).class_eval do
239
- alias_method :fetch_without_datadog, :fetch
240
- def fetch(*args, &block)
241
- payload = {
242
- action: 'GET',
243
- key: args[0],
244
- tracing_context: {}
245
- }
246
-
247
- begin
248
- # process and catch cache exceptions
249
- Datadog::Contrib::Rails::ActiveSupport.start_trace_cache(payload)
250
- fetch_without_datadog(*args, &block)
251
- rescue Exception => e
252
- payload[:exception] = [e.class.name, e.message]
253
- payload[:exception_object] = e
254
- raise e
258
+ do_once(:patch_cache_store_fetch) do
259
+ cache_store_class(:fetch).class_eval do
260
+ alias_method :fetch_without_datadog, :fetch
261
+ def fetch(*args, &block)
262
+ payload = {
263
+ action: 'GET',
264
+ key: args[0],
265
+ tracing_context: {}
266
+ }
267
+
268
+ begin
269
+ # process and catch cache exceptions
270
+ Datadog::Contrib::Rails::ActiveSupport.start_trace_cache(payload)
271
+ fetch_without_datadog(*args, &block)
272
+ rescue Exception => e
273
+ payload[:exception] = [e.class.name, e.message]
274
+ payload[:exception_object] = e
275
+ raise e
276
+ end
277
+ ensure
278
+ Datadog::Contrib::Rails::ActiveSupport.finish_trace_cache(payload)
255
279
  end
256
- ensure
257
- Datadog::Contrib::Rails::ActiveSupport.finish_trace_cache(payload)
258
280
  end
259
281
  end
260
282
  end
261
283
 
262
284
  def patch_cache_store_write
263
- cache_store_class(:write).class_eval do
264
- alias_method :write_without_datadog, :write
265
- def write(*args, &block)
266
- payload = {
267
- action: 'SET',
268
- key: args[0],
269
- tracing_context: {}
270
- }
271
-
272
- begin
273
- # process and catch cache exceptions
274
- Datadog::Contrib::Rails::ActiveSupport.start_trace_cache(payload)
275
- write_without_datadog(*args, &block)
276
- rescue Exception => e
277
- payload[:exception] = [e.class.name, e.message]
278
- payload[:exception_object] = e
279
- raise e
285
+ do_once(:patch_cache_store_write) do
286
+ cache_store_class(:write).class_eval do
287
+ alias_method :write_without_datadog, :write
288
+ def write(*args, &block)
289
+ payload = {
290
+ action: 'SET',
291
+ key: args[0],
292
+ tracing_context: {}
293
+ }
294
+
295
+ begin
296
+ # process and catch cache exceptions
297
+ Datadog::Contrib::Rails::ActiveSupport.start_trace_cache(payload)
298
+ write_without_datadog(*args, &block)
299
+ rescue Exception => e
300
+ payload[:exception] = [e.class.name, e.message]
301
+ payload[:exception_object] = e
302
+ raise e
303
+ end
304
+ ensure
305
+ Datadog::Contrib::Rails::ActiveSupport.finish_trace_cache(payload)
280
306
  end
281
- ensure
282
- Datadog::Contrib::Rails::ActiveSupport.finish_trace_cache(payload)
283
307
  end
284
308
  end
285
309
  end
286
310
 
287
311
  def patch_cache_store_delete
288
- cache_store_class(:delete).class_eval do
289
- alias_method :delete_without_datadog, :delete
290
- def delete(*args, &block)
291
- payload = {
292
- action: 'DELETE',
293
- key: args[0],
294
- tracing_context: {}
295
- }
296
-
297
- begin
298
- # process and catch cache exceptions
299
- Datadog::Contrib::Rails::ActiveSupport.start_trace_cache(payload)
300
- delete_without_datadog(*args, &block)
301
- rescue Exception => e
302
- payload[:exception] = [e.class.name, e.message]
303
- payload[:exception_object] = e
304
- raise e
312
+ do_once(:patch_cache_store_delete) do
313
+ cache_store_class(:delete).class_eval do
314
+ alias_method :delete_without_datadog, :delete
315
+ def delete(*args, &block)
316
+ payload = {
317
+ action: 'DELETE',
318
+ key: args[0],
319
+ tracing_context: {}
320
+ }
321
+
322
+ begin
323
+ # process and catch cache exceptions
324
+ Datadog::Contrib::Rails::ActiveSupport.start_trace_cache(payload)
325
+ delete_without_datadog(*args, &block)
326
+ rescue Exception => e
327
+ payload[:exception] = [e.class.name, e.message]
328
+ payload[:exception_object] = e
329
+ raise e
330
+ end
331
+ ensure
332
+ Datadog::Contrib::Rails::ActiveSupport.finish_trace_cache(payload)
305
333
  end
306
- ensure
307
- Datadog::Contrib::Rails::ActiveSupport.finish_trace_cache(payload)
308
334
  end
309
335
  end
310
336
  end
311
337
 
312
338
  def self.reload_cache_store
313
- return unless Datadog.registry[:redis].patched?
339
+ redis = Datadog.registry[:redis]
340
+ return unless redis && redis.patched?
314
341
 
315
342
  return unless defined?(::ActiveSupport::Cache::RedisStore) &&
316
343
  defined?(::Rails.cache) &&