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,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