ddtrace 0.8.2 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.env +3 -1
- data/.gitignore +1 -0
- data/Appraisals +10 -0
- data/Rakefile +27 -1
- data/ddtrace.gemspec +2 -2
- data/docker-compose.yml +10 -0
- data/docs/GettingStarted.md +119 -0
- data/gemfiles/contrib.gemfile +5 -0
- data/gemfiles/contrib_old.gemfile +4 -0
- data/lib/ddtrace.rb +4 -11
- data/lib/ddtrace/buffer.rb +14 -0
- data/lib/ddtrace/contrib/aws/instrumentation.rb +43 -0
- data/lib/ddtrace/contrib/aws/parsed_context.rb +56 -0
- data/lib/ddtrace/contrib/aws/patcher.rb +56 -0
- data/lib/ddtrace/contrib/aws/services.rb +115 -0
- data/lib/ddtrace/contrib/dalli/instrumentation.rb +35 -0
- data/lib/ddtrace/contrib/dalli/patcher.rb +50 -0
- data/lib/ddtrace/contrib/dalli/quantize.rb +17 -0
- data/lib/ddtrace/contrib/faraday/middleware.rb +75 -0
- data/lib/ddtrace/contrib/faraday/patcher.rb +52 -0
- data/lib/ddtrace/contrib/mongodb/parsers.rb +57 -0
- data/lib/ddtrace/contrib/mongodb/patcher.rb +93 -0
- data/lib/ddtrace/contrib/mongodb/subscribers.rb +71 -0
- data/lib/ddtrace/contrib/rails/action_controller.rb +18 -19
- data/lib/ddtrace/contrib/rails/action_view.rb +51 -61
- data/lib/ddtrace/contrib/rails/active_support.rb +29 -73
- data/lib/ddtrace/contrib/rails/core_extensions.rb +191 -53
- data/lib/ddtrace/contrib/redis/quantize.rb +4 -6
- data/lib/ddtrace/contrib/resque/patcher.rb +38 -0
- data/lib/ddtrace/contrib/resque/resque_job.rb +31 -0
- data/lib/ddtrace/contrib/sucker_punch/exception_handler.rb +26 -0
- data/lib/ddtrace/contrib/sucker_punch/instrumentation.rb +60 -0
- data/lib/ddtrace/contrib/sucker_punch/patcher.rb +50 -0
- data/lib/ddtrace/ext/http.rb +1 -0
- data/lib/ddtrace/ext/mongo.rb +12 -0
- data/lib/ddtrace/monkey.rb +18 -0
- data/lib/ddtrace/pipeline.rb +46 -0
- data/lib/ddtrace/pipeline/span_filter.rb +38 -0
- data/lib/ddtrace/pipeline/span_processor.rb +20 -0
- data/lib/ddtrace/tracer.rb +18 -0
- data/lib/ddtrace/utils.rb +23 -3
- data/lib/ddtrace/version.rb +2 -2
- data/lib/ddtrace/workers.rb +30 -22
- data/lib/ddtrace/writer.rb +5 -7
- metadata +30 -9
@@ -0,0 +1,57 @@
|
|
1
|
+
module Datadog
|
2
|
+
module Contrib
|
3
|
+
# MongoDB module includes classes and functions to instrument MongoDB clients
|
4
|
+
module MongoDB
|
5
|
+
module_function
|
6
|
+
|
7
|
+
# skipped keys are related to command names, since they are already
|
8
|
+
# extracted by the query_builder
|
9
|
+
SKIP_KEYS = [:_id].freeze
|
10
|
+
PLACEHOLDER = '?'.freeze
|
11
|
+
|
12
|
+
# returns a formatted and normalized query
|
13
|
+
def query_builder(command_name, database_name, command)
|
14
|
+
# always skip the command name
|
15
|
+
skip = SKIP_KEYS | [command_name.to_s]
|
16
|
+
|
17
|
+
result = {
|
18
|
+
operation: command_name,
|
19
|
+
database: database_name,
|
20
|
+
collection: command.values.first
|
21
|
+
}
|
22
|
+
|
23
|
+
command.each do |key, value|
|
24
|
+
result[key] = quantize_statement(value, skip) unless skip.include?(key)
|
25
|
+
end
|
26
|
+
|
27
|
+
result
|
28
|
+
end
|
29
|
+
|
30
|
+
# removes the values from the given query; this quantization recursively
|
31
|
+
# replace elements available in a given query, so that Arrays, Hashes and so
|
32
|
+
# on are compacted. It ensures a low cardinality so that it can be used
|
33
|
+
# as a Span resource.
|
34
|
+
def quantize_statement(statement, skip = [])
|
35
|
+
case statement
|
36
|
+
when Hash
|
37
|
+
statement.each_with_object({}) do |(key, value), quantized|
|
38
|
+
quantized[key] = quantize_value(value, skip) unless skip.include?(key)
|
39
|
+
end
|
40
|
+
else
|
41
|
+
quantize_value(statement, skip)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def quantize_value(value, skip = [])
|
46
|
+
case value
|
47
|
+
when Hash
|
48
|
+
quantize_statement(value, skip)
|
49
|
+
when Array
|
50
|
+
quantize_value(value.first, skip)
|
51
|
+
else
|
52
|
+
PLACEHOLDER
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# requirements should be kept minimal as Patcher is a shared requirement.
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
module Contrib
|
5
|
+
# MongoDB module includes classes and functions to instrument MongoDB clients
|
6
|
+
module MongoDB
|
7
|
+
APP = 'mongodb'.freeze
|
8
|
+
SERVICE = 'mongodb'.freeze
|
9
|
+
|
10
|
+
# Patcher adds subscribers to the MongoDB driver so that each command is traced.
|
11
|
+
# Use the `Datadog::Monkey.patch_module(:mongodb)` to activate tracing for
|
12
|
+
# this module.
|
13
|
+
module Patcher
|
14
|
+
@patched = false
|
15
|
+
|
16
|
+
module_function
|
17
|
+
|
18
|
+
def patched?
|
19
|
+
@patched
|
20
|
+
end
|
21
|
+
|
22
|
+
def patch
|
23
|
+
# versions prior to 2.1.0 don't support the Monitoring API
|
24
|
+
if !@patched && (defined?(::Mongo::Monitoring::Global) && \
|
25
|
+
Gem::Version.new(::Mongo::VERSION) >= Gem::Version.new('2.1.0'))
|
26
|
+
begin
|
27
|
+
require 'ddtrace/pin'
|
28
|
+
require 'ddtrace/ext/net'
|
29
|
+
require 'ddtrace/ext/mongo'
|
30
|
+
require 'ddtrace/ext/app_types'
|
31
|
+
require 'ddtrace/contrib/mongodb/parsers'
|
32
|
+
require 'ddtrace/contrib/mongodb/subscribers'
|
33
|
+
|
34
|
+
patch_mongo_client()
|
35
|
+
add_mongo_monitoring()
|
36
|
+
|
37
|
+
@patched = true
|
38
|
+
rescue StandardError => e
|
39
|
+
Datadog::Tracer.log.error("Unable to apply MongoDB integration: #{e}")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
@patched
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_mongo_monitoring
|
46
|
+
# Subscribe to all COMMAND queries with our subscriber class
|
47
|
+
::Mongo::Monitoring::Global.subscribe(
|
48
|
+
::Mongo::Monitoring::COMMAND,
|
49
|
+
Datadog::Contrib::MongoDB::MongoCommandSubscriber.new
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
53
|
+
def patch_mongo_client
|
54
|
+
::Mongo::Client.class_eval do
|
55
|
+
alias_method :initialize_without_datadog, :initialize
|
56
|
+
Datadog::Monkey.without_warnings do
|
57
|
+
remove_method :initialize
|
58
|
+
end
|
59
|
+
|
60
|
+
def initialize(*args, &blk)
|
61
|
+
# attach the Pin instance
|
62
|
+
initialize_without_datadog(*args, &blk)
|
63
|
+
pin = Datadog::Pin.new(SERVICE, app: APP, app_type: Datadog::Ext::AppTypes::DB)
|
64
|
+
pin.onto(self)
|
65
|
+
if pin.tracer && pin.service
|
66
|
+
pin.tracer.set_service_info(pin.service, 'mongodb', pin.app_type)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def datadog_pin
|
71
|
+
# safe-navigation to avoid crashes during each query
|
72
|
+
return unless respond_to? :cluster
|
73
|
+
return unless cluster.respond_to? :addresses
|
74
|
+
return unless cluster.addresses.respond_to? :first
|
75
|
+
Datadog::Pin.get_from(cluster.addresses.first)
|
76
|
+
end
|
77
|
+
|
78
|
+
def datadog_pin=(pin)
|
79
|
+
# safe-navigation to avoid crashes during each query
|
80
|
+
return unless respond_to? :cluster
|
81
|
+
return unless cluster.respond_to? :addresses
|
82
|
+
return unless cluster.addresses.respond_to? :each
|
83
|
+
# attach the PIN to all cluster addresses. One of them is used
|
84
|
+
# when executing a Command and it is attached to the Monitoring
|
85
|
+
# Event instance.
|
86
|
+
cluster.addresses.each { |x| pin.onto(x) }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Datadog
|
2
|
+
module Contrib
|
3
|
+
# MongoDB module includes classes and functions to instrument MongoDB clients
|
4
|
+
module MongoDB
|
5
|
+
# `MongoCommandSubscriber` listens to all events from the `Monitoring`
|
6
|
+
# system available in the Mongo driver.
|
7
|
+
class MongoCommandSubscriber
|
8
|
+
def started(event)
|
9
|
+
pin = Datadog::Pin.get_from(event.address)
|
10
|
+
return unless pin && pin.enabled?
|
11
|
+
|
12
|
+
# start a trace and store it in the current thread; using the `operation_id`
|
13
|
+
# is safe since it's a unique id used to link events together. Also only one
|
14
|
+
# thread is involved in this execution so thread-local storage should be safe. Reference:
|
15
|
+
# https://github.com/mongodb/mongo-ruby-driver/blob/master/lib/mongo/monitoring.rb#L70
|
16
|
+
# https://github.com/mongodb/mongo-ruby-driver/blob/master/lib/mongo/monitoring/publishable.rb#L38-L56
|
17
|
+
span = pin.tracer.trace('mongo.cmd', service: pin.service, span_type: Datadog::Ext::Mongo::TYPE)
|
18
|
+
Thread.current[:datadog_mongo_span] = span
|
19
|
+
|
20
|
+
# build a quantized Query using the Parser module
|
21
|
+
query = Datadog::Contrib::MongoDB.query_builder(event.command_name, event.database_name, event.command)
|
22
|
+
serialized_query = query.to_s
|
23
|
+
|
24
|
+
# add operation tags; the full query is stored and used as a resource,
|
25
|
+
# since it has been quantized and reduced
|
26
|
+
span.set_tag(Datadog::Ext::Mongo::DB, query[:database])
|
27
|
+
span.set_tag(Datadog::Ext::Mongo::COLLECTION, query[:collection])
|
28
|
+
span.set_tag(Datadog::Ext::Mongo::OPERATION, query[:operation])
|
29
|
+
span.set_tag(Datadog::Ext::Mongo::QUERY, serialized_query)
|
30
|
+
span.set_tag(Datadog::Ext::NET::TARGET_HOST, event.address.host)
|
31
|
+
span.set_tag(Datadog::Ext::NET::TARGET_PORT, event.address.port)
|
32
|
+
|
33
|
+
# set the resource with the quantized query
|
34
|
+
span.resource = serialized_query
|
35
|
+
end
|
36
|
+
|
37
|
+
def failed(event)
|
38
|
+
span = Thread.current[:datadog_mongo_span]
|
39
|
+
return unless span
|
40
|
+
|
41
|
+
# the failure is not a real exception because it's handled by
|
42
|
+
# the framework itself, so we set only the error and the message
|
43
|
+
span.set_error(event)
|
44
|
+
rescue StandardError => e
|
45
|
+
Datadog::Tracer.log.debug("error when handling MongoDB 'failed' event: #{e}")
|
46
|
+
ensure
|
47
|
+
# whatever happens, the Span must be removed from the local storage and
|
48
|
+
# it must be finished to prevent any leak
|
49
|
+
span.finish() unless span.nil?
|
50
|
+
Thread.current[:datadog_mongo_span] = nil
|
51
|
+
end
|
52
|
+
|
53
|
+
def succeeded(event)
|
54
|
+
span = Thread.current[:datadog_mongo_span]
|
55
|
+
return unless span
|
56
|
+
|
57
|
+
# add fields that are available only after executing the query
|
58
|
+
rows = event.reply.fetch('n', nil)
|
59
|
+
span.set_tag(Datadog::Ext::Mongo::ROWS, rows) unless rows.nil?
|
60
|
+
rescue StandardError => e
|
61
|
+
Datadog::Tracer.log.debug("error when handling MongoDB 'succeeded' event: #{e}")
|
62
|
+
ensure
|
63
|
+
# whatever happens, the Span must be removed from the local storage and
|
64
|
+
# it must be finished to prevent any leak
|
65
|
+
span.finish() unless span.nil?
|
66
|
+
Thread.current[:datadog_mongo_span] = nil
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -6,40 +6,40 @@ module Datadog
|
|
6
6
|
module Rails
|
7
7
|
# Code used to create and handle 'rails.action_controller' spans.
|
8
8
|
module ActionController
|
9
|
-
KEY = 'datadog_actioncontroller'.freeze
|
10
|
-
|
11
9
|
def self.instrument
|
10
|
+
# patch Rails core components
|
11
|
+
Datadog::RailsActionPatcher.patch_action_controller
|
12
|
+
|
12
13
|
# subscribe when the request processing starts
|
13
|
-
::ActiveSupport::Notifications.subscribe('start_processing.action_controller') do |*args|
|
14
|
+
::ActiveSupport::Notifications.subscribe('!datadog.start_processing.action_controller') do |*args|
|
14
15
|
start_processing(*args)
|
15
16
|
end
|
16
17
|
|
17
18
|
# subscribe when the request processing has been completed
|
18
|
-
::ActiveSupport::Notifications.subscribe('
|
19
|
-
|
19
|
+
::ActiveSupport::Notifications.subscribe('!datadog.finish_processing.action_controller') do |*args|
|
20
|
+
finish_processing(*args)
|
20
21
|
end
|
21
22
|
end
|
22
23
|
|
23
|
-
def self.start_processing(
|
24
|
-
|
25
|
-
|
24
|
+
def self.start_processing(_name, _start, _finish, _id, payload)
|
25
|
+
# trace the execution
|
26
26
|
tracer = ::Rails.configuration.datadog_trace.fetch(:tracer)
|
27
27
|
service = ::Rails.configuration.datadog_trace.fetch(:default_controller_service)
|
28
28
|
type = Datadog::Ext::HTTP::TYPE
|
29
|
-
tracer.trace('rails.action_controller', service: service, span_type: type)
|
29
|
+
span = tracer.trace('rails.action_controller', service: service, span_type: type)
|
30
30
|
|
31
|
-
|
31
|
+
# attach the current span to the tracing context
|
32
|
+
tracing_context = payload.fetch(:tracing_context)
|
33
|
+
tracing_context[:dd_request_span] = span
|
32
34
|
rescue StandardError => e
|
33
35
|
Datadog::Tracer.log.error(e.message)
|
34
36
|
end
|
35
37
|
|
36
|
-
def self.
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
span = tracer.active_span()
|
42
|
-
return unless span
|
38
|
+
def self.finish_processing(_name, start, finish, _id, payload)
|
39
|
+
# retrieve the tracing context and the latest active span
|
40
|
+
tracing_context = payload.fetch(:tracing_context)
|
41
|
+
span = tracing_context[:dd_request_span]
|
42
|
+
return unless span && !span.finished?
|
43
43
|
|
44
44
|
begin
|
45
45
|
resource = "#{payload.fetch(:controller)}##{payload.fetch(:action)}"
|
@@ -69,8 +69,7 @@ module Datadog
|
|
69
69
|
span.set_error(error) if status.starts_with?('5')
|
70
70
|
end
|
71
71
|
ensure
|
72
|
-
span.
|
73
|
-
span.finish(finish)
|
72
|
+
span.finish()
|
74
73
|
end
|
75
74
|
rescue StandardError => e
|
76
75
|
Datadog::Tracer.log.error(e.message)
|
@@ -10,99 +10,89 @@ module Datadog
|
|
10
10
|
Datadog::RailsRendererPatcher.patch_renderer()
|
11
11
|
|
12
12
|
# subscribe when the template rendering starts
|
13
|
-
::ActiveSupport::Notifications.subscribe('start_render_template.action_view') do |*args|
|
13
|
+
::ActiveSupport::Notifications.subscribe('!datadog.start_render_template.action_view') do |*args|
|
14
14
|
start_render_template(*args)
|
15
15
|
end
|
16
16
|
|
17
|
-
# subscribe when the
|
18
|
-
::ActiveSupport::Notifications.subscribe('
|
19
|
-
|
17
|
+
# subscribe when the template rendering has been processed
|
18
|
+
::ActiveSupport::Notifications.subscribe('!datadog.finish_render_template.action_view') do |*args|
|
19
|
+
finish_render_template(*args)
|
20
20
|
end
|
21
21
|
|
22
|
-
# subscribe when the
|
23
|
-
::ActiveSupport::Notifications.subscribe('
|
24
|
-
|
22
|
+
# subscribe when the partial rendering starts
|
23
|
+
::ActiveSupport::Notifications.subscribe('!datadog.start_render_partial.action_view') do |*args|
|
24
|
+
start_render_partial(*args)
|
25
25
|
end
|
26
26
|
|
27
27
|
# subscribe when the partial rendering has been processed
|
28
|
-
::ActiveSupport::Notifications.subscribe('
|
29
|
-
|
28
|
+
::ActiveSupport::Notifications.subscribe('!datadog.finish_render_partial.action_view') do |*args|
|
29
|
+
finish_render_partial(*args)
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
def self.
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
def self.start_render_template(*)
|
38
|
-
key = get_key('render_template')
|
39
|
-
return if Thread.current[key]
|
33
|
+
def self.start_render_template(_name, _start, _finish, _id, payload)
|
34
|
+
# retrieve the tracing context
|
35
|
+
tracing_context = payload.fetch(:tracing_context)
|
40
36
|
|
37
|
+
# create a new Span and add it to the tracing context
|
41
38
|
tracer = ::Rails.configuration.datadog_trace.fetch(:tracer)
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
Thread.current[key] = true
|
39
|
+
span = tracer.trace('rails.render_template', span_type: Datadog::Ext::HTTP::TEMPLATE)
|
40
|
+
tracing_context[:dd_rails_template_span] = span
|
46
41
|
rescue StandardError => e
|
47
|
-
Datadog::Tracer.log.
|
42
|
+
Datadog::Tracer.log.debug(e.message)
|
48
43
|
end
|
49
44
|
|
50
|
-
def self.
|
51
|
-
|
52
|
-
|
45
|
+
def self.finish_render_template(_name, _start, _finish, _id, payload)
|
46
|
+
# retrieve the tracing context and the latest active span
|
47
|
+
tracing_context = payload.fetch(:tracing_context)
|
48
|
+
span = tracing_context[:dd_rails_template_span]
|
49
|
+
return if !span || span.finished?
|
53
50
|
|
54
|
-
|
55
|
-
|
56
|
-
|
51
|
+
# finish the tracing and update the execution time
|
52
|
+
begin
|
53
|
+
template_name = tracing_context[:template_name]
|
54
|
+
layout = tracing_context[:layout]
|
55
|
+
exception = tracing_context[:exception]
|
57
56
|
|
58
|
-
|
57
|
+
span.set_tag('rails.template_name', template_name) if template_name
|
58
|
+
span.set_tag('rails.layout', layout) if layout
|
59
|
+
span.set_error(exception) if exception
|
60
|
+
ensure
|
61
|
+
span.finish()
|
62
|
+
end
|
59
63
|
rescue StandardError => e
|
60
|
-
Datadog::Tracer.log.
|
64
|
+
Datadog::Tracer.log.debug(e.message)
|
61
65
|
end
|
62
66
|
|
63
|
-
def self.
|
64
|
-
|
65
|
-
|
66
|
-
Thread.current[key] = false
|
67
|
+
def self.start_render_partial(_name, _start, _finish, _id, payload)
|
68
|
+
# retrieve the tracing context
|
69
|
+
tracing_context = payload.fetch(:tracing_context)
|
67
70
|
|
68
|
-
# finish the tracing and update the execution time
|
69
71
|
tracer = ::Rails.configuration.datadog_trace.fetch(:tracer)
|
70
|
-
span = tracer.
|
71
|
-
|
72
|
-
|
73
|
-
begin
|
74
|
-
template_name = Datadog::Contrib::Rails::Utils.normalize_template_name(payload.fetch(:identifier))
|
75
|
-
span.set_tag('rails.template_name', template_name)
|
76
|
-
span.set_tag('rails.layout', payload.fetch(:layout))
|
77
|
-
span.set_error(payload[:exception]) if payload[:exception]
|
78
|
-
ensure
|
79
|
-
span.start_time = start
|
80
|
-
span.finish(finish)
|
81
|
-
end
|
72
|
+
span = tracer.trace('rails.render_partial', span_type: Datadog::Ext::HTTP::TEMPLATE)
|
73
|
+
tracing_context[:dd_rails_partial_span] = span
|
82
74
|
rescue StandardError => e
|
83
|
-
Datadog::Tracer.log.
|
75
|
+
Datadog::Tracer.log.debug(e.message)
|
84
76
|
end
|
85
77
|
|
86
|
-
def self.
|
87
|
-
|
88
|
-
|
89
|
-
|
78
|
+
def self.finish_render_partial(_name, start, finish, _id, payload)
|
79
|
+
# retrieve the tracing context and the latest active span
|
80
|
+
tracing_context = payload.fetch(:tracing_context)
|
81
|
+
span = tracing_context[:dd_rails_partial_span]
|
82
|
+
return if !span || span.finished?
|
90
83
|
|
91
84
|
# finish the tracing and update the execution time
|
92
|
-
tracer = ::Rails.configuration.datadog_trace.fetch(:tracer)
|
93
|
-
span = tracer.active_span()
|
94
|
-
return unless span
|
95
|
-
|
96
85
|
begin
|
97
|
-
template_name =
|
98
|
-
|
99
|
-
|
86
|
+
template_name = tracing_context[:template_name]
|
87
|
+
exception = tracing_context[:exception]
|
88
|
+
|
89
|
+
span.set_tag('rails.template_name', template_name) if template_name
|
90
|
+
span.set_error(exception) if exception
|
100
91
|
ensure
|
101
|
-
span.
|
102
|
-
span.finish(finish)
|
92
|
+
span.finish()
|
103
93
|
end
|
104
94
|
rescue StandardError => e
|
105
|
-
Datadog::Tracer.log.
|
95
|
+
Datadog::Tracer.log.debug(e.message)
|
106
96
|
end
|
107
97
|
end
|
108
98
|
end
|
@@ -11,102 +11,58 @@ module Datadog
|
|
11
11
|
Datadog::RailsCachePatcher.patch_cache_store()
|
12
12
|
|
13
13
|
# subscribe when a cache read starts being processed
|
14
|
-
::ActiveSupport::Notifications.subscribe('
|
15
|
-
start_trace_cache(
|
16
|
-
end
|
17
|
-
|
18
|
-
# subscribe when a cache fetch starts being processed
|
19
|
-
::ActiveSupport::Notifications.subscribe('start_cache_fetch.active_support') do |*args|
|
20
|
-
start_trace_cache('GET', *args)
|
21
|
-
end
|
22
|
-
|
23
|
-
# subscribe when a cache write starts being processed
|
24
|
-
::ActiveSupport::Notifications.subscribe('start_cache_write.active_support') do |*args|
|
25
|
-
start_trace_cache('SET', *args)
|
26
|
-
end
|
27
|
-
|
28
|
-
# subscribe when a cache delete starts being processed
|
29
|
-
::ActiveSupport::Notifications.subscribe('start_cache_delete.active_support') do |*args|
|
30
|
-
start_trace_cache('DELETE', *args)
|
14
|
+
::ActiveSupport::Notifications.subscribe('!datadog.start_cache_tracing.active_support') do |*args|
|
15
|
+
start_trace_cache(*args)
|
31
16
|
end
|
32
17
|
|
33
18
|
# subscribe when a cache read has been processed
|
34
|
-
::ActiveSupport::Notifications.subscribe('
|
35
|
-
|
19
|
+
::ActiveSupport::Notifications.subscribe('!datadog.finish_cache_tracing.active_support') do |*args|
|
20
|
+
finish_trace_cache(*args)
|
36
21
|
end
|
22
|
+
end
|
37
23
|
|
38
|
-
|
39
|
-
::
|
40
|
-
|
41
|
-
end
|
24
|
+
def self.start_trace_cache(_name, _start, _finish, _id, payload)
|
25
|
+
tracer = ::Rails.configuration.datadog_trace.fetch(:tracer)
|
26
|
+
tracing_context = payload.fetch(:tracing_context)
|
42
27
|
|
43
|
-
#
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
28
|
+
# In most of the cases Rails ``fetch()`` and ``read()`` calls are nested.
|
29
|
+
# This check ensures that two reads are not nested since they don't provide
|
30
|
+
# interesting details.
|
31
|
+
# NOTE: the ``finish_trace_cache()`` is fired but it already has a safe-guard
|
32
|
+
# to avoid any kind of issue.
|
33
|
+
current_span = tracer.active_span
|
34
|
+
return if current_span.try('name') == 'rails.cache' &&
|
35
|
+
current_span.try('resource') == 'GET' &&
|
36
|
+
payload[:action] == 'GET'
|
48
37
|
|
49
|
-
|
38
|
+
# create a new ``Span`` and add it to the tracing context
|
50
39
|
service = ::Rails.configuration.datadog_trace.fetch(:default_cache_service)
|
51
40
|
type = Datadog::Ext::CACHE::TYPE
|
52
|
-
tracer.trace('rails.cache', service: service, span_type: type)
|
53
|
-
|
54
|
-
|
55
|
-
def self.get_key(resource)
|
56
|
-
'datadog_activesupport_' + resource
|
57
|
-
end
|
58
|
-
|
59
|
-
def self.start_trace_cache(resource, *_args)
|
60
|
-
key = get_key(resource)
|
61
|
-
# This is mostly to trap the case of fetch/read. In some cases the framework
|
62
|
-
# will call fetch but fetch won't call read. In some cases read can be called
|
63
|
-
# alone. And in some cases they are nested. In all cases we want to have one
|
64
|
-
# and only one span.
|
65
|
-
return if Thread.current[key]
|
66
|
-
create_span(::Rails.configuration.datadog_trace.fetch(:tracer))
|
67
|
-
Thread.current[key] = true
|
41
|
+
span = tracer.trace('rails.cache', service: service, span_type: type)
|
42
|
+
span.resource = payload.fetch(:action)
|
43
|
+
tracing_context[:dd_cache_span] = span
|
68
44
|
rescue StandardError => e
|
69
|
-
Datadog::Tracer.log.
|
45
|
+
Datadog::Tracer.log.debug(e.message)
|
70
46
|
end
|
71
47
|
|
72
|
-
def self.
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
Thread.current[key] = false
|
78
|
-
else
|
79
|
-
# Create a span now, as start_trace_cache was not called.
|
80
|
-
#
|
81
|
-
# This could typically happen if, for some reason the monkey-patching
|
82
|
-
# of the cache class did not work as expected. Doing this, we might
|
83
|
-
# loose some interesting parentship between some spans, because this
|
84
|
-
# span is created too late, and children won't "find" their parent.
|
85
|
-
# But, it's better than no span at all, and it case there is no child
|
86
|
-
# at all, it will work just as expected. In practice, it's required to
|
87
|
-
# have standard file cache work together with redis cache.
|
88
|
-
create_span(tracer)
|
89
|
-
end
|
90
|
-
span = tracer.active_span()
|
91
|
-
return unless span
|
48
|
+
def self.finish_trace_cache(_name, _start, _finish, _id, payload)
|
49
|
+
# retrieve the tracing context and continue the trace
|
50
|
+
tracing_context = payload.fetch(:tracing_context)
|
51
|
+
span = tracing_context[:dd_cache_span]
|
52
|
+
return unless span && !span.finished?
|
92
53
|
|
93
54
|
begin
|
94
|
-
# finish the tracing and update the execution time
|
95
|
-
span.resource = resource
|
96
55
|
# discard parameters from the cache_store configuration
|
97
56
|
store, = *Array.wrap(::Rails.configuration.cache_store).flatten
|
98
57
|
span.set_tag('rails.cache.backend', store)
|
99
58
|
span.set_tag('rails.cache.key', payload.fetch(:key))
|
100
59
|
span.set_error(payload[:exception]) if payload[:exception]
|
101
60
|
ensure
|
102
|
-
span.
|
103
|
-
span.finish(finish)
|
61
|
+
span.finish()
|
104
62
|
end
|
105
63
|
rescue StandardError => e
|
106
|
-
Datadog::Tracer.log.
|
64
|
+
Datadog::Tracer.log.debug(e.message)
|
107
65
|
end
|
108
|
-
|
109
|
-
private_class_method :create_span, :get_key, :start_trace_cache, :trace_cache
|
110
66
|
end
|
111
67
|
end
|
112
68
|
end
|