ddtrace 0.12.1 → 0.13.0.beta1

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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/.env +11 -21
  3. data/.rubocop.yml +1 -4
  4. data/Appraisals +75 -439
  5. data/CHANGELOG.md +16 -19
  6. data/Rakefile +89 -259
  7. data/circle.yml +69 -0
  8. data/ddtrace.gemspec +6 -6
  9. data/docker-compose.yml +37 -222
  10. data/docs/GettingStarted.md +260 -19
  11. data/gemfiles/contrib.gemfile +5 -0
  12. data/gemfiles/contrib_old.gemfile +4 -1
  13. data/gemfiles/rails30_postgres.gemfile +0 -1
  14. data/gemfiles/rails30_postgres_sidekiq.gemfile +0 -1
  15. data/gemfiles/rails32_mysql2.gemfile +0 -1
  16. data/gemfiles/rails32_postgres.gemfile +0 -1
  17. data/gemfiles/rails32_postgres_redis.gemfile +0 -1
  18. data/gemfiles/rails32_postgres_sidekiq.gemfile +0 -1
  19. data/gemfiles/rails5_mysql2.gemfile +1 -1
  20. data/gemfiles/rails5_postgres.gemfile +1 -1
  21. data/gemfiles/rails5_postgres_redis.gemfile +1 -1
  22. data/gemfiles/rails5_postgres_sidekiq.gemfile +1 -1
  23. data/lib/ddtrace.rb +6 -0
  24. data/lib/ddtrace/configuration.rb +2 -2
  25. data/lib/ddtrace/contrib/active_model_serializers/event.rb +57 -0
  26. data/lib/ddtrace/contrib/active_model_serializers/events.rb +30 -0
  27. data/lib/ddtrace/contrib/active_model_serializers/events/render.rb +32 -0
  28. data/lib/ddtrace/contrib/active_model_serializers/events/serialize.rb +35 -0
  29. data/lib/ddtrace/contrib/active_model_serializers/patcher.rb +62 -0
  30. data/lib/ddtrace/contrib/active_record/event.rb +30 -0
  31. data/lib/ddtrace/contrib/active_record/events.rb +30 -0
  32. data/lib/ddtrace/contrib/active_record/events/instantiation.rb +51 -0
  33. data/lib/ddtrace/contrib/active_record/events/sql.rb +48 -0
  34. data/lib/ddtrace/contrib/active_record/patcher.rb +3 -73
  35. data/lib/ddtrace/contrib/active_record/utils.rb +1 -15
  36. data/lib/ddtrace/contrib/active_support/notifications/event.rb +62 -0
  37. data/lib/ddtrace/contrib/aws/instrumentation.rb +2 -2
  38. data/lib/ddtrace/contrib/elasticsearch/patcher.rb +2 -2
  39. data/lib/ddtrace/contrib/elasticsearch/quantize.rb +8 -40
  40. data/lib/ddtrace/contrib/excon/middleware.rb +140 -0
  41. data/lib/ddtrace/contrib/excon/patcher.rb +50 -0
  42. data/lib/ddtrace/contrib/grpc/datadog_interceptor.rb +65 -0
  43. data/lib/ddtrace/contrib/grpc/datadog_interceptor/client.rb +49 -0
  44. data/lib/ddtrace/contrib/grpc/datadog_interceptor/server.rb +66 -0
  45. data/lib/ddtrace/contrib/grpc/intercept_with_datadog.rb +49 -0
  46. data/lib/ddtrace/contrib/grpc/patcher.rb +62 -0
  47. data/lib/ddtrace/contrib/http/patcher.rb +16 -18
  48. data/lib/ddtrace/contrib/racecar/event.rb +61 -0
  49. data/lib/ddtrace/contrib/racecar/events.rb +30 -0
  50. data/lib/ddtrace/contrib/racecar/events/batch.rb +27 -0
  51. data/lib/ddtrace/contrib/racecar/events/message.rb +27 -0
  52. data/lib/ddtrace/contrib/racecar/patcher.rb +6 -52
  53. data/lib/ddtrace/contrib/rack/middlewares.rb +65 -11
  54. data/lib/ddtrace/contrib/rack/patcher.rb +16 -0
  55. data/lib/ddtrace/contrib/rack/request_queue.rb +34 -0
  56. data/lib/ddtrace/contrib/rails/action_view.rb +65 -0
  57. data/lib/ddtrace/contrib/rails/active_support.rb +8 -9
  58. data/lib/ddtrace/contrib/rails/core_extensions.rb +115 -74
  59. data/lib/ddtrace/contrib/rake/instrumentation.rb +70 -0
  60. data/lib/ddtrace/contrib/rake/patcher.rb +53 -0
  61. data/lib/ddtrace/contrib/sequel/database.rb +58 -0
  62. data/lib/ddtrace/contrib/sequel/dataset.rb +59 -0
  63. data/lib/ddtrace/contrib/sequel/patcher.rb +56 -0
  64. data/lib/ddtrace/contrib/sequel/utils.rb +28 -0
  65. data/lib/ddtrace/ext/distributed.rb +5 -0
  66. data/lib/ddtrace/ext/grpc.rb +7 -0
  67. data/lib/ddtrace/ext/http.rb +35 -5
  68. data/lib/ddtrace/propagation/grpc_propagator.rb +54 -0
  69. data/lib/ddtrace/quantization/hash.rb +89 -0
  70. data/lib/ddtrace/tracer.rb +1 -4
  71. data/lib/ddtrace/utils.rb +4 -10
  72. data/lib/ddtrace/utils/database.rb +21 -0
  73. data/lib/ddtrace/version.rb +3 -3
  74. metadata +38 -13
  75. data/.circleci/config.yml +0 -456
  76. data/.circleci/images/primary/Dockerfile-1.9.3 +0 -69
  77. data/.circleci/images/primary/Dockerfile-2.0.0 +0 -69
  78. data/.circleci/images/primary/Dockerfile-2.1.10 +0 -69
  79. data/.circleci/images/primary/Dockerfile-2.2.10 +0 -69
  80. data/.circleci/images/primary/Dockerfile-2.3.7 +0 -73
  81. data/.circleci/images/primary/Dockerfile-2.4.4 +0 -73
  82. data/lib/ddtrace/contrib/rails/action_controller_patch.rb +0 -77
