ddtrace 0.12.0.beta2 → 0.12.0.rc1

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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/Appraisals +8 -8
  3. data/CHANGELOG.md +293 -0
  4. data/README.md +11 -114
  5. data/Rakefile +26 -18
  6. data/docs/GettingStarted.md +704 -453
  7. data/gemfiles/contrib.gemfile +2 -2
  8. data/gemfiles/rails4_mysql2.gemfile +1 -1
  9. data/gemfiles/rails5_mysql2.gemfile +2 -2
  10. data/gemfiles/rails5_postgres.gemfile +1 -1
  11. data/gemfiles/rails5_postgres_redis.gemfile +1 -1
  12. data/gemfiles/rails5_postgres_sidekiq.gemfile +1 -1
  13. data/lib/ddtrace.rb +1 -0
  14. data/lib/ddtrace/context.rb +96 -34
  15. data/lib/ddtrace/context_flush.rb +132 -0
  16. data/lib/ddtrace/contrib/active_record/patcher.rb +55 -70
  17. data/lib/ddtrace/contrib/active_record/utils.rb +83 -0
  18. data/lib/ddtrace/contrib/active_support/notifications/subscriber.rb +66 -0
  19. data/lib/ddtrace/contrib/active_support/notifications/subscription.rb +155 -0
  20. data/lib/ddtrace/contrib/elasticsearch/patcher.rb +6 -1
  21. data/lib/ddtrace/contrib/elasticsearch/quantize.rb +89 -0
  22. data/lib/ddtrace/contrib/grape/endpoint.rb +1 -1
  23. data/lib/ddtrace/contrib/racecar/patcher.rb +43 -19
  24. data/lib/ddtrace/contrib/rack/middlewares.rb +58 -11
  25. data/lib/ddtrace/contrib/rack/patcher.rb +18 -11
  26. data/lib/ddtrace/contrib/rails/action_controller.rb +9 -11
  27. data/lib/ddtrace/contrib/rails/action_view.rb +5 -1
  28. data/lib/ddtrace/contrib/rails/active_support.rb +6 -2
  29. data/lib/ddtrace/contrib/rails/core_extensions.rb +280 -215
  30. data/lib/ddtrace/contrib/rails/framework.rb +38 -23
  31. data/lib/ddtrace/contrib/rails/middlewares.rb +7 -2
  32. data/lib/ddtrace/contrib/rails/patcher.rb +9 -6
  33. data/lib/ddtrace/contrib/rails/railtie.rb +4 -2
  34. data/lib/ddtrace/contrib/rails/utils.rb +9 -40
  35. data/lib/ddtrace/patcher.rb +32 -10
  36. data/lib/ddtrace/quantization/http.rb +86 -0
  37. data/lib/ddtrace/tracer.rb +29 -2
  38. data/lib/ddtrace/transport.rb +33 -20
  39. data/lib/ddtrace/version.rb +1 -1
  40. data/lib/ddtrace/writer.rb +11 -5
  41. metadata +8 -3
  42. data/lib/ddtrace/contrib/rails/active_record.rb +0 -80
@@ -13,6 +13,8 @@ module Datadog
13
13
  # application. If request tags are not set by the app, they will be set using
14
14
  # information available at the Rack level.
15
15
  class TraceMiddleware
16
+ RACK_REQUEST_SPAN = 'datadog.rack_request_span'.freeze
17
+
16
18
  def initialize(app)
17
19
  @app = app
18
20
  end
@@ -35,7 +37,17 @@ module Datadog
35
37
  # start a new request span and attach it to the current Rack environment;
36
38
  # we must ensure that the span `resource` is set later
37
39
  request_span = tracer.trace('rack.request', trace_options)
