ddtrace 0.8.2 → 0.9.0
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/.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
|