@@ -0,0 +1,50 @@
1
+ require 'ddtrace/ext/app_types'
2
+
3
+ module Datadog
4
+ module Contrib
5
+ module Excon
6
+ # Responsible for hooking the instrumentation into Excon
7
+ module Patcher
8
+ include Base
9
+
10
+ DEFAULT_SERVICE = 'excon'.freeze
11
+
12
+ register_as :excon
13
+ option :tracer, default: Datadog.tracer
14
+ option :service_name, default: DEFAULT_SERVICE
15
+ option :distributed_tracing, default: false
16
+ option :split_by_domain, default: false
17
+ option :error_handler, default: nil
18
+
19
+ @patched = false
20
+
21
+ module_function
22
+
23
+ def patch
24
+ return @patched if patched? || !compatible?
25
+
26
+ require 'ddtrace/contrib/excon/middleware'
27
+
28
+ add_middleware
29
+
30
+ @patched = true
31
+ rescue => e
32
+ Tracer.log.error("Unable to apply Excon integration: #{e}")
33
+ @patched
34
+ end
35
+
36
+ def patched?
37
+ @patched
38
+ end
39
+
40
+ def compatible?
41
+ defined?(::Excon)
42
+ end
43
+
44
+ def add_middleware
45
+ ::Excon.defaults[:middlewares] = Middleware.around_default_stack
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,65 @@
1
+ module Datadog
2
+ module Contrib
3
+ module GRPC
4
+ # :nodoc:
5
+ module DatadogInterceptor
6
+ # :nodoc:
7
+ class Base < ::GRPC::Interceptor
8
+ attr_accessor :datadog_pin
9
+
10
+ def initialize(options = {})
11
+ datadog_pin_configuration { |c| yield(c) if block_given? }
12
+ end
13
+
14
+ def request_response(**keywords)
15
+ trace(keywords) { yield }
16
+ end
17
+
18
+ def client_streamer(**keywords)
19
+ trace(keywords) { yield }
20
+ end
21
+
22
+ def server_streamer(**keywords)
23
+ trace(keywords) { yield }
24
+ end
25
+
26
+ def bidi_streamer(**keywords)
27
+ trace(keywords) { yield }
28
+ end
29
+
30
+ private
31
+
32
+ def datadog_pin_configuration
33
+ pin = default_datadog_pin
34
+
35
+ if block_given?
36
+ pin = Pin.new(
37
+ pin.service_name,
38
+ app: pin.app,
39
+ app_type: pin.app_type,
40
+ tracer: pin.tracer
41
+ )
42
+
43
+ yield(pin)
44
+ end
45
+
46
+ pin.onto(self)
47
+
48
+ pin
49
+ end
50
+
51
+ def default_datadog_pin
52
+ Pin.get_from(::GRPC)
53
+ end
54
+
55
+ def tracer
56
+ datadog_pin.tracer
57
+ end
58
+ end
59
+
60
+ require_relative 'datadog_interceptor/client'
61
+ require_relative 'datadog_interceptor/server'
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,49 @@
1
+ module Datadog
2
+ module Contrib
3
+ module GRPC
4
+ module DatadogInterceptor
5
+ # The DatadogInterceptor::Client implements the tracing strategy
6
+ # for gRPC client-side endpoitns. This middleware compoent will
7
+ # inject trace context information into gRPC metadata prior to
8
+ # sending the request to the server.
9
+ class Client < Base
10
+ def trace(keywords)
11
+ keywords[:metadata] ||= {}
12
+
13
+ options = {
14
+ span_type: Datadog::Ext::GRPC::TYPE,
15
+ service: datadog_pin.service_name,
16
+ resource: format_resource(keywords[:method])
17
+ }
18
+
19
+ tracer.trace('grpc.client', options) do |span|
20
+ annotate!(span, keywords[:metadata])
21
+
22
+ yield
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def annotate!(span, metadata)
29
+ metadata.each do |header, value|
30
+ span.set_tag(header, value)
31
+ end
32
+
33
+ Datadog::GRPCPropagator
34
+ .inject!(span.context, metadata)
35
+ rescue StandardError => e
36
+ Datadog::Tracer.log.debug("GRPC client trace failed: #{e}")
37
+ end
38
+
39
+ def format_resource(proto_method)
40
+ proto_method.downcase
41
+ .split('/')
42
+ .reject(&:empty?)
43
+ .join('.')
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,66 @@
1
+ module Datadog
2
+ module Contrib
3
+ module GRPC
4
+ module DatadogInterceptor
5
+ # The DatadogInterceptor::Server implements the tracing strategy
6
+ # for gRPC server-side endpoints. When the datadog fields have been
7
+ # added to the gRPC call metadata, this middleware component will
8
+ # extract any client-side tracing information, attempting to associate
9
+ # its tracing context with a parent client-side context
10
+ class Server < Base
11
+ def trace(keywords)
12
+ options = {
13
+ span_type: Datadog::Ext::GRPC::TYPE,
14
+ service: datadog_pin.service_name,
15
+ resource: format_resource(keywords[:method])
16
+ }
17
+ metadata = keywords[:call].metadata
18
+
19
+ set_distributed_context!(tracer, metadata)
20
+
21
+ tracer.trace('grpc.service', options) do |span|
22
+ annotate!(span, metadata)
23
+
24
+ yield
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def set_distributed_context!(tracer, metadata)
31
+ tracer.provider.context = Datadog::GRPCPropagator
32
+ .extract(metadata)
33
+ rescue StandardError => e
34
+ Datadog::Tracer.log.debug(
35
+ "unable to propagate GRPC metadata to context: #{e}"
36
+ )
37
+ end
38
+
39
+ def annotate!(span, metadata)
40
+ metadata.each do |header, value|
41
+ next if reserved_headers.include?(header)
42
+ span.set_tag(header, value)
43
+ end
44
+ rescue StandardError => e
45
+ Datadog::Tracer.log.debug("GRPC client trace failed: #{e}")
46
+ end
47
+
48
+ def reserved_headers
49
+ [Datadog::Ext::DistributedTracing::GRPC_METADATA_TRACE_ID,
50
+ Datadog::Ext::DistributedTracing::GRPC_METADATA_PARENT_ID,
51
+ Datadog::Ext::DistributedTracing::GRPC_METADATA_SAMPLING_PRIORITY]
52
+ end
53
+
54
+ def format_resource(proto_method)
55
+ proto_method.owner
56
+ .to_s
57
+ .downcase
58
+ .split('::')
59
+ .<<(proto_method.name)
60
+ .join('.')
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,49 @@
1
+ require_relative 'datadog_interceptor'
2
+
3
+ module Datadog
4
+ module Contrib
5
+ module GRPC
6
+ # :nodoc:
7
+ # The `#intercept!` method is implemented in gRPC; this module
8
+ # will be prepended to the original class, effectively injecting
9
+ # our tracing middleware into the head of the call chain.
10
+ module InterceptWithDatadog
11
+ def intercept!(type, args = {})
12
+ if should_prepend?
13
+ datadog_interceptor = choose_datadog_interceptor(args)
14
+
15
+ @interceptors.unshift(datadog_interceptor.new) if datadog_interceptor
16
+
17
+ @trace_started = true
18
+ end
19
+
20
+ super
21
+ end
22
+
23
+ private
24
+
25
+ def should_prepend?
26
+ !trace_started? && !already_prepended?
27
+ end
28
+
29
+ def trace_started?
30
+ defined?(@trace_started) && @trace_started
31
+ end
32
+
33
+ def already_prepended?
34
+ @interceptors.any? do |interceptor|
35
+ interceptor.class.ancestors.include?(Datadog::Contrib::GRPC::DatadogInterceptor::Base)
36
+ end
37
+ end
38
+
39
+ def choose_datadog_interceptor(args)
40
+ if args.key?(:metadata)
41
+ Datadog::Contrib::GRPC::DatadogInterceptor::Client
42
+ elsif args.key?(:call)
43
+ Datadog::Contrib::GRPC::DatadogInterceptor::Server
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,62 @@
1
+ # requirements should be kept minimal as Patcher is a shared requirement.
2
+
3
+ module Datadog
4
+ module Contrib
5
+ module GRPC
6
+ SERVICE = 'grpc'.freeze
7
+
8
+ # Patcher enables patching of 'grpc' module.
9
+ module Patcher
10
+ include Base
11
+ register_as :grpc, auto_patch: true
12
+ option :tracer, default: Datadog.tracer
13
+ option :service_name, default: SERVICE
14
+
15
+ @patched = false
16
+
17
+ module_function
18
+
19
+ def patch
20
+ return false unless compatible?
21
+ return @patched if @patched
22
+
23
+ require 'ddtrace/ext/grpc'
24
+ require 'ddtrace/propagation/grpc_propagator'
25
+ require 'ddtrace/contrib/grpc/datadog_interceptor'
26
+ require 'ddtrace/contrib/grpc/intercept_with_datadog'
27
+
28
+ add_pin
29
+ prepend_interceptor
30
+
31
+ @patched = true
32
+ rescue StandardError => e
33
+ Datadog::Tracer.log.error("Unable to apply gRPC integration: #{e}")
34
+ ensure
35
+ @patched
36
+ end
37
+
38
+ def compatible?
39
+ defined?(::GRPC::VERSION) && Gem::Version.new(::GRPC::VERSION) >= Gem::Version.new('0.10.0')
40
+ end
41
+
42
+ def patched?
43
+ @patched
44
+ end
45
+
46
+ def add_pin
47
+ Pin.new(
48
+ get_option(:service_name),
49
+ app: 'grpc',
50
+ app_type: 'grpc',
51
+ tracer: get_option(:tracer)
52
+ ).onto(::GRPC)
53
+ end
54
+
55
+ def prepend_interceptor
56
+ ::GRPC::InterceptionContext
57
+ .prepend(Datadog::Contrib::GRPC::InterceptWithDatadog)
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -48,8 +48,6 @@ module Datadog
48
48
  include Base
