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