ddtrace 0.13.0 → 0.14.0

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 (58) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +88 -6
  3. data/.rubocop.yml +10 -0
  4. data/Appraisals +24 -0
  5. data/CHANGELOG.md +108 -2
  6. data/Gemfile +1 -0
  7. data/Rakefile +32 -2
  8. data/benchmarks/postgres_database.yml +9 -0
  9. data/benchmarks/sidekiq_test.rb +154 -0
  10. data/ddtrace.gemspec +3 -0
  11. data/docs/GettingStarted.md +127 -6
  12. data/lib/ddtrace/configuration.rb +19 -7
  13. data/lib/ddtrace/context.rb +8 -0
  14. data/lib/ddtrace/contrib/active_record/configuration/resolver.rb +46 -0
  15. data/lib/ddtrace/contrib/active_record/configuration/settings.rb +28 -0
  16. data/lib/ddtrace/contrib/active_record/events/sql.rb +11 -7
  17. data/lib/ddtrace/contrib/active_record/integration.rb +44 -0
  18. data/lib/ddtrace/contrib/active_record/patcher.rb +4 -28
  19. data/lib/ddtrace/contrib/active_record/utils.rb +5 -11
  20. data/lib/ddtrace/contrib/active_support/notifications/subscription.rb +2 -1
  21. data/lib/ddtrace/contrib/base.rb +4 -3
  22. data/lib/ddtrace/contrib/configurable.rb +55 -0
  23. data/lib/ddtrace/contrib/configuration/option.rb +33 -0
  24. data/lib/ddtrace/contrib/configuration/option_definition.rb +29 -0
  25. data/lib/ddtrace/contrib/configuration/option_definition_set.rb +20 -0
  26. data/lib/ddtrace/contrib/configuration/option_set.rb +8 -0
  27. data/lib/ddtrace/contrib/configuration/options.rb +95 -0
  28. data/lib/ddtrace/contrib/configuration/resolver.rb +12 -0
  29. data/lib/ddtrace/contrib/configuration/settings.rb +35 -0
  30. data/lib/ddtrace/contrib/delayed_job/patcher.rb +41 -0
  31. data/lib/ddtrace/contrib/delayed_job/plugin.rb +43 -0
  32. data/lib/ddtrace/contrib/integration.rb +16 -0
  33. data/lib/ddtrace/contrib/patchable.rb +38 -0
  34. data/lib/ddtrace/contrib/patcher.rb +28 -0
  35. data/lib/ddtrace/contrib/rack/middlewares.rb +34 -15
  36. data/lib/ddtrace/contrib/rake/instrumentation.rb +15 -3
  37. data/lib/ddtrace/contrib/registerable.rb +33 -0
  38. data/lib/ddtrace/contrib/resque/resque_job.rb +2 -10
  39. data/lib/ddtrace/contrib/rest_client/configuration/settings.rb +22 -0
  40. data/lib/ddtrace/contrib/rest_client/integration.rb +27 -0
  41. data/lib/ddtrace/contrib/rest_client/patcher.rb +25 -0
  42. data/lib/ddtrace/contrib/rest_client/request_patch.rb +90 -0
  43. data/lib/ddtrace/contrib/sequel/configuration/settings.rb +13 -0
  44. data/lib/ddtrace/contrib/sequel/database.rb +1 -1
  45. data/lib/ddtrace/contrib/sequel/integration.rb +38 -0
  46. data/lib/ddtrace/contrib/sequel/patcher.rb +4 -20
  47. data/lib/ddtrace/monkey.rb +0 -7
  48. data/lib/ddtrace/patcher.rb +5 -0
  49. data/lib/ddtrace/pin.rb +1 -1
  50. data/lib/ddtrace/propagation/http_propagator.rb +6 -0
  51. data/lib/ddtrace/span.rb +0 -1
  52. data/lib/ddtrace/tracer.rb +22 -5
  53. data/lib/ddtrace/vendor/active_record/connection_specification.rb +301 -0
  54. data/lib/ddtrace/version.rb +1 -1
  55. data/lib/ddtrace/workers.rb +6 -4
  56. data/lib/ddtrace/writer.rb +4 -5
  57. data/lib/ddtrace.rb +7 -15
  58. metadata +57 -3