49
49
  register_as :http, auto_patch: true
50
50
  option :distributed_tracing, default: false
51
- option :service_name, default: SERVICE
52
- option :tracer, default: Datadog.tracer
53
51
 
54
52
  @patched = false
55
53
 
@@ -66,7 +64,7 @@ module Datadog
66
64
  require 'ddtrace/ext/net'
67
65
  require 'ddtrace/ext/distributed'
68
66
 
69
- patch_http
67
+ patch_http()
70
68
 
71
69
  @patched = true
72
70
  rescue StandardError => e
@@ -76,7 +74,7 @@ module Datadog
76
74
  @patched
77
75
  end
78
76
 
79
- # patched? tells whether patch has been successfully applied
77
+ # patched? tells wether patch has been successfully applied
80
78
  def patched?
81
79
  @patched
82
80
  end
@@ -86,27 +84,27 @@ module Datadog
86
84
  # rubocop:disable Metrics/AbcSize
87
85
  def patch_http
88
86
  ::Net::HTTP.class_eval do
89
- alias_method :request_without_datadog, :request
90
- remove_method :request
91
-
92
- def datadog_pin
93
- @datadog_pindatadog_pin ||= begin
94
- service = Datadog.configuration[:http][:service_name]
95
- tracer = Datadog.configuration[:http][:tracer]
87
+ alias_method :initialize_without_datadog, :initialize
88
+ Datadog::Patcher.without_warnings do
89
+ remove_method :initialize
90
+ end
96
91
 
