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.
- checksums.yaml +4 -4
- data/Appraisals +8 -8
- data/CHANGELOG.md +293 -0
- data/README.md +11 -114
- data/Rakefile +26 -18
- data/docs/GettingStarted.md +704 -453
- data/gemfiles/contrib.gemfile +2 -2
- data/gemfiles/rails4_mysql2.gemfile +1 -1
- data/gemfiles/rails5_mysql2.gemfile +2 -2
- data/gemfiles/rails5_postgres.gemfile +1 -1
- data/gemfiles/rails5_postgres_redis.gemfile +1 -1
- data/gemfiles/rails5_postgres_sidekiq.gemfile +1 -1
- data/lib/ddtrace.rb +1 -0
- data/lib/ddtrace/context.rb +96 -34
- data/lib/ddtrace/context_flush.rb +132 -0
- data/lib/ddtrace/contrib/active_record/patcher.rb +55 -70
- data/lib/ddtrace/contrib/active_record/utils.rb +83 -0
- data/lib/ddtrace/contrib/active_support/notifications/subscriber.rb +66 -0
- data/lib/ddtrace/contrib/active_support/notifications/subscription.rb +155 -0
- data/lib/ddtrace/contrib/elasticsearch/patcher.rb +6 -1
- data/lib/ddtrace/contrib/elasticsearch/quantize.rb +89 -0
- data/lib/ddtrace/contrib/grape/endpoint.rb +1 -1
- data/lib/ddtrace/contrib/racecar/patcher.rb +43 -19
- data/lib/ddtrace/contrib/rack/middlewares.rb +58 -11
- data/lib/ddtrace/contrib/rack/patcher.rb +18 -11
- data/lib/ddtrace/contrib/rails/action_controller.rb +9 -11
- data/lib/ddtrace/contrib/rails/action_view.rb +5 -1
- data/lib/ddtrace/contrib/rails/active_support.rb +6 -2
- data/lib/ddtrace/contrib/rails/core_extensions.rb +280 -215
- data/lib/ddtrace/contrib/rails/framework.rb +38 -23
- data/lib/ddtrace/contrib/rails/middlewares.rb +7 -2
- data/lib/ddtrace/contrib/rails/patcher.rb +9 -6
- data/lib/ddtrace/contrib/rails/railtie.rb +4 -2
- data/lib/ddtrace/contrib/rails/utils.rb +9 -40
- data/lib/ddtrace/patcher.rb +32 -10
- data/lib/ddtrace/quantization/http.rb +86 -0
- data/lib/ddtrace/tracer.rb +29 -2
- data/lib/ddtrace/transport.rb +33 -20
- data/lib/ddtrace/version.rb +1 -1
- data/lib/ddtrace/writer.rb +11 -5
- metadata +8 -3
- 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[
|
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
|
-
#
|
85
|
-
#
|
86
|
-
#
|
87
|
-
#
|
88
|
-
#
|
89
|
-
|
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
|
-
|
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
|
-
|
21
|
+
unless patched?
|
22
|
+
require_relative 'middlewares'
|
23
|
+
@patched = true
|
24
|
+
end
|
21
25
|
|
22
|
-
|
23
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
55
|
-
|
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
|
-
|
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
|
-
|
11
|
-
|
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
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
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
|
-
#
|
65
|
-
|
66
|
-
|
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
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
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
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
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
|
-
|
102
|
-
|
103
|
-
|
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
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
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
|
-
#
|
115
|
-
|
116
|
-
|
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
|
-
|
119
|
-
|
120
|
-
|
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
|
-
|
126
|
-
|
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
|
-
|
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
|
-
|
148
|
-
|
149
|
-
#
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
#
|
157
|
-
#
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
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
|
-
|
179
|
-
|
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
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
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
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
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
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
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
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
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
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
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
|