ddtrace 0.12.1 → 0.13.0.beta1

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