ddtrace 0.17.3 → 0.18.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 +2 -2
- data/Appraisals +4 -0
- data/CHANGELOG.md +21 -2
- data/Rakefile +31 -29
- data/docker-compose.yml +7 -7
- data/docs/GettingStarted.md +26 -2
- data/lib/ddtrace.rb +4 -0
- data/lib/ddtrace/augmentation.rb +13 -0
- data/lib/ddtrace/augmentation/method_wrapper.rb +20 -0
- data/lib/ddtrace/augmentation/method_wrapping.rb +38 -0
- data/lib/ddtrace/augmentation/shim.rb +102 -0
- data/lib/ddtrace/contrib/active_record/events/sql.rb +6 -2
- data/lib/ddtrace/contrib/active_record/patcher.rb +2 -0
- data/lib/ddtrace/contrib/active_record/patches/abstract_adapter.rb +72 -0
- data/lib/ddtrace/contrib/active_record/utils.rb +8 -18
- data/lib/ddtrace/contrib/aws/instrumentation.rb +15 -11
- data/lib/ddtrace/contrib/aws/patcher.rb +0 -11
- data/lib/ddtrace/contrib/configurable.rb +13 -9
- data/lib/ddtrace/contrib/rack/request_queue.rb +5 -1
- data/lib/ddtrace/contrib/shoryuken/configuration/settings.rb +14 -0
- data/lib/ddtrace/contrib/shoryuken/ext.rb +18 -0
- data/lib/ddtrace/contrib/shoryuken/integration.rb +35 -0
- data/lib/ddtrace/contrib/shoryuken/patcher.rb +30 -0
- data/lib/ddtrace/contrib/shoryuken/tracer.rb +37 -0
- data/lib/ddtrace/contrib/sidekiq/client_tracer.rb +35 -0
- data/lib/ddtrace/contrib/sidekiq/configuration/settings.rb +1 -0
- data/lib/ddtrace/contrib/sidekiq/ext.rb +2 -0
- data/lib/ddtrace/contrib/sidekiq/patcher.rb +9 -2
- data/lib/ddtrace/contrib/sidekiq/server_tracer.rb +50 -0
- data/lib/ddtrace/contrib/sidekiq/tracing.rb +38 -0
- data/lib/ddtrace/sync_writer.rb +2 -1
- data/lib/ddtrace/transport.rb +6 -3
- data/lib/ddtrace/utils/database.rb +7 -3
- data/lib/ddtrace/version.rb +2 -2
- data/lib/ddtrace/writer.rb +1 -4
- metadata +15 -3
- data/lib/ddtrace/contrib/sidekiq/tracer.rb +0 -72
data/lib/ddtrace.rb
CHANGED
@@ -9,9 +9,12 @@ require 'ddtrace/quantization/http'
|
|
9
9
|
require 'ddtrace/pipeline'
|
10
10
|
require 'ddtrace/configuration'
|
11
11
|
require 'ddtrace/patcher'
|
12
|
+
require 'ddtrace/augmentation'
|
12
13
|
|
13
14
|
# \Datadog global namespace that includes all tracing functionality for Tracer and Span classes.
|
14
15
|
module Datadog
|
16
|
+
extend Augmentation
|
17
|
+
|
15
18
|
@tracer = Tracer.new
|
16
19
|
@registry = Registry.new
|
17
20
|
@configuration = Configuration.new(registry: @registry)
|
@@ -68,6 +71,7 @@ require 'ddtrace/contrib/redis/integration'
|
|
68
71
|
require 'ddtrace/contrib/resque/integration'
|
69
72
|
require 'ddtrace/contrib/rest_client/integration'
|
70
73
|
require 'ddtrace/contrib/sequel/integration'
|
74
|
+
require 'ddtrace/contrib/shoryuken/integration'
|
71
75
|
require 'ddtrace/contrib/sidekiq/integration'
|
72
76
|
require 'ddtrace/contrib/sinatra/integration'
|
73
77
|
require 'ddtrace/contrib/sucker_punch/integration'
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'ddtrace/augmentation/method_wrapper'
|
2
|
+
require 'ddtrace/augmentation/method_wrapping'
|
3
|
+
require 'ddtrace/augmentation/shim'
|
4
|
+
|
5
|
+
module Datadog
|
6
|
+
# Namespace for components that help modify
|
7
|
+
# existing code for instrumentation purposes.
|
8
|
+
module Augmentation
|
9
|
+
def shim(object, &block)
|
10
|
+
Shim.new(object, &block)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Datadog
|
2
|
+
# Represents an wrapped method, with a reference to the original block
|
3
|
+
# and the block that wraps around it.
|
4
|
+
class MethodWrapper
|
5
|
+
attr_reader \
|
6
|
+
:original,
|
7
|
+
:wrapper
|
8
|
+
|
9
|
+
DEFAULT_WRAPPER = proc { |original, *args, &block| original.call(*args, &block) }
|
10
|
+
|
11
|
+
def initialize(original, &block)
|
12
|
+
@original = original
|
13
|
+
@wrapper = block_given? ? block : DEFAULT_WRAPPER
|
14
|
+
end
|
15
|
+
|
16
|
+
def call(*args, &block)
|
17
|
+
wrapper.call(original, *args, &block)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'ddtrace/patcher'
|
3
|
+
|
4
|
+
module Datadog
|
5
|
+
# Shorthands for wrapping methods
|
6
|
+
module MethodWrapping
|
7
|
+
include Datadog::Patcher
|
8
|
+
|
9
|
+
def wrapped_methods
|
10
|
+
@wrapped_methods ||= Set.new
|
11
|
+
end
|
12
|
+
|
13
|
+
# Adds method block directly to the object.
|
14
|
+
# Block is evaluated in the context of the object.
|
15
|
+
# Faster than #wrap_method!
|
16
|
+
def override_method!(method_name, &block)
|
17
|
+
return unless block_given?
|
18
|
+
|
19
|
+
without_warnings do
|
20
|
+
define_singleton_method(method_name, &block).tap do
|
21
|
+
wrapped_methods.add(method_name)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Adds method wrapper to the object.
|
27
|
+
# Block is evaluated in the original context of the block.
|
28
|
+
# Slower than #override_method!
|
29
|
+
def wrap_method!(original_method, &block)
|
30
|
+
return unless block_given?
|
31
|
+
original_method = original_method.is_a?(Symbol) ? method(original_method) : original_method
|
32
|
+
|
33
|
+
override_method!(original_method.name) do |*original_args, &original_block|
|
34
|
+
block.call(original_method, *original_args, &original_block)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'ddtrace/patcher'
|
3
|
+
require 'ddtrace/augmentation/method_wrapping'
|
4
|
+
|
5
|
+
module Datadog
|
6
|
+
# A "stand-in" that intercepts calls to another object. i.e. man-in-the-middle.
|
7
|
+
# This shim forwards all methods to object, except those overriden.
|
8
|
+
# Useful if you want to intercept inbound behavior to an object without modifying
|
9
|
+
# the object in question, especially useful if the overridding behavior shouldn't be global.
|
10
|
+
class Shim
|
11
|
+
extend Forwardable
|
12
|
+
include Datadog::Patcher
|
13
|
+
include Datadog::MethodWrapping
|
14
|
+
|
15
|
+
METHODS = Set[
|
16
|
+
:override_method!,
|
17
|
+
:shim,
|
18
|
+
:shim?,
|
19
|
+
:shim_target,
|
20
|
+
:wrap_method!,
|
21
|
+
:wrapped_methods
|
22
|
+
].freeze
|
23
|
+
|
24
|
+
EXCLUDED_METHODS = Set[
|
25
|
+
# For all objects
|
26
|
+
:__binding__,
|
27
|
+
:__id__,
|
28
|
+
:__send__,
|
29
|
+
:extend,
|
30
|
+
:itself,
|
31
|
+
:object_id,
|
32
|
+
:respond_to?,
|
33
|
+
:tap
|
34
|
+
].freeze
|
35
|
+
|
36
|
+
attr_reader :shim_target, :shim
|
37
|
+
|
38
|
+
def self.shim?(object)
|
39
|
+
# Check whether it responds to #shim? because otherwise the
|
40
|
+
# Shim forwards all method calls, including type checks to
|
41
|
+
# the wrapped object, to mimimize its intrusion.
|
42
|
+
object.respond_to?(:shim?)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Pass this a block to override methods
|
46
|
+
def initialize(shim_target)
|
47
|
+
@shim = self
|
48
|
+
@shim_target = shim_target
|
49
|
+
|
50
|
+
# Save a reference to the original :define_singleton_method
|
51
|
+
# so methods can be defined on the shim after forwarding is applied.
|
52
|
+
@definition_method = method(:define_singleton_method)
|
53
|
+
|
54
|
+
# Wrap any methods
|
55
|
+
yield(self) if block_given?
|
56
|
+
|
57
|
+
# Forward methods
|
58
|
+
forwarded_methods = (
|
59
|
+
shim_target.public_methods.to_set \
|
60
|
+
- METHODS \
|
61
|
+
- EXCLUDED_METHODS \
|
62
|
+
- wrapped_methods
|
63
|
+
)
|
64
|
+
forward_methods!(*forwarded_methods)
|
65
|
+
end
|
66
|
+
|
67
|
+
def override_method!(method_name, &block)
|
68
|
+
return unless block_given?
|
69
|
+
|
70
|
+
without_warnings do
|
71
|
+
@definition_method.call(method_name, &block).tap do
|
72
|
+
wrapped_methods.add(method_name)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def wrap_method!(method_name, &block)
|
78
|
+
super(shim_target.method(method_name), &block)
|
79
|
+
end
|
80
|
+
|
81
|
+
def shim?
|
82
|
+
true
|
83
|
+
end
|
84
|
+
|
85
|
+
def respond_to?(method_name)
|
86
|
+
return true if METHODS.include?(method_name)
|
87
|
+
shim_target.respond_to?(method_name)
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def forward_methods!(*forwarded_methods)
|
93
|
+
return if forwarded_methods.empty?
|
94
|
+
|
95
|
+
singleton_class.send(
|
96
|
+
:def_delegators,
|
97
|
+
:@shim_target,
|
98
|
+
*forwarded_methods
|
99
|
+
)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -24,10 +24,14 @@ module Datadog
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def process(span, event, _id, payload)
|
27
|
-
config = Utils.connection_config(payload[:
|
27
|
+
config = Utils.connection_config(payload[:connection])
|
28
28
|
settings = Datadog.configuration[:active_record, config]
|
29
29
|
adapter_name = Datadog::Utils::Database.normalize_vendor(config[:adapter])
|
30
|
-
service_name =
|
30
|
+
service_name = if settings.service_name != Datadog::Utils::Database::VENDOR_DEFAULT
|
31
|
+
settings.service_name
|
32
|
+
else
|
33
|
+
adapter_name
|
34
|
+
end
|
31
35
|
|
32
36
|
span.name = "#{adapter_name}.query"
|
33
37
|
span.service = service_name
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'ddtrace/contrib/patcher'
|
2
|
+
require 'ddtrace/contrib/active_record/patches/abstract_adapter'
|
2
3
|
require 'ddtrace/contrib/active_record/events'
|
3
4
|
|
4
5
|
module Datadog
|
@@ -17,6 +18,7 @@ module Datadog
|
|
17
18
|
def patch
|
18
19
|
do_once(:active_record) do
|
19
20
|
begin
|
21
|
+
::ActiveRecord::ConnectionAdapters::AbstractAdapter.send(:include, Patches::AbstractAdapter)
|
20
22
|
Events.subscribe!
|
21
23
|
rescue StandardError => e
|
22
24
|
Datadog::Tracer.log.error("Unable to apply Active Record integration: #{e}")
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'ddtrace/augmentation/shim'
|
3
|
+
|
4
|
+
module Datadog
|
5
|
+
module Contrib
|
6
|
+
module ActiveRecord
|
7
|
+
# Defines basic behaviors for an ActiveRecord event.
|
8
|
+
module Patches
|
9
|
+
# Adds patch to AbstractAdapter to make it pass more information through
|
10
|
+
# ActiveSupport notifications, for better instrumentation.
|
11
|
+
module AbstractAdapter
|
12
|
+
def self.included(base)
|
13
|
+
if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.0.0')
|
14
|
+
base.class_eval do
|
15
|
+
alias_method :log_without_datadog, :log
|
16
|
+
remove_method :log
|
17
|
+
include InstanceMethods
|
18
|
+
end
|
19
|
+
else
|
20
|
+
base.send(:prepend, InstanceMethods)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Compatibility shim for Rubies not supporting `.prepend`
|
25
|
+
module InstanceMethodsCompatibility
|
26
|
+
def log(*args, &block)
|
27
|
+
log_without_datadog(*args, &block)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# InstanceMethods - implementing instrumentation
|
32
|
+
module InstanceMethods
|
33
|
+
include InstanceMethodsCompatibility unless Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.0.0')
|
34
|
+
|
35
|
+
EVENT_ACTIVERECORD_SQL = 'sql.active_record'.freeze
|
36
|
+
|
37
|
+
# Override #log since sometimes connections are initialized prior
|
38
|
+
# to when the patch is applied; this will allow existing connections
|
39
|
+
# to receive the Shim as well.
|
40
|
+
def log(*args, &block)
|
41
|
+
insert_shim! unless shim_inserted?
|
42
|
+
super
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def shim_inserted?
|
48
|
+
instance_variable_defined?(:@instrumenter) \
|
49
|
+
&& Datadog::Shim.shim?(@instrumenter)
|
50
|
+
end
|
51
|
+
|
52
|
+
def insert_shim!
|
53
|
+
@instrumenter = Datadog::Shim.new(@instrumenter) do |shim|
|
54
|
+
connection = self
|
55
|
+
|
56
|
+
shim.override_method!(:instrument) do |*args, &block|
|
57
|
+
# Inject connection into arguments
|
58
|
+
if args[0] == EVENT_ACTIVERECORD_SQL && args[1].is_a?(Hash)
|
59
|
+
args[1][:connection] ||= connection
|
60
|
+
end
|
61
|
+
|
62
|
+
# Call original
|
63
|
+
shim_target.instrument(*args, &block)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -3,6 +3,8 @@ module Datadog
|
|
3
3
|
module ActiveRecord
|
4
4
|
# Common utilities for Rails
|
5
5
|
module Utils
|
6
|
+
EMPTY_CONFIG = {}.freeze
|
7
|
+
|
6
8
|
def self.adapter_name
|
7
9
|
Datadog::Utils::Database.normalize_vendor(connection_config[:adapter])
|
8
10
|
end
|
@@ -19,28 +21,16 @@ module Datadog
|
|
19
21
|
connection_config[:port]
|
20
22
|
end
|
21
23
|
|
22
|
-
def self.connection_config(
|
23
|
-
|
24
|
+
def self.connection_config(connection = nil)
|
25
|
+
connection.nil? ? default_connection_config : connection_config_from_connection(connection)
|
24
26
|
end
|
25
27
|
|
26
|
-
# Attempt to retrieve the connection from an object ID.
|
27
|
-
def self.connection_by_id(object_id)
|
28
|
-
return nil if object_id.nil?
|
29
|
-
ObjectSpace._id2ref(object_id)
|
30
|
-
rescue StandardError
|
31
|
-
nil
|
32
|
-
end
|
33
|
-
|
34
|
-
# Attempt to retrieve the connection config from an object ID.
|
35
28
|
# Typical of ActiveSupport::Notifications `sql.active_record`
|
36
|
-
def self.
|
37
|
-
connection = connection_by_id(object_id)
|
38
|
-
return {} if connection.nil?
|
39
|
-
|
29
|
+
def self.connection_config_from_connection(connection)
|
40
30
|
if connection.instance_variable_defined?(:@config)
|
41
31
|
connection.instance_variable_get(:@config)
|
42
32
|
else
|
43
|
-
|
33
|
+
EMPTY_CONFIG
|
44
34
|
end
|
45
35
|
end
|
46
36
|
|
@@ -53,9 +43,9 @@ module Datadog
|
|
53
43
|
end
|
54
44
|
|
55
45
|
connection_pool = ::ActiveRecord::Base.connection_handler.retrieve_connection_pool(current_connection_name)
|
56
|
-
connection_pool.nil? ?
|
46
|
+
connection_pool.nil? ? EMPTY_CONFIG : (@default_connection_config = connection_pool.spec.config)
|
57
47
|
rescue StandardError
|
58
|
-
|
48
|
+
EMPTY_CONFIG
|
59
49
|
end
|
60
50
|
end
|
61
51
|
end
|
@@ -13,22 +13,18 @@ module Datadog
|
|
13
13
|
# Generates Spans for all interactions with AWS
|
14
14
|
class Handler < Seahorse::Client::Handler
|
15
15
|
def call(context)
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
pin.tracer.trace(Ext::SPAN_COMMAND) do |span|
|
21
|
-
result = @handler.call(context)
|
22
|
-
annotate!(span, pin, ParsedContext.new(context))
|
23
|
-
result
|
16
|
+
tracer.trace(Ext::SPAN_COMMAND) do |span|
|
17
|
+
@handler.call(context).tap do
|
18
|
+
annotate!(span, ParsedContext.new(context))
|
19
|
+
end
|
24
20
|
end
|
25
21
|
end
|
26
22
|
|
27
23
|
private
|
28
24
|
|
29
|
-
def annotate!(span,
|
30
|
-
span.service =
|
31
|
-
span.span_type =
|
25
|
+
def annotate!(span, context)
|
26
|
+
span.service = configuration[:service_name]
|
27
|
+
span.span_type = Datadog::Ext::AppTypes::WEB
|
32
28
|
span.name = Ext::SPAN_COMMAND
|
33
29
|
span.resource = context.safely(:resource)
|
34
30
|
span.set_tag(Ext::TAG_AGENT, Ext::TAG_DEFAULT_AGENT)
|
@@ -39,6 +35,14 @@ module Datadog
|
|
39
35
|
span.set_tag(Datadog::Ext::HTTP::METHOD, context.safely(:http_method))
|
40
36
|
span.set_tag(Datadog::Ext::HTTP::STATUS_CODE, context.safely(:status_code))
|
41
37
|
end
|
38
|
+
|
39
|
+
def tracer
|
40
|
+
configuration[:tracer]
|
41
|
+
end
|
42
|
+
|
43
|
+
def configuration
|
44
|
+
Datadog.configuration[:aws]
|
45
|
+
end
|
42
46
|
end
|
43
47
|
end
|
44
48
|
end
|
@@ -22,7 +22,6 @@ module Datadog
|
|
22
22
|
require 'ddtrace/contrib/aws/instrumentation'
|
23
23
|
require 'ddtrace/contrib/aws/services'
|
24
24
|
|
25
|
-
add_pin
|
26
25
|
add_plugin(Seahorse::Client::Base, *loaded_constants)
|
27
26
|
rescue StandardError => e
|
28
27
|
Datadog::Tracer.log.error("Unable to apply AWS integration: #{e}")
|
@@ -30,16 +29,6 @@ module Datadog
|
|
30
29
|
end
|
31
30
|
end
|
32
31
|
|
33
|
-
def add_pin
|
34
|
-
Pin
|
35
|
-
.new(
|
36
|
-
get_option(:service_name),
|
37
|
-
app: Ext::APP,
|
38
|
-
app_type: Datadog::Ext::AppTypes::WEB,
|
39
|
-
tracer: get_option(:tracer)
|
40
|
-
).onto(::Aws)
|
41
|
-
end
|
42
|
-
|
43
32
|
def add_plugin(*targets)
|
44
33
|
targets.each { |klass| klass.add_plugin(Instrumentation) }
|
45
34
|
end
|
@@ -20,11 +20,8 @@ module Datadog
|
|
20
20
|
@resolver = nil
|
21
21
|
end
|
22
22
|
|
23
|
-
def configuration(
|
24
|
-
|
25
|
-
name = resolver.resolve(name)
|
26
|
-
return nil unless configurations.key?(name)
|
27
|
-
configurations[name]
|
23
|
+
def configuration(key = :default)
|
24
|
+
configurations[resolve_configuration_key(key)]
|
28
25
|
end
|
29
26
|
|
30
27
|
def configurations
|
@@ -33,12 +30,12 @@ module Datadog
|
|
33
30
|
end
|
34
31
|
end
|
35
32
|
|
36
|
-
def configure(
|
37
|
-
|
33
|
+
def configure(key, options = {}, &block)
|
34
|
+
key = resolver.resolve(key || :default)
|
38
35
|
|
39
|
-
configurations[
|
36
|
+
configurations[key].tap do |settings|
|
40
37
|
settings.configure(options, &block)
|
41
|
-
configurations[
|
38
|
+
configurations[key] = settings
|
42
39
|
end
|
43
40
|
end
|
44
41
|
|
@@ -49,6 +46,13 @@ module Datadog
|
|
49
46
|
def resolver
|
50
47
|
@resolver ||= Configuration::Resolver.new
|
51
48
|
end
|
49
|
+
|
50
|
+
def resolve_configuration_key(key = :default)
|
51
|
+
key = :default if key.nil?
|
52
|
+
key = resolver.resolve(key)
|
53
|
+
key = :default unless configurations.key?(key)
|
54
|
+
key
|
55
|
+
end
|
52
56
|
end
|
53
57
|
end
|
54
58
|
end
|