38
- env[:datadog_rack_request_span] = request_span
40
+ env[RACK_REQUEST_SPAN] = request_span
41
+
42
+ # TODO: For backwards compatibility; this attribute is deprecated.
43
+ env[:datadog_rack_request_span] = env[RACK_REQUEST_SPAN]
44
+
45
+ # Add deprecation warnings
46
+ add_deprecation_warnings(env)
47
+
48
+ # Copy the original env, before the rest of the stack executes.
49
+ # Values may change; we want values before that happens.
50
+ original_env = env.dup
39
51
 
40
52
  # call the rest of the stack
41
53
  status, headers, response = @app.call(env)
@@ -60,7 +72,7 @@ module Datadog
60
72
  # the result for this request; `resource` and `tags` are expected to
61
73
  # be set in another level but if they're missing, reasonable defaults
62
74
  # are used.
63
- set_request_tags!(request_span, env, status, headers, response)
75
+ set_request_tags!(request_span, env, status, headers, response, original_env)
64
76
 
65
77
  # ensure the request_span is finished and the context reset;
66
78
  # this assumes that the Rack middleware creates a root span
@@ -73,20 +85,26 @@ module Datadog
73
85
  end
74
86
 
75
87
  def resource_name_for(env, status)
76
- if Datadog.configuration[:rack][:middleware_names]
88
+ if Datadog.configuration[:rack][:middleware_names] && env['RESPONSE_MIDDLEWARE']
77
89
  "#{env['RESPONSE_MIDDLEWARE']}##{env['REQUEST_METHOD']}"
78
90
  else
79
91
  "#{env['REQUEST_METHOD']} #{status}".strip
80
92
  end
81
93
  end
82
94
 
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']
95
+ def set_request_tags!(request_span, env, status, headers, response, original_env)
96
+ # http://www.rubydoc.info/github/rack/rack/file/SPEC
97
+ # The source of truth in Rack is the PATH_INFO key that holds the
98
+ # URL for the current request; but some frameworks may override that
99
+ # value, especially during exception handling.
100
+ #
101
+ # Because of this, we prefer to use REQUEST_URI, if available, which is the
102
+ # relative path + query string, and doesn't mutate.
103
+ #
104
+ # REQUEST_URI is only available depending on what web server is running though.
105
+ # So when its not available, we want the original, unmutated PATH_INFO, which
106
+ # is just the relative path without query strings.
107
+ url = env['REQUEST_URI'] || original_env['PATH_INFO']
90
108
  request_id = get_request_id(headers, env)
91
109
 
92
110
  request_span.resource ||= resource_name_for(env, status)
@@ -94,7 +112,8 @@ module Datadog
94
112
  request_span.set_tag(Datadog::Ext::HTTP::METHOD, env['REQUEST_METHOD'])
95
113
  end
96
114
  if request_span.get_tag(Datadog::Ext::HTTP::URL).nil?
97
- request_span.set_tag(Datadog::Ext::HTTP::URL, url)
115
+ options = Datadog.configuration[:rack][:quantize]
116
+ request_span.set_tag(Datadog::Ext::HTTP::URL, Datadog::Quantization::HTTP.url(url, options))
98
117
  end
99
118
  if request_span.get_tag(Datadog::Ext::HTTP::BASE_URL).nil?
100
119
  request_obj = ::Rack::Request.new(env)
@@ -129,6 +148,34 @@ module Datadog
129
148
  headers ||= {}
130
149
  headers['X-Request-Id'] || headers['X-Request-ID'] || env['HTTP_X_REQUEST_ID']
131
150
  end
151
+
152
+ private
153
+
154
+ REQUEST_SPAN_DEPRECATION_WARNING = %(
155
+ :datadog_rack_request_span is considered an internal symbol in the Rack env,
156
+ and has been been DEPRECATED. Public support for its usage is discontinued.
157
+ If you need the Rack request span, try using `Datadog.tracer.active_span`.
158
+ This key will be removed in version 0.14.0).freeze
159
+
160
+ def add_deprecation_warnings(env)
161
+ env.instance_eval do
162
+ def [](key)
163
+ if key == :datadog_rack_request_span && !@request_span_warning_issued
164
+ Datadog::Tracer.log.warn(REQUEST_SPAN_DEPRECATION_WARNING)
165
+ @request_span_warning_issued = true
166
+ end
167
+ super
168
+ end
169
+
170
+ def []=(key, value)
171
+ if key == :datadog_rack_request_span && !@request_span_warning_issued
172
+ Datadog::Tracer.log.warn(REQUEST_SPAN_DEPRECATION_WARNING)
173
+ @request_span_warning_issued = true
174
+ end
175
+ super
176
+ end
177
+ end
178
+ end
132
179
  end