@@ -15,23 +15,31 @@ module Datadog
15
15
  def invoke(*args)
16
16
  return super unless enabled?
17
17
 
18
- tracer.trace(SPAN_NAME_INVOKE) do |span|
18
+ tracer.trace(SPAN_NAME_INVOKE, span_options) do |span|
19
19
  super
20
20
  annotate_invoke!(span, args)
21
21
  end
22
+ ensure
23
+ shutdown_tracer!
22
24
  end
23
25
 
24
26
  def execute(args = nil)
25
27
  return super unless enabled?
26
28
 
27
- tracer.trace(SPAN_NAME_EXECUTE) do |span|
29
+ tracer.trace(SPAN_NAME_EXECUTE, span_options) do |span|
28
30
  super
29
31
  annotate_execute!(span, args)
30
32
  end
33
+ ensure
34
+ shutdown_tracer!
31
35
  end
32
36
 
33
37
  private
34
38
 
39
+ def shutdown_tracer!
40
+ tracer.shutdown! if tracer.active_span.nil? && ::Rake.application.top_level_tasks.include?(name)
41
+ end
42
+
35
43
  def annotate_invoke!(span, args)
36
44
  span.resource = name
37
45
  span.set_tag('rake.task.arg_names', arg_names)
@@ -48,7 +56,7 @@ module Datadog
48
56
  end
49
57
 
50
58
  def quantize_args(args)
51
- quantize_options = Datadog.configuration[:rake][:quantize][:args]
59
+ quantize_options = configuration[:quantize][:args]
52
60
  Datadog::Quantization::Hash.format(args, quantize_options)
53
61
  end
54
62
 
@@ -60,6 +68,10 @@ module Datadog
60
68
  configuration[:tracer]
61
69
  end
62
70
 
71
+ def span_options
72
+ { service: configuration[:service_name] }
73
+ end
74
+
63
75
  def configuration
64
76
  Datadog.configuration[:rake]
65
77
  end
@@ -0,0 +1,33 @@
1
+ require 'ddtrace/registry'
2
+
3
+ module Datadog
4
+ module Contrib
5
+ # Defines registerable behavior for integrations
6
+ module Registerable
7
+ def self.included(base)
8
+ base.send(:extend, ClassMethods)
9
+ base.send(:include, InstanceMethods)
10
+ end
11
+
12
+ # Class methods for registerable behavior
13
+ module ClassMethods
14
+ def register_as(name, options = {})
15
+ registry = options.fetch(:registry, Datadog.registry)
16
+ auto_patch = options.fetch(:auto_patch, false)
17
+
18
+ registry.add(name, new(name, options), auto_patch)
19
+ end
20
+ end
21
+
22
+ # Instance methods for registerable behavior
23
+ module InstanceMethods
24
+ attr_reader \
25
+ :name
26
+
27
+ def initialize(name, options = {})
28
+ @name = name
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -16,26 +16,18 @@ module Datadog
16
16
  yield
17
17
  span.service = pin.service
18
18
  end
19
+ ensure
20
+ pin.tracer.shutdown! if pin && pin.tracer
19
21
  end
20
22
  end
21
23
  end
22
24
  end
23
25
  end
24
26
 
25
- Resque.before_first_fork do
26
- pin = Datadog::Pin.get_from(Resque)
27
- next unless pin && pin.tracer
28
-
29
- # Create SyncWriter instance before forking
30
- sync_writer = Datadog::SyncWriter.new(transport: pin.tracer.writer.transport)
31
- Datadog::Contrib::Resque.sync_writer = sync_writer
32
- end
33
-
34
27
  Resque.after_fork do
35
28
  # get the current tracer
36
29
  pin = Datadog::Pin.get_from(Resque)
37
30
  next unless pin && pin.tracer
38
31
  # clean the state so no CoW happens
39
32
  pin.tracer.provider.context = nil
40
- pin.tracer.writer = Datadog::Contrib::Resque.sync_writer
41
33
  end
