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,30 @@
1
+ require 'ddtrace/contrib/racecar/events/batch'
2
+ require 'ddtrace/contrib/racecar/events/message'
3
+
4
+ module Datadog
5
+ module Contrib
6
+ module Racecar
7
+ # Defines collection of instrumented Racecar events
8
+ module Events
9
+ ALL = [
10
+ Events::Batch,
11
+ Events::Message
12
+ ].freeze
13
+
14
+ module_function
15
+
16
+ def all
17
+ self::ALL
18
+ end
19
+
20
+ def subscriptions
21
+ all.collect(&:subscriptions).collect(&:to_a).flatten
22
+ end
23
+
24
+ def subscribe!
25
+ all.each(&:subscribe!)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,27 @@
1
+ require 'ddtrace/contrib/racecar/event'
2
+
3
+ module Datadog
4
+ module Contrib
5
+ module Racecar
6
+ module Events
7
+ # Defines instrumentation for process_batch.racecar event
8
+ module Batch
9
+ include Racecar::Event
10
+
11
+ EVENT_NAME = 'process_batch.racecar'.freeze
12
+ SPAN_NAME = 'racecar.batch'.freeze
13
+
14
+ module_function
15
+
16
+ def event_name
17
+ self::EVENT_NAME
18
+ end
19
+
20
+ def span_name
21
+ self::SPAN_NAME
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ require 'ddtrace/contrib/racecar/event'
2
+
3
+ module Datadog
4
+ module Contrib
5
+ module Racecar
6
+ module Events
7
+ # Defines instrumentation for process_message.racecar event
8
+ module Message
9
+ include Racecar::Event
10
+
11
+ EVENT_NAME = 'process_message.racecar'.freeze
12
+ SPAN_NAME = 'racecar.message'.freeze
13
+
14
+ module_function
15
+
16
+ def event_name
17
+ self::EVENT_NAME
18
+ end
19
+
20
+ def span_name
21
+ self::SPAN_NAME
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,5 +1,5 @@
1
1
  require 'ddtrace/ext/app_types'
2
- require 'ddtrace/contrib/active_support/notifications/subscriber'
2
+ require 'ddtrace/contrib/racecar/events'
3
3
 
4
4
  module Datadog
5
5
  module Contrib
@@ -7,50 +7,26 @@ module Datadog
7
7
  # Provides instrumentation for `racecar` through ActiveSupport instrumentation signals
8
8
  module Patcher
9
9
  include Base
10
- include ActiveSupport::Notifications::Subscriber
11
10
 
12
- NAME_MESSAGE = 'racecar.message'.freeze
13
- NAME_BATCH = 'racecar.batch'.freeze
14
11
  register_as :racecar
15
12
  option :service_name, default: 'racecar'
16
13
  option :tracer, default: Datadog.tracer do |value|
17
14
  (value || Datadog.tracer).tap do |v|
18
15
  # Make sure to update tracers of all subscriptions
19
- subscriptions.each do |subscription|
16
+ Events.subscriptions.each do |subscription|
20
17
  subscription.tracer = v
21
18
  end
22
19
  end
23
20
  end
24
21
 
25
- on_subscribe do
26
- # Subscribe to single messages
27
- subscription(
28
- self::NAME_MESSAGE,
29
- { service: configuration[:service_name] },
30
- configuration[:tracer],
31
- &method(:process)
32
- ).tap do |subscription|
33
- subscription.before_trace { ensure_clean_context! }
34
- subscription.subscribe('process_message.racecar')
35
- end
36
-
37
- # Subscribe to batch messages
38
- subscription(
39
- self::NAME_BATCH,
40
- { service: configuration[:service_name] },
41
- configuration[:tracer],
42
- &method(:process)
43
- ).tap do |subscription|
44
- subscription.before_trace { ensure_clean_context! }
45
- subscription.subscribe('process_batch.racecar')
46
- end
47
- end
48
-
49
22
  class << self
50
23
  def patch
51
24
  return patched? if patched? || !compatible?
52
25
 