133
180
  end
134
181
  end
@@ -8,6 +8,7 @@ module Datadog
8
8
  option :tracer, default: Datadog.tracer
9
9
  option :distributed_tracing, default: false
10
10
  option :middleware_names, default: false
11
+ option :quantize, default: {}
11
12
  option :application
12
13
  option :service_name, default: 'rack', depends_on: [:tracer] do |value|
13
14
  get_option(:tracer).set_service_info(value, 'rack', Ext::AppTypes::WEB)
@@ -17,12 +18,24 @@ module Datadog
17
18
  module_function
18
19
 
19
20
  def patch
20
- return true if patched?
21
+ unless patched?
22
+ require_relative 'middlewares'
23
+ @patched = true
24
+ end
21
25
 
22
- require_relative 'middlewares'
23
- @patched = true
26
+ if !@middleware_patched && get_option(:middleware_names)
27
+ if get_option(:application)
28
+ enable_middleware_names
29
+ @middleware_patched = true
30
+ else
31
+ Datadog::Tracer.log.warn(%(
32
+ Rack :middleware_names requires you to also pass :application.
33
+ Middleware names have NOT been patched; please provide :application.
34
+ e.g. use: :rack, middleware_names: true, application: my_rack_app).freeze)
35
+ end
36
+ end
24
37
 
25
- enable_middleware_names if get_option(:middleware_names)
38
+ @patched || @middleware_patched
26
39
  end
27
40
 
28
41
  def patched?
@@ -30,8 +43,7 @@ module Datadog
30
43
  end
31
44
 
32
45
  def enable_middleware_names
33
- root = get_option(:application) || rails_app
34
- retain_middleware_name(root)
46
+ retain_middleware_name(get_option(:application))
35
47
  rescue => e
36
48
  # We can safely ignore these exceptions since they happen only in the
37
49
  # context of middleware patching outside a Rails server process (eg. a
@@ -40,11 +52,6 @@ module Datadog
40
52
  Tracer.log.debug("Error patching middleware stack: #{e}")
41
53
  end
42
54
 
43
- def rails_app
44
- return unless Datadog.registry[:rails].compatible?
45
- ::Rails.application.app
46
- end
47
-
48
55
  def retain_middleware_name(middleware)
49
56
  return unless middleware && middleware.respond_to?(:call)
50
57
 
@@ -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)
@@ -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,250 @@ 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
67
-
68
- # method aliasing to patch the class
69
- alias_method :render_without_datadog, :render
70
- alias_method :render, :render_with_datadog
73
+ # method aliasing to patch the class
74
+ alias_method :render_without_datadog, :render
75
+ alias_method :render, :render_with_datadog
71
76
 
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)
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)
100
107
 
101
- # Then finish the span associated with the context
102
- Datadog::Contrib::Rails::ActionView.finish_render_partial(tracing_context: tracing_context)
103
- end
108
+ # Then finish the span associated with the context
109
+ Datadog::Contrib::Rails::ActionView.finish_render_partial(tracing_context: tracing_context)
110
+ end
104
111
 
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)
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)
112
123
  end
113
124
 
114
- # execute the original function anyway
115
- render_partial_without_datadog(*args)
116
- 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
117
131
 
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
132
+ def current_span_id
133
+ Datadog.configuration[:rails][:tracer].call_context.current_span.span_id
134
+ end
124
135
 
