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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.env +2 -2
  3. data/Appraisals +4 -0
  4. data/CHANGELOG.md +21 -2
  5. data/Rakefile +31 -29
  6. data/docker-compose.yml +7 -7
  7. data/docs/GettingStarted.md +26 -2
  8. data/lib/ddtrace.rb +4 -0
  9. data/lib/ddtrace/augmentation.rb +13 -0
  10. data/lib/ddtrace/augmentation/method_wrapper.rb +20 -0
  11. data/lib/ddtrace/augmentation/method_wrapping.rb +38 -0
  12. data/lib/ddtrace/augmentation/shim.rb +102 -0
  13. data/lib/ddtrace/contrib/active_record/events/sql.rb +6 -2
  14. data/lib/ddtrace/contrib/active_record/patcher.rb +2 -0
  15. data/lib/ddtrace/contrib/active_record/patches/abstract_adapter.rb +72 -0
  16. data/lib/ddtrace/contrib/active_record/utils.rb +8 -18
  17. data/lib/ddtrace/contrib/aws/instrumentation.rb +15 -11
  18. data/lib/ddtrace/contrib/aws/patcher.rb +0 -11
  19. data/lib/ddtrace/contrib/configurable.rb +13 -9
  20. data/lib/ddtrace/contrib/rack/request_queue.rb +5 -1
  21. data/lib/ddtrace/contrib/shoryuken/configuration/settings.rb +14 -0
  22. data/lib/ddtrace/contrib/shoryuken/ext.rb +18 -0
  23. data/lib/ddtrace/contrib/shoryuken/integration.rb +35 -0
  24. data/lib/ddtrace/contrib/shoryuken/patcher.rb +30 -0
  25. data/lib/ddtrace/contrib/shoryuken/tracer.rb +37 -0
  26. data/lib/ddtrace/contrib/sidekiq/client_tracer.rb +35 -0
  27. data/lib/ddtrace/contrib/sidekiq/configuration/settings.rb +1 -0
  28. data/lib/ddtrace/contrib/sidekiq/ext.rb +2 -0
  29. data/lib/ddtrace/contrib/sidekiq/patcher.rb +9 -2
  30. data/lib/ddtrace/contrib/sidekiq/server_tracer.rb +50 -0
  31. data/lib/ddtrace/contrib/sidekiq/tracing.rb +38 -0
  32. data/lib/ddtrace/sync_writer.rb +2 -1
  33. data/lib/ddtrace/transport.rb +6 -3
  34. data/lib/ddtrace/utils/database.rb +7 -3
  35. data/lib/ddtrace/version.rb +2 -2
  36. data/lib/ddtrace/writer.rb +1 -4
  37. metadata +15 -3
  38. data/lib/ddtrace/contrib/sidekiq/tracer.rb +0 -72
@@ -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[:connection_id])
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 = !settings.nil? ? settings.service_name : configuration[: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(object_id = nil)
23
- object_id.nil? ? default_connection_config : connection_config_by_id(object_id)
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.connection_config_by_id(object_id)
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? ? {} : (@default_connection_config = connection_pool.spec.config)
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
- pin = Datadog::Pin.get_from(::Aws)
17
-
18
- return @handler.call(context) unless pin && pin.enabled?
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, pin, context)
30
- span.service = pin.service
31
- span.span_type = pin.app_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(name = :default)
24
- name = :default if name.nil?
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(name, options = {}, &block)
37
- name = resolver.resolve(name || :default)
33
+ def configure(key, options = {}, &block)
34
+ key = resolver.resolve(key || :default)
38
35
 
39
- configurations[name].tap do |settings|
36
+ configurations[key].tap do |settings|
40
37
  settings.configure(options, &block)
41
- configurations[name] = settings
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