53
- subscribe!
26
+ # Subscribe to Racecar events
27
+ Events.subscribe!
28
+
29
+ # Set service info
54
30
  configuration[:tracer].set_service_info(
55
31
  configuration[:service_name],
56
32
  'racecar',
@@ -65,19 +41,6 @@ module Datadog
65
41
  @patched = false
66
42
  end
67
43
 
68
- def process(span, event, _, payload)
69
- span.service = configuration[:service_name]
70
- span.resource = payload[:consumer_class]
71
-
72
- span.set_tag('kafka.topic', payload[:topic])
73
- span.set_tag('kafka.consumer', payload[:consumer_class])
74
- span.set_tag('kafka.partition', payload[:partition])
75
- span.set_tag('kafka.offset', payload[:offset]) if payload.key?(:offset)
76
- span.set_tag('kafka.first_offset', payload[:first_offset]) if payload.key?(:first_offset)
77
- span.set_tag('kafka.message_count', payload[:message_count]) if payload.key?(:message_count)
78
- span.set_error(payload[:exception_object]) if payload[:exception_object]
79
- end
80
-
81
44
  private
82
45
 
83
46
  def configuration
@@ -87,15 +50,6 @@ module Datadog
87
50
  def compatible?
88
51
  defined?(::Racecar) && defined?(::ActiveSupport::Notifications)
89
52
  end
90
-
91
- # Context objects are thread-bound.
92
- # If Racecar re-uses threads, context from a previous trace
93
- # could leak into the new trace. This "cleans" current context,
94
- # preventing such a leak.
95
- def ensure_clean_context!
96
- return unless configuration[:tracer].call_context.current_span
97
- configuration[:tracer].provider.context = Context.new
98
- end
99
53
  end
100
54
  end
101
55
  end
@@ -1,6 +1,7 @@
1
1
  require 'ddtrace/ext/app_types'
2
2
  require 'ddtrace/ext/http'
3
3
  require 'ddtrace/propagation/http_propagator'
4
+ require 'ddtrace/contrib/rack/request_queue'
4
5
 
5
6
  module Datadog
6
7
  module Contrib
@@ -19,10 +20,28 @@ module Datadog
19
20
  @app = app
20
21
  end
21
22
 
23
+ def compute_queue_time(env, tracer)
24
+ return unless Datadog.configuration[:rack][:request_queuing]
25
+
26
+ # parse the request queue time
27
+ request_start = Datadog::Contrib::Rack::QueueTime.get_request_start(env)
28
+ return if request_start.nil?
29
+
30
+ tracer.trace(
31
+ 'http_server.queue',
32
+ start_time: request_start,
33
+ service: Datadog.configuration[:rack][:web_service_name]
34
+ )
35
+ end
36
+
22
37
  def call(env)
23
38
  # retrieve integration settings
24
39
  tracer = Datadog.configuration[:rack][:tracer]
25
40
 
41
+ # [experimental] create a root Span to keep track of frontend web servers
42
+ # (i.e. Apache, nginx) if the header is properly set
43
+ frontend_span = compute_queue_time(env, tracer)
44
+
26
45
  trace_options = {
27
46
  service: Datadog.configuration[:rack][:service_name],
28
47
  resource: nil,
@@ -77,6 +96,7 @@ module Datadog
77
96
  # ensure the request_span is finished and the context reset;
78
97
  # this assumes that the Rack middleware creates a root span
79
98
  request_span.finish
99
+ frontend_span.finish unless frontend_span.nil?
80
100
 
81
101
  # TODO: Remove this once we change how context propagation works. This
82
102
  # ensures we clean thread-local variables on each HTTP request avoiding
@@ -105,7 +125,8 @@ module Datadog
105
125
  # So when its not available, we want the original, unmutated PATH_INFO, which
106
126
  # is just the relative path without query strings.
107
127
  url = env['REQUEST_URI'] || original_env['PATH_INFO']
108
- request_id = get_request_id(headers, env)
128
+ request_headers = parse_request_headers(env)
129
+ response_headers = parse_response_headers(headers || {})
109
130
 
110
131
  request_span.resource ||= resource_name_for(env, status)
111
132
  if request_span.get_tag(Datadog::Ext::HTTP::METHOD).nil?
@@ -130,8 +151,15 @@ module Datadog
130
151
  if request_span.get_tag(Datadog::Ext::HTTP::STATUS_CODE).nil? && status
131
152
  request_span.set_tag(Datadog::Ext::HTTP::STATUS_CODE, status)
132
153
  end
133
- if request_span.get_tag(Datadog::Ext::HTTP::REQUEST_ID).nil? && request_id
134
- request_span.set_tag(Datadog::Ext::HTTP::REQUEST_ID, request_id)
154
+
155
+ # Request headers
156
+ request_headers.each do |name, value|
157
+ request_span.set_tag(name, value) if request_span.get_tag(name).nil?
158
+ end
159
+
160
+ # Response headers
161
+ response_headers.each do |name, value|
162
+ request_span.set_tag(name, value) if request_span.get_tag(name).nil?
135
163
  end
136
164
 
137
165
  # detect if the status code is a 5xx and flag the request span as an error
@@ -141,14 +169,6 @@ module Datadog
141
169
  end
142
170
  end
143
171
 
144
- # If Rails is present, it will sanitize & use the Request ID header,
145
- # or generate a UUID if no request ID header is present, then set that as headers['X-Request-Id'].
146
- # Othewise use whatever Rack variables are present (they should all be the same.)
147
- def get_request_id(headers, env)
148
- headers ||= {}
149
- headers['X-Request-Id'] || headers['X-Request-ID'] || env['HTTP_X_REQUEST_ID']
150
- end
151
-
152
172
  private
153
173
 
154
174
  REQUEST_SPAN_DEPRECATION_WARNING = %(
@@ -176,6 +196,40 @@ module Datadog
176
196
  end
177
197
  end
178
198
  end
199
+
200
+ def parse_request_headers(env)
201
+ {}.tap do |result|
202
+ whitelist = Datadog.configuration[:rack][:headers][:request] || []
203
+ whitelist.each do |header|
204
+ rack_header = header_to_rack_header(header)
205
+ if env.key?(rack_header)
206
+ result[Datadog::Ext::HTTP::RequestHeaders.to_tag(header)] = env[rack_header]
207
+ end
208
+ end
209
+ end
210
+ end
211
+
212
+ def parse_response_headers(headers)
213
+ {}.tap do |result|
214
+ whitelist = Datadog.configuration[:rack][:headers][:response] || []
215
+ whitelist.each do |header|
216
+ if headers.key?(header)
217
+ result[Datadog::Ext::HTTP::ResponseHeaders.to_tag(header)] = headers[header]
218
+ else
219
+ # Try a case-insensitive lookup
220
+ uppercased_header = header.to_s.upcase
221
+ matching_header = headers.keys.find { |h| h.upcase == uppercased_header }
222
+ if matching_header
223
+ result[Datadog::Ext::HTTP::ResponseHeaders.to_tag(header)] = headers[matching_header]
224
+ end
225
+ end
226
+ end
227
+ end
228
+ end
229
+
230
+ def header_to_rack_header(name)
231
+ "HTTP_#{name.to_s.upcase.gsub(/[-\s]/, '_')}"
232
+ end
179
233
  end
180
234
  end
181
235
  end
@@ -4,6 +4,14 @@ module Datadog
4
4
  # Provides instrumentation for `rack`
5
5
  module Patcher
6
6
  include Base
7
+
8
+ DEFAULT_HEADERS = {
9
+ response: [
10
+ 'Content-Type',
11
+ 'X-Request-ID'
12
+ ]
13
+ }.freeze
14
+
7
15
  register_as :rack
8
16
  option :tracer, default: Datadog.tracer
9
17
  option :distributed_tracing, default: false
@@ -14,6 +22,14 @@ module Datadog
14
22
  get_option(:tracer).set_service_info(value, 'rack', Ext::AppTypes::WEB)
15
23
  value
16
24
  end
25
+ option :request_queuing, default: false
26
+ option :web_service_name, default: 'web-server', depends_on: [:tracer, :request_queuing] do |value|
27
+ if get_option(:request_queuing)
28
+ get_option(:tracer).set_service_info(value, 'webserver', Ext::AppTypes::WEB)
29
+ end
30
+ value
31
+ end
32
+ option :headers, default: DEFAULT_HEADERS
17
33
 
18
34
  module_function
19
35
 
@@ -0,0 +1,34 @@
1
+ module Datadog
2
+ module Contrib
3
+ module Rack
4
+ # QueueTime simply...
5
+ module QueueTime
6
+ REQUEST_START = 'HTTP_X_REQUEST_START'.freeze
7
+ QUEUE_START = 'HTTP_X_QUEUE_START'.freeze
8
+
9
+ module_function
10
+
11
+ def get_request_start(env, now = Time.now.utc)
12
+ header = env[REQUEST_START] || env[QUEUE_START]
13
+ return unless header
14
+
15
+ # nginx header is in the format "t=1512379167.574"
16
+ # TODO: this should be generic enough to work with any
17
+ # frontend web server or load balancer
18
+ time_string = header.split('t=')[1]
19
+ return if time_string.nil?
20
+
21
+ # return the request_start only if it's lesser than
22
+ # current time, to avoid significant clock skew
23
+ request_start = Time.at(time_string.to_f)
24
+ request_start.utc > now ? nil : request_start
25
+ rescue StandardError => e
26
+ # in case of an Exception we don't create a
27
+ # `request.queuing` span
28
+ Datadog::Tracer.log.debug("[rack] unable to parse request queue headers: #{e}")
29
+ nil
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -13,6 +13,71 @@ module Datadog
13
13
  Datadog::RailsRendererPatcher.patch_renderer
14
14
  end
15
15
  end
16
+
17
+ def self.start_render_template(payload)
18
+ # retrieve the tracing context
19
+ tracing_context = payload.fetch(:tracing_context)
20
+
21
+ # create a new Span and add it to the tracing context
22
+ tracer = Datadog.configuration[:rails][:tracer]
23
+ span = tracer.trace('rails.render_template', span_type: Datadog::Ext::HTTP::TEMPLATE)
24
+ tracing_context[:dd_rails_template_span] = span
25
+ rescue StandardError => e
26
+ Datadog::Tracer.log.debug(e.message)
27
+ end
28
+
29
+ def self.finish_render_template(payload)
30
+ # retrieve the tracing context and the latest active span
31
+ tracing_context = payload.fetch(:tracing_context)
32
+ span = tracing_context[:dd_rails_template_span]
33
+ return if !span || span.finished?
34
+
35
+ # finish the tracing and update the execution time
36
+ begin
37
+ template_name = tracing_context[:template_name]
38
+ layout = tracing_context[:layout]
39
+ exception = tracing_context[:exception]
40
+
41
+ span.set_tag('rails.template_name', template_name) if template_name
42
+ span.set_tag('rails.layout', layout) if layout
43
+ span.set_error(exception) if exception
44
+ ensure
45
+ span.finish()
46
+ end
47
+ rescue StandardError => e
48
+ Datadog::Tracer.log.debug(e.message)
49
+ end
50
+
51
+ def self.start_render_partial(payload)
52
+ # retrieve the tracing context
53
+ tracing_context = payload.fetch(:tracing_context)
54
+
55
+ tracer = Datadog.configuration[:rails][:tracer]
56
+ span = tracer.trace('rails.render_partial', span_type: Datadog::Ext::HTTP::TEMPLATE)
57
+ tracing_context[:dd_rails_partial_span] = span
58
+ rescue StandardError => e
59
+ Datadog::Tracer.log.debug(e.message)
60
+ end
61
+
62
+ def self.finish_render_partial(payload)
63
+ # retrieve the tracing context and the latest active span
64
+ tracing_context = payload.fetch(:tracing_context)
65
+ span = tracing_context[:dd_rails_partial_span]
66
+ return if !span || span.finished?
67
+
68
+ # finish the tracing and update the execution time
69
+ begin
70
+ template_name = tracing_context[:template_name]
71
+ exception = tracing_context[:exception]
72
+
73
+ span.set_tag('rails.template_name', template_name) if template_name
74
+ span.set_error(exception) if exception
75
+ ensure
76
+ span.finish()
77
+ end
78
+ rescue StandardError => e
79
+ Datadog::Tracer.log.debug(e.message)
80
+ end
16
81
  end
17
82
  end
18
83
  end