ddtrace 0.12.0.beta2 → 0.12.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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