125
- def current_span_id
126
- Datadog.configuration[:rails][:tracer].call_context.current_span.span_id
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
127
141
  end
128
-
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
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
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.0.0')
161
+ # Patch Rails controller base class
162
+ ::ActionController::Metal.send(:prepend, ActionControllerPatch)
163
+ else
164
+ # Rewrite module that gets composed into the Rails controller base class
165
+ ::ActionController::Instrumentation.class_eval do
166
+ def process_action_with_datadog(*args)
167
+ # mutable payload with a tracing context that is used in two different
168
+ # signals; it propagates the request span so that it can be finished
169
+ # no matter what
170
+ payload = {
171
+ controller: self.class,
172
+ action: action_name,
173
+ headers: {
174
+ # The exception this controller was given in the request,
175
+ # which is typical if the controller is configured to handle exceptions.
176
+ request_exception: request.headers['action_dispatch.exception']
177
+ },
178
+ tracing_context: {}
179
+ }
180
+
181
+ begin
182
+ # process and catch request exceptions
183
+ Datadog::Contrib::Rails::ActionController.start_processing(payload)
184
+ result = process_action_without_datadog(*args)
185
+ payload[:status] = response.status
186
+ result
187
+ rescue Exception => e
188
+ payload[:exception] = [e.class.name, e.message]
189
+ payload[:exception_object] = e
190
+ raise e
191
+ end
192
+ ensure
193
+ Datadog::Contrib::Rails::ActionController.finish_processing(payload)
194
+ end
195
+
196
+ alias_method :process_action_without_datadog, :process_action
197
+ alias_method :process_action, :process_action_with_datadog
173
198
  end
174
- ensure
175
- Datadog::Contrib::Rails::ActionController.finish_processing(payload)
176
199
  end
200
+ end
201
+ end
177
202
 
178
- alias_method :process_action_without_datadog, :process_action
179
- alias_method :process_action, :process_action_with_datadog
203
+ # ActionController patch for Ruby 2.0+
204
+ module ActionControllerPatch
205
+ def process_action(*args)
206
+ # mutable payload with a tracing context that is used in two different
207
+ # signals; it propagates the request span so that it can be finished
208
+ # no matter what
209
+ payload = {
210
+ controller: self.class,
211
+ action: action_name,
212
+ headers: {
213
+ # The exception this controller was given in the request,
214
+ # which is typical if the controller is configured to handle exceptions.
215
+ request_exception: request.headers['action_dispatch.exception']
216
+ },
217
+ tracing_context: {}
218
+ }
219
+
220
+ begin
221
+ # process and catch request exceptions
222
+ Datadog::Contrib::Rails::ActionController.start_processing(payload)
223
+ result = super(*args)
224
+ payload[:status] = response.status
225
+ result
226
+ rescue Exception => e
227
+ payload[:exception] = [e.class.name, e.message]
228
+ payload[:exception_object] = e
229
+ raise e
230
+ end
231
+ ensure
232
+ Datadog::Contrib::Rails::ActionController.finish_processing(payload)
180
233
  end
181
234
  end
182
235
  end
183
236
 
184
237
  # RailsCachePatcher contains function to patch Rails caching libraries.
185
238
  module RailsCachePatcher
239
+ include Datadog::Patcher
240
+
186
241
  module_function
187
242
 
188
243
  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
244
+ do_once(:patch_cache_store) do
245
+ patch_cache_store_read
246
+ patch_cache_store_fetch
247
+ patch_cache_store_write
248
+ patch_cache_store_delete
249
+ reload_cache_store
250
+ end
194
251
  end
195
252
 
196
253
  def cache_store_class(k)
@@ -210,101 +267,109 @@ module Datadog
210
267
  end
211
268
 
212
269
  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