@@ -0,0 +1,22 @@
1
+ require 'ddtrace/contrib/configuration/settings'
2
+ require 'ddtrace/contrib/active_record/utils'
3
+
4
+ module Datadog
5
+ module Contrib
6
+ module RestClient
7
+ module Configuration
8
+ # Unique settings for RestClient
9
+ class Settings < Contrib::Configuration::Settings
10
+ NAME = 'rest_client'.freeze
11
+
12
+ option :service_name, default: NAME, depends_on: [:tracer] do |value|
13
+ get_option(:tracer).set_service_info(value, NAME, Ext::AppTypes::DB)
14
+ value
15
+ end
16
+
17
+ option :distributed_tracing, default: false
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,27 @@
1
+ require 'ddtrace/contrib/integration'
2
+ require 'ddtrace/contrib/rest_client/configuration/settings'
3
+ require 'ddtrace/contrib/rest_client/patcher'
4
+
5
+ module Datadog
6
+ module Contrib
7
+ module RestClient
8
+ # Description of RestClient integration
9
+ class Integration
10
+ include Contrib::Integration
11
+ register_as :rest_client
12
+
13
+ def self.compatible?
14
+ RUBY_VERSION >= '1.9.3' && defined?(::RestClient::Request)
15
+ end
16
+
17
+ def default_configuration
18
+ Configuration::Settings.new
19
+ end
20
+
21
+ def patcher
22
+ Patcher
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,25 @@
1
+ module Datadog
2
+ module Contrib
3
+ module RestClient
4
+ # RestClient integration
5
+ module Patcher
6
+ include Contrib::Patcher
7
+
8
+ module_function
9
+
10
+ def patched?
11
+ done?(:rest_client)
12
+ end
13
+
14
+ def patch
15
+ do_once(:rest_client) do
16
+ require 'ddtrace/ext/app_types'
17
+ require 'ddtrace/contrib/rest_client/request_patch'
18
+
19
+ ::RestClient::Request.send(:include, RequestPatch)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,90 @@
1
+ require 'ddtrace/ext/net'
2
+ require 'ddtrace/ext/distributed'
3
+ require 'ddtrace/propagation/http_propagator'
4
+
5
+ module Datadog
6
+ module Contrib
7
+ module RestClient
8
+ # RestClient RequestPatch
9
+ module RequestPatch
10
+ REQUEST_TRACE_NAME = 'rest_client.request'.freeze
11
+
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 :execute_without_datadog, :execute
16
+ remove_method :execute
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 execute(&block)
27
+ execute_without_datadog(&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
+ def execute(&block)
36
+ uri = URI.parse(url)
37
+
38
+ return super(&block) unless datadog_configuration[:tracer].enabled
39
+
40
+ datadog_trace_request(uri) do |span|
41
+ if datadog_configuration[:distributed_tracing]
42
+ Datadog::HTTPPropagator.inject!(span.context, processed_headers)
43
+ end
44
+
45
+ super(&block)
46
+ end
47
+ end
48
+
49
+ def datadog_tag_request(uri, span)
50
+ span.resource = method.to_s.upcase
51
+ span.set_tag(Ext::HTTP::URL, uri.path)
52
+ span.set_tag(Ext::HTTP::METHOD, method.to_s.upcase)
53
+ span.set_tag(Ext::NET::TARGET_HOST, uri.host)
54
+ span.set_tag(Ext::NET::TARGET_PORT, uri.port)
55
+ end
56
+
57
+ def datadog_trace_request(uri)
58
+ span = datadog_configuration[:tracer].trace(REQUEST_TRACE_NAME,
59
+ service: datadog_configuration[:service_name],
60
+ span_type: Datadog::Ext::AppTypes::WEB)
61
+
62
+ datadog_tag_request(uri, span)
63
+
64
+ response = yield(span)
65
+
66
+ span.set_tag(Ext::HTTP::STATUS_CODE, response.code)
67
+ response
68
+ rescue ::RestClient::ExceptionWithResponse => e
69
+ span.set_error(e) if Ext::HTTP::ERROR_RANGE.cover?(e.http_code)
70
+ span.set_tag(Ext::HTTP::STATUS_CODE, e.http_code)
71
+
72
+ raise e
73
+ # rubocop:disable Lint/RescueException
74
+ rescue Exception => e
75
+ # rubocop:enable Lint/RescueException
76
+ span.set_error(e)
77
+
78
+ raise e
79
+ ensure
80
+ span.finish
81
+ end
82
+
83
+ def datadog_configuration
84
+ Datadog.configuration[:rest_client]
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,13 @@
1
+ require 'ddtrace/contrib/configuration/settings'
2
+
3
+ module Datadog
4
+ module Contrib
5
+ module Sequel
6
+ module Configuration
7
+ class Settings < Contrib::Configuration::Settings
8
+ # Add any custom Sequel settings or behavior here.
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -31,7 +31,7 @@ module Datadog
31
31
  def datadog_pin
32
32
  @pin ||= Datadog::Pin.new(
33
33
  Datadog.configuration[:sequel][:service_name] || adapter_name,
34
- app: Patcher::APP,
34
+ app: Integration::APP,
35
35
  app_type: Datadog::Ext::AppTypes::DB,
36
36
  tracer: Datadog.configuration[:sequel][:tracer] || Datadog.tracer
37
37
  )
@@ -0,0 +1,38 @@
1
+ require 'ddtrace/contrib/integration'
2
+ require 'ddtrace/contrib/sequel/configuration/settings'
3
+ require 'ddtrace/contrib/sequel/patcher'
4
+
5
+ module Datadog
6
+ module Contrib
7
+ module Sequel
8
+ # Description of Sequel integration
9
+ class Integration
10
+ include Contrib::Integration
11
+
12
+ APP = 'sequel'.freeze
13
+
14
+ register_as :sequel, auto_patch: false
15
+
16
+ def self.version
17
+ Gem.loaded_specs['sequel'] && Gem.loaded_specs['sequel'].version
18
+ end
19
+
20
+ def self.present?
21
+ super && defined?(::Sequel)
22
+ end
23
+
24
+ def self.compatible?
25
+ super && RUBY_VERSION >= '2.0.0'
26
+ end
27
+
28
+ def default_configuration
29
+ Configuration::Settings.new
30
+ end
31
+
32
+ def patcher
33
+ Patcher
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,3 +1,4 @@
1
+ require 'ddtrace/contrib/patcher'
1
2
  require 'ddtrace/contrib/sequel/database'
2
3
  require 'ddtrace/contrib/sequel/dataset'
3
4
 
@@ -7,40 +8,23 @@ module Datadog
7
8
  # Patcher enables patching of 'sequel' module.
8
9
  # This is used in monkey.rb to manually apply patches
9
10
  module Patcher
10
- include Base
11
-
12
- APP = 'sequel'.freeze
13
-
14
- register_as :sequel, auto_patch: false
15
- option :service_name
16
- option :tracer, default: Datadog.tracer
17
-
18
- @patched = false
11
+ include Contrib::Patcher
19
12
 
20
13
  module_function
21
14
 
22
- # patched? tells whether patch has been successfully applied
23
15
  def patched?
24
- @patched
16
+ done?(:sequel)
25
17
  end
26
18
 
27
19
  def patch
28
- if !@patched && compatible?
20
+ do_once(:sequel) do
29
21
  begin
30
22
  patch_sequel_database
31
23
  patch_sequel_dataset
32
-
33
- @patched = true
34
24
  rescue StandardError => e
35
25
  Datadog::Tracer.log.error("Unable to apply Sequel integration: #{e}")
36
26
  end
37
27
  end
38
-
39
- @patched
40
- end
41
-
42
- def compatible?
43
- RUBY_VERSION >= '2.0.0' && defined?(::Sequel)
44
28
  end
45
29
 
46
30
  def patch_sequel_database
@@ -15,8 +15,6 @@ module Datadog
15
15
 
16
16
  module_function
17
17
 
18
- attr_writer :registry
19
-
20
18
  def registry
21
19
  log_deprecation_warning('Monkey#registry')
22
20
  @registry
@@ -54,11 +52,6 @@ module Datadog
54
52
  end
55
53
 
56
54
  class << self
57
- def registry
58
- log_deprecation_warning('Monkey#registry')
59
- @registry
60
- end
61
-
62
55
  attr_writer :registry
63
56
  end
64
57
  end
@@ -32,6 +32,11 @@ module Datadog
32
32
  @done_once[key] = true
33
33
  end
34
34
  end
35
+
36
+ def done?(key)
37
+ return false unless instance_variable_defined?(:@done_once)
38
+ !@done_once.nil? && @done_once.key?(key)
39
+ end
35
40
  end
36
41
 
37
42
  # Extend the common methods so they're available as a module function.
data/lib/ddtrace/pin.rb CHANGED
@@ -10,7 +10,7 @@ module Datadog
10
10
  obj.datadog_pin
11
11
  end
12
12
 
13
- attr_accessor :service_name
13
+ attr_reader :service_name
14
14
  attr_accessor :app
15
15
  attr_accessor :tags
16
16
  attr_accessor :app_type
@@ -9,6 +9,12 @@ module Datadog
9
9
 
10
10
  # inject! popolates the env with span ID, trace ID and sampling priority
11
11
  def self.inject!(context, env)
12
+ # Prevent propagation from being attempted if context provided is nil.
13
+ if context.nil?
14
+ Datadog::Tracer.log.debug('Cannot inject context into env to propagate over HTTP: context is nil.'.freeze)
15
+ return
16
+ end
17
+
12
18
  env[HTTP_HEADER_TRACE_ID] = context.trace_id.to_s
13
19
  env[HTTP_HEADER_PARENT_ID] = context.span_id.to_s
14
20
  env[HTTP_HEADER_SAMPLING_PRIORITY] = context.sampling_priority.to_s
data/lib/ddtrace/span.rb CHANGED
@@ -25,7 +25,6 @@ module Datadog
25
25
  :tracer, :context
26
26
 
27
27
  attr_reader :parent
28
-
29
28
  # Create a new span linked to the given tracer. Call the \Tracer method <tt>start_span()</tt>
30
29
  # and then <tt>finish()</tt> once the tracer operation is over.
31
30
  #
@@ -24,6 +24,7 @@ module Datadog
24
24
  attr_writer :default_service
25
25
 
26
26
  ALLOWED_SPAN_OPTIONS = [:service, :resource, :span_type].freeze
27
+ DEFAULT_ON_ERROR = proc { |span, error| span.set_error(error) unless span.nil? }
27
28
 
28
29
  # Global, memoized, lazy initialized instance of a logger that is used within the the Datadog
29
30
  # namespace. This logger outputs to +STDOUT+ by default, and is considered thread-safe.
@@ -277,14 +278,23 @@ module Datadog
277
278
  # * +tags+: extra tags which should be added to the span.
278
279
  def trace(name, options = {})
279
280
  options[:child_of] = call_context
280
- span = start_span(name, options)
281
281
 
282
282
  # call the finish only if a block is given; this ensures
283
283
  # that a call to tracer.trace() without a block, returns
284
284
  # a span that should be manually finished.
285
285
  if block_given?
286
+ span = nil
287
+ return_value = nil
288
+
286
289
  begin
287
- yield(span)
290
+ begin
291
+ span = start_span(name, options)
292
+ # rubocop:disable Lint/UselessAssignment
293
+ rescue StandardError => e
294
+ Datadog::Tracer.log.debug('Failed to start span: #{e}')
295
+ ensure
296
+ return_value = yield(span)
297
+ end
288
298
  # rubocop:disable Lint/RescueException
289
299
  # Here we really want to catch *any* exception, not only StandardError,
290
300
  # as we really have no clue of what is in the block,
@@ -292,13 +302,15 @@ module Datadog
292
302
  # It's not a problem since we re-raise it afterwards so for example a
293
303
  # SignalException::Interrupt would still bubble up.
294
304
  rescue Exception => e
295
- span.set_error(e)
305
+ (options[:on_error] || DEFAULT_ON_ERROR).call(span, e)
296
306
  raise e
297
307
  ensure
298
- span.finish()
308
+ span.finish unless span.nil?
299
309
  end
310
+
311
+ return_value
300
312
  else
301
- span
313
+ start_span(name, options)
302
314
  end
303
315
  end
304
316
 
@@ -334,6 +346,11 @@ module Datadog
334
346
  call_context.current_span
335
347
  end
336
348
 
349
+ # Return the current active root span or +nil+.
350
+ def active_root_span
351
+ call_context.current_root_span
352
+ end
353
+
337
354
  # Send the trace to the writer to enqueue the spans list in the agent
338
355
  # sending queue.
339
356
  def write(trace)