97
- Datadog::Pin.new(service, app: APP, app_type: Datadog::Ext::AppTypes::WEB, tracer: tracer)
98
- end
92
+ def initialize(*args)
93
+ pin = Datadog::Pin.new(SERVICE, app: APP, app_type: Datadog::Ext::AppTypes::WEB)
94
+ pin.onto(self)
95
+ initialize_without_datadog(*args)
99
96
  end
100
97
 
98
+ alias_method :request_without_datadog, :request
99
+ remove_method :request
100
+
101
101
  def request(req, body = nil, &block) # :yield: +response+
102
- pin = datadog_pin
102
+ pin = Datadog::Pin.get_from(self)
103
103
  return request_without_datadog(req, body, &block) unless pin && pin.tracer
104
104
 
105
105
  transport = pin.tracer.writer.transport
106
-
107
- if Datadog::Contrib::HTTP.should_skip_tracing?(req, @address, @port, transport, pin)
108
- return request_without_datadog(req, body, &block)
109
- end
106
+ return request_without_datadog(req, body, &block) if
107
+ Datadog::Contrib::HTTP.should_skip_tracing?(req, @address, @port, transport, pin)
110
108
 
111
109
  pin.tracer.trace(NAME) do |span|
112
110
  begin
@@ -0,0 +1,61 @@
1
+ require 'ddtrace/contrib/active_support/notifications/event'
2
+
3
+ module Datadog
4
+ module Contrib
5
+ module Racecar
6
+ # Defines basic behaviors for an ActiveRecord event.
7
+ module Event
8
+ def self.included(base)
9
+ base.send(:include, ActiveSupport::Notifications::Event)
10
+ base.send(:extend, ClassMethods)
11
+ end
12
+
13
+ # Class methods for Racecar events.
14
+ # Note, they share the same process method and before_trace method.
15
+ module ClassMethods
16
+ def subscription(*args)
17
+ super.tap do |subscription|
18
+ subscription.before_trace { ensure_clean_context! }
19
+ end
20
+ end
21
+
22
+ def span_options
23
+ { service: configuration[:service_name] }
24
+ end
25
+
26
+ def tracer
27
+ configuration[:tracer]
28
+ end
29
+
30
+ def configuration
31
+ Datadog.configuration[:racecar]
32
+ end
33
+
34
+ def process(span, event, _id, payload)
35
+ span.service = configuration[:service_name]
36
+ span.resource = payload[:consumer_class]
37
+
38
+ span.set_tag('kafka.topic', payload[:topic])
39
+ span.set_tag('kafka.consumer', payload[:consumer_class])
40
+ span.set_tag('kafka.partition', payload[:partition])
41
+ span.set_tag('kafka.offset', payload[:offset]) if payload.key?(:offset)
42
+ span.set_tag('kafka.first_offset', payload[:first_offset]) if payload.key?(:first_offset)
43
+ span.set_tag('kafka.message_count', payload[:message_count]) if payload.key?(:message_count)
44
+ span.set_error(payload[:exception_object]) if payload[:exception_object]
45
+ end
46
+
47
+ private
48
+
49
+ # Context objects are thread-bound.
50
+ # If Racecar re-uses threads, context from a previous trace
51
+ # could leak into the new trace. This "cleans" current context,
52
+ # preventing such a leak.
53
+ def ensure_clean_context!
54
+ return unless configuration[:tracer].call_context.current_span
55
+ configuration[:tracer].provider.context = Context.new
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end