ddtrace 0.17.3 → 0.18.0

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