270
+ do_once(:patch_cache_store_read) do
271
+ cache_store_class(:read).class_eval do
272
+ alias_method :read_without_datadog, :read
273
+ def read(*args, &block)
274
+ payload = {
275
+ action: 'GET',
276
+ key: args[0],
277
+ tracing_context: {}
278
+ }
279
+
280
+ begin
281
+ # process and catch cache exceptions
282
+ Datadog::Contrib::Rails::ActiveSupport.start_trace_cache(payload)
283
+ read_without_datadog(*args, &block)
284
+ rescue Exception => e
285
+ payload[:exception] = [e.class.name, e.message]
286
+ payload[:exception_object] = e
287
+ raise e
288
+ end
289
+ ensure
290
+ Datadog::Contrib::Rails::ActiveSupport.finish_trace_cache(payload)
230
291
  end
231
- ensure
232
- Datadog::Contrib::Rails::ActiveSupport.finish_trace_cache(payload)
233
292
  end
234
293
  end
235
294
  end
236
295
 
237
296
  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
297
+ do_once(:patch_cache_store_fetch) do
298
+ cache_store_class(:fetch).class_eval do
299
+ alias_method :fetch_without_datadog, :fetch
300
+ def fetch(*args, &block)
301
+ payload = {
302
+ action: 'GET',
303
+ key: args[0],
304
+ tracing_context: {}
305
+ }
306
+
307
+ begin
308
+ # process and catch cache exceptions
309
+ Datadog::Contrib::Rails::ActiveSupport.start_trace_cache(payload)
310
+ fetch_without_datadog(*args, &block)
311
+ rescue Exception => e
312
+ payload[:exception] = [e.class.name, e.message]
313
+ payload[:exception_object] = e
314
+ raise e
315
+ end
316
+ ensure
317
+ Datadog::Contrib::Rails::ActiveSupport.finish_trace_cache(payload)
255
318
  end
256
- ensure
257
- Datadog::Contrib::Rails::ActiveSupport.finish_trace_cache(payload)
258
319
  end
259
320
  end
260
321
  end
261
322
 
262
323
  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
324
+ do_once(:patch_cache_store_write) do
325
+ cache_store_class(:write).class_eval do
326
+ alias_method :write_without_datadog, :write
327
+ def write(*args, &block)
328
+ payload = {
329
+ action: 'SET',
330
+ key: args[0],
331
+ tracing_context: {}
332
+ }
333
+
334
+ begin
335
+ # process and catch cache exceptions
336
+ Datadog::Contrib::Rails::ActiveSupport.start_trace_cache(payload)
337
+ write_without_datadog(*args, &block)
338
+ rescue Exception => e
339
+ payload[:exception] = [e.class.name, e.message]
340
+ payload[:exception_object] = e
341
+ raise e
342
+ end
343
+ ensure
344
+ Datadog::Contrib::Rails::ActiveSupport.finish_trace_cache(payload)
280
345
  end
281
- ensure
282
- Datadog::Contrib::Rails::ActiveSupport.finish_trace_cache(payload)
283
346
  end
284
347
  end
285
348
  end
286
349
 
287
350
  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
351
+ do_once(:patch_cache_store_delete) do
352
+ cache_store_class(:delete).class_eval do
353
+ alias_method :delete_without_datadog, :delete
354
+ def delete(*args, &block)
355
+ payload = {
356
+ action: 'DELETE',
357
+ key: args[0],
358
+ tracing_context: {}
359
+ }
360
+
361
+ begin
362
+ # process and catch cache exceptions
363
+ Datadog::Contrib::Rails::ActiveSupport.start_trace_cache(payload)
364
+ delete_without_datadog(*args, &block)
365
+ rescue Exception => e
366
+ payload[:exception] = [e.class.name, e.message]
367
+ payload[:exception_object] = e
368
+ raise e
369
+ end
370
+ ensure
371
+ Datadog::Contrib::Rails::ActiveSupport.finish_trace_cache(payload)
305
372
  end
306
- ensure
307
- Datadog::Contrib::Rails::ActiveSupport.finish_trace_cache(payload)
308
373
  end
309
374
  end
310
375
  end