ddtrace 0.24.0 → 0.25.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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +6 -6
  3. data/.circleci/images/primary/Dockerfile-1.9.3 +6 -2
  4. data/.circleci/images/primary/Dockerfile-2.0.0 +6 -2
  5. data/.circleci/images/primary/Dockerfile-2.1.10 +6 -2
  6. data/.circleci/images/primary/Dockerfile-2.2.10 +6 -2
  7. data/.circleci/images/primary/{Dockerfile-2.4.4 → Dockerfile-2.3.8} +5 -3
  8. data/.circleci/images/primary/{Dockerfile-2.3.7 → Dockerfile-2.4.6} +2 -2
  9. data/CHANGELOG.md +36 -0
  10. data/docker-compose.yml +6 -6
  11. data/docs/DevelopmentGuide.md +63 -0
  12. data/docs/GettingStarted.md +73 -3
  13. data/lib/ddtrace/configuration.rb +20 -1
  14. data/lib/ddtrace/contrib/http/circuit_breaker.rb +39 -11
  15. data/lib/ddtrace/contrib/http/instrumentation.rb +2 -11
  16. data/lib/ddtrace/ext/forced_tracing.rb +18 -2
  17. data/lib/ddtrace/ext/manual_tracing.rb +9 -0
  18. data/lib/ddtrace/ext/metrics.rb +0 -1
  19. data/lib/ddtrace/ext/runtime.rb +0 -1
  20. data/lib/ddtrace/forced_tracing.rb +3 -3
  21. data/lib/ddtrace/metrics.rb +0 -3
  22. data/lib/ddtrace/runtime/metrics.rb +0 -1
  23. data/lib/ddtrace/sync_writer.rb +1 -1
  24. data/lib/ddtrace/tracer.rb +78 -17
  25. data/lib/ddtrace/transport.rb +9 -0
  26. data/lib/ddtrace/transport/http.rb +85 -0
  27. data/lib/ddtrace/transport/http/adapters/net.rb +112 -0
  28. data/lib/ddtrace/transport/http/adapters/registry.rb +24 -0
  29. data/lib/ddtrace/transport/http/adapters/test.rb +77 -0
  30. data/lib/ddtrace/transport/http/adapters/unix_socket.rb +64 -0
  31. data/lib/ddtrace/transport/http/api.rb +46 -0
  32. data/lib/ddtrace/transport/http/api/endpoint.rb +27 -0
  33. data/lib/ddtrace/transport/http/api/fallbacks.rb +22 -0
  34. data/lib/ddtrace/transport/http/api/instance.rb +29 -0
  35. data/lib/ddtrace/transport/http/api/map.rb +14 -0
  36. data/lib/ddtrace/transport/http/api/spec.rb +15 -0
  37. data/lib/ddtrace/transport/http/builder.rb +165 -0
  38. data/lib/ddtrace/transport/http/client.rb +108 -0
  39. data/lib/ddtrace/transport/http/env.rb +48 -0
  40. data/lib/ddtrace/transport/http/response.rb +22 -0
  41. data/lib/ddtrace/transport/http/traces.rb +140 -0
  42. data/lib/ddtrace/transport/parcel.rb +13 -0
  43. data/lib/ddtrace/transport/request.rb +13 -0
  44. data/lib/ddtrace/transport/response.rb +49 -0
  45. data/lib/ddtrace/transport/statistics.rb +44 -0
  46. data/lib/ddtrace/transport/traces.rb +33 -0
  47. data/lib/ddtrace/version.rb +1 -1
  48. data/lib/ddtrace/workers.rb +6 -2
  49. data/lib/ddtrace/writer.rb +48 -21
  50. metadata +26 -4
@@ -6,16 +6,24 @@ module Datadog
6
6
  module Configuration
7
7
  attr_writer :configuration
8
8
 
9
+ RUBY_19_DEPRECATION_WARNING = %(
10
+ Support for Ruby versions < 2.0 in dd-trace-rb is DEPRECATED.
11
+ Last version to support Ruby < 2.0 will be 0.26.x, which will only receive critical bugfixes to existing features.
12
+ Support for Ruby versions < 2.0 will be REMOVED with version 0.27.0.).freeze
13
+
9
14
  def configuration
10
15
  @configuration ||= Settings.new
11
16
  end
12
17
 
13
18
  def configure(target = configuration, opts = {})
14
19
  if target.is_a?(Settings)
15
- yield(target)
20
+ yield(target) if block_given?
16
21
  else
17
22
  PinSetup.new(target, opts).call
18
23
  end
24
+
25
+ # Raise Ruby 1.9 deprecation warning, if necessary.
26
+ raise_ruby_19_deprecation_warning!
19
27
  end
20
28
 
21
29
  # Helper methods
@@ -26,5 +34,16 @@ module Datadog
26
34
  def runtime_metrics
27
35
  tracer.writer.runtime_metrics
28
36
  end
37
+
38
+ # TODO: Remove with version 0.27.0
39
+ def raise_ruby_19_deprecation_warning!
40
+ return unless Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.0')
41
+
42
+ require 'ddtrace/patcher'
43
+
44
+ Datadog::Patcher.do_once(:ruby_19_deprecation_warning) do
45
+ Datadog::Tracer.log.warn(RUBY_19_DEPRECATION_WARNING)
46
+ end
47
+ end
29
48
  end
30
49
  end
@@ -5,24 +5,52 @@ module Datadog
5
5
  # HTTP integration circuit breaker behavior
6
6
  # For avoiding recursive traces.
7
7
  module CircuitBreaker
8
- def should_skip_tracing?(req, address, port, transport, pin)
9
- # we don't want to trace our own call to the API (they use net/http)
10
- # when we know the host & port (from the URI) we use it, else (most-likely
8
+ def should_skip_tracing?(req, address, port, tracer)
9
+ return true if datadog_http_request?(req, address, port, tracer)
10
+
11
+ # we don't want a "shotgun" effect with two nested traces for one
12
+ # logical get, and request is likely to call itself recursively
13
+ active = tracer.active_span
14
+ return true if active && (active.name == Ext::SPAN_REQUEST)
15
+
16
+ false
17
+ end
18
+
19
+ # We don't want to trace our own call to the API (they use net/http)
20
+ # TODO: We don't want this kind of coupling with the transport.
21
+ # Remove this when transport implements its own "skip tracing" mechanism.
22
+ def datadog_http_request?(req, address, port, tracer)
23
+ transport = tracer.writer.transport
24
+
25
+ transport_hostname = nil
26
+ transport_port = nil
27
+
28
+ # Get settings from transport, if available.
29
+ case transport
30
+ when Datadog::HTTPTransport
31
+ transport_hostname = transport.hostname.to_s
32
+ transport_port = transport.port.to_i
33
+ when Datadog::Transport::HTTP::Client
34
+ adapter = transport.current_api.adapter
35
+ if adapter.is_a?(Datadog::Transport::HTTP::Adapters::Net)
36
+ transport_hostname = adapter.hostname.to_s
37
+ transport_port = adapter.port.to_i
38
+ end
39
+ end
40
+
41
+ # When we know the host & port (from the URI) we use it, else (most-likely
11
42
  # called with a block) rely on the URL at the end.
12
43
  if req.respond_to?(:uri) && req.uri
13
- if req.uri.host.to_s == transport.hostname.to_s &&
14
- req.uri.port.to_i == transport.port.to_i
44
+ if req.uri.host.to_s == transport_hostname &&
45
+ req.uri.port.to_i == transport_port
15
46
  return true
16
47
  end
17
48
  elsif address && port &&
18
- address.to_s == transport.hostname.to_s &&
19
- port.to_i == transport.port.to_i
49
+ address.to_s == transport_hostname &&
50
+ port.to_i == transport_port
20
51
  return true
21
52
  end
22
- # we don't want a "shotgun" effect with two nested traces for one
23
- # logical get, and request is likely to call itself recursively
24
- active = pin.tracer.active_span
25
- return true if active && (active.name == Ext::SPAN_REQUEST)
53
+
26
54
  false
27
55
  end
28
56
 
@@ -54,9 +54,7 @@ module Datadog
54
54
  pin = datadog_pin
55
55
  return super(req, body, &block) unless pin && pin.tracer
56
56
 
57
- transport = pin.tracer.writer.transport
58
-
59
- if Datadog::Contrib::HTTP.should_skip_tracing?(req, @address, @port, transport, pin)
57
+ if Datadog::Contrib::HTTP.should_skip_tracing?(req, @address, @port, pin.tracer)
60
58
  return super(req, body, &block)
61
59
  end
62
60
 
@@ -67,14 +65,7 @@ module Datadog
67
65
  span.resource = req.method
68
66
 
69
67
  if pin.tracer.enabled && !Datadog::Contrib::HTTP.should_skip_distributed_tracing?(pin)
70
- req.add_field(Datadog::Ext::DistributedTracing::HTTP_HEADER_TRACE_ID, span.trace_id)
71
- req.add_field(Datadog::Ext::DistributedTracing::HTTP_HEADER_PARENT_ID, span.span_id)
72
- if span.context.sampling_priority
73
- req.add_field(
74
- Datadog::Ext::DistributedTracing::HTTP_HEADER_SAMPLING_PRIORITY,
75
- span.context.sampling_priority
76
- )
77
- end
68
+ Datadog::HTTPPropagator.inject!(span.context, req)
78
69
  end
79
70
  rescue StandardError => e
80
71
  Datadog::Tracer.log.error("error preparing span for http request: #{e}")
@@ -1,9 +1,25 @@
1
+ require 'ddtrace/ext/manual_tracing'
2
+ require 'ddtrace/tracer'
3
+
1
4
  module Datadog
2
5
  module Ext
3
6
  # Defines constants for forced tracing
4
7
  module ForcedTracing
5
- TAG_KEEP = 'manual.drop'.freeze
6
- TAG_DROP = 'manual.keep'.freeze
8
+ @deprecation_warning_shown = false
9
+
10
+ def self.const_missing(name)
11
+ super unless Ext::ManualTracing.const_defined?(name)
12
+
13
+ # Only log each deprecation warning once (safeguard against log spam)
14
+ unless @deprecation_warning_shown
15
+ Datadog::Tracer.log.warn(
16
+ 'forced tracing: Datadog::Ext::ForcedTracing has been renamed to Datadog::Ext::ManualTracing'
17
+ )
18
+ @deprecation_warning_shown = true
19
+ end
20
+
21
+ Ext::ManualTracing.const_get(name)
22
+ end
7
23
  end
8
24
  end
9
25
  end
@@ -0,0 +1,9 @@
1
+ module Datadog
2
+ module Ext
3
+ # Defines constants for manual tracing
4
+ module ManualTracing
5
+ TAG_KEEP = 'manual.keep'.freeze
6
+ TAG_DROP = 'manual.drop'.freeze
7
+ end
8
+ end
9
+ end
@@ -4,7 +4,6 @@ module Datadog
4
4
  TAG_LANG = 'language'.freeze
5
5
  TAG_LANG_INTERPRETER = 'language-interpreter'.freeze
6
6
  TAG_LANG_VERSION = 'language-version'.freeze
7
- TAG_RUNTIME_ID = 'runtime-id'.freeze
8
7
  TAG_TRACER_VERSION = 'tracer-version'.freeze
9
8
  end
10
9
  end
@@ -16,7 +16,6 @@ module Datadog
16
16
  TRACER_VERSION = Datadog::VERSION::STRING
17
17
 
18
18
  TAG_LANG = 'language'.freeze
19
- TAG_RUNTIME_ID = 'runtime-id'.freeze
20
19
 
21
20
  # Metrics
22
21
  module Metrics
@@ -1,4 +1,4 @@
1
- require 'ddtrace/ext/forced_tracing'
1
+ require 'ddtrace/ext/manual_tracing'
2
2
  require 'ddtrace/ext/priority'
3
3
 
4
4
  module Datadog
@@ -49,9 +49,9 @@ module Datadog
49
49
  # Configure sampling priority if they give us a forced tracing tag
50
50
  # DEV: Do not set if the value they give us is explicitly "false"
51
51
  case key
52
- when Ext::ForcedTracing::TAG_KEEP
52
+ when Ext::ManualTracing::TAG_KEEP
53
53
  ForcedTracing.keep(self) unless value == false
54
- when Ext::ForcedTracing::TAG_DROP
54
+ when Ext::ManualTracing::TAG_DROP
55
55
  ForcedTracing.drop(self) unless value == false
56
56
  else
57
57
  # Otherwise, set the tag normally.
@@ -117,9 +117,6 @@ module Datadog
117
117
  # and defaults are unfrozen for mutation in Statsd.
118
118
  DEFAULT.dup.tap do |options|
119
119
  options[:tags] = options[:tags].dup
120
-
121
- # Add runtime ID dynamically because it might change during fork.
122
- options[:tags] << "#{Ext::Metrics::TAG_RUNTIME_ID}:#{Runtime::Identity.id}".freeze
123
120
  end
124
121
  end
125
122
  end
@@ -26,7 +26,6 @@ module Datadog
26
26
 
27
27
  # Tag span with language and runtime ID for association with metrics
28
28
  span.set_tag(Ext::Runtime::TAG_LANG, Runtime::Identity.lang)
29
- span.set_tag(Ext::Runtime::TAG_RUNTIME_ID, Runtime::Identity.id)
30
29
  end
31
30
 
32
31
  # Associate service with runtime metrics
@@ -12,7 +12,7 @@ module Datadog
12
12
  def initialize(options = {})
13
13
  @transport = options.fetch(:transport) do
14
14
  transport_options = options.fetch(:transport_options, {})
15
- HTTPTransport.new(transport_options)
15
+ Transport::HTTP.default(transport_options)
16
16
  end
17
17
 
18
18
  # Runtime metrics
@@ -135,12 +135,9 @@ module Datadog
135
135
  #
136
136
  def configure(options = {})
137
137
  enabled = options.fetch(:enabled, nil)
138
- hostname = options.fetch(:hostname, nil)
139
- port = options.fetch(:port, nil)
140
138
 
141
139
  # Those are rare "power-user" options.
142
140
  sampler = options.fetch(:sampler, nil)
143
- priority_sampling = options.fetch(:priority_sampling, nil)
144
141
  max_spans_before_partial_flush = options.fetch(:max_spans_before_partial_flush, nil)
145
142
  min_spans_before_partial_flush = options.fetch(:min_spans_before_partial_flush, nil)
146
143
  partial_flush_timeout = options.fetch(:partial_flush_timeout, nil)
@@ -148,19 +145,7 @@ module Datadog
148
145
  @enabled = enabled unless enabled.nil?
149
146
  @sampler = sampler unless sampler.nil?
150
147
 
151
- # Re-build the sampler and writer if priority sampling is enabled,
152
- # but neither are configured. Verify the sampler isn't already a
153
- # priority sampler too, so we don't wrap one with another.
154
- if priority_sampling != false && !@sampler.is_a?(PrioritySampler)
155
- @sampler = PrioritySampler.new(base_sampler: @sampler)
156
- @writer = Writer.new(priority_sampler: @sampler)
157
- elsif priority_sampling == false
158
- @sampler = sampler || Datadog::AllSampler.new if @sampler.is_a?(PrioritySampler)
159
- @writer = Writer.new
160
- end
161
-
162
- @writer.transport.hostname = hostname unless hostname.nil?
163
- @writer.transport.port = port unless port.nil?
148
+ configure_writer(options)
164
149
 
165
150
  @context_flush = Datadog::ContextFlush.new(options) unless min_spans_before_partial_flush.nil? &&
166
151
  max_spans_before_partial_flush.nil? &&
@@ -393,6 +378,82 @@ module Datadog
393
378
  @writer.write(trace)
394
379
  end
395
380
 
396
- private :write, :guess_context_and_parent
381
+ # TODO: Move this kind of configuration building out of the tracer.
382
+ # Tracer should not have this kind of knowledge of writer.
383
+ # rubocop:disable Metrics/PerceivedComplexity
384
+ # rubocop:disable Metrics/CyclomaticComplexity
385
+ # rubocop:disable Metrics/MethodLength
386
+ def configure_writer(options = {})
387
+ hostname = options.fetch(:hostname, nil)
388
+ port = options.fetch(:port, nil)
389
+ sampler = options.fetch(:sampler, nil)
390
+ priority_sampling = options.fetch(:priority_sampling, nil)
391
+ writer = options.fetch(:writer, nil)
392
+ transport_options = options.fetch(:transport_options, {})
393
+
394
+ # Compile writer options
395
+ rebuild_writer = false
396
+ writer_options = {}
397
+
398
+ # Re-build the sampler and writer if priority sampling is enabled,
399
+ # but neither are configured. Verify the sampler isn't already a
400
+ # priority sampler too, so we don't wrap one with another.
401
+ if options.key?(:writer)
402
+ if writer.priority_sampler.nil?
403
+ deactivate_priority_sampling!(sampler)
404
+ else
405
+ activate_priority_sampling!(writer.priority_sampler)
406
+ end
407
+ elsif priority_sampling != false && !@sampler.is_a?(PrioritySampler)
408
+ writer_options[:priority_sampler] = activate_priority_sampling!(@sampler)
409
+ rebuild_writer = true
410
+ elsif priority_sampling == false
411
+ deactivate_priority_sampling!(sampler)
412
+ rebuild_writer = true
413
+ elsif @sampler.is_a?(PrioritySampler)
414
+ # Make sure to add sampler to options if transport is rebuilt.
415
+ writer_options[:priority_sampler] = @sampler
416
+ end
417
+
418
+ # Apply options to transport
419
+ if transport_options.is_a?(Proc)
420
+ transport_options = { on_build: transport_options }
421
+ rebuild_writer = true
422
+ end
423
+
424
+ if hostname || port
425
+ transport_options[:hostname] = hostname unless hostname.nil?
426
+ transport_options[:port] = port unless port.nil?
427
+ rebuild_writer = true
428
+ end
429
+
430
+ writer_options[:transport_options] = transport_options
431
+
432
+ if rebuild_writer || writer
433
+ # Make sure old writer is shut down before throwing away.
434
+ # Don't want additional threads running...
435
+ @writer.stop unless writer.nil?
436
+ @writer = writer || Writer.new(writer_options)
437
+ end
438
+ end
439
+
440
+ def activate_priority_sampling!(base_sampler = nil)
441
+ @sampler = if base_sampler.is_a?(PrioritySampler)
442
+ base_sampler
443
+ else
444
+ PrioritySampler.new(base_sampler: base_sampler)
445
+ end
446
+ end
447
+
448
+ def deactivate_priority_sampling!(base_sampler = nil)
449
+ @sampler = base_sampler || Datadog::AllSampler.new if @sampler.is_a?(PrioritySampler)
450
+ end
451
+
452
+ private \
453
+ :activate_priority_sampling!,
454
+ :configure_writer,
455
+ :deactivate_priority_sampling!,
456
+ :guess_context_and_parent,
457
+ :write
397
458
  end
398
459
  end
@@ -16,6 +16,12 @@ module Datadog
16
16
  DEFAULT_AGENT_HOST = '127.0.0.1'.freeze
17
17
  DEFAULT_TRACE_AGENT_PORT = '8126'.freeze
18
18
 
19
+ DEPRECATION_WARNING = %(
20
+ Datadog::HTTPTransport is deprecated as of version 0.24.0.
21
+ It has been replaced by Datadog::Transport:HTTP, which is the new default.
22
+ You can create one with `Datadog::Transport::HTTP.default`
23
+ Datadog::HTTPTransport will be REMOVED in version 0.25.0).freeze
24
+
19
25
  # seconds before the transport timeout
20
26
  TIMEOUT = 1
21
27
 
@@ -46,6 +52,9 @@ module Datadog
46
52
  private_constant :API
47
53
 
48
54
  def initialize(options = {})
55
+ # Log deprecation warning
56
+ Datadog::Tracer.log.warn(DEPRECATION_WARNING)
57
+
49
58
  api_version = options.fetch(:api_version, V3)
50
59
 
51
60
  @hostname = options[:hostname] || ENV['DD_AGENT_HOST'] || DEFAULT_AGENT_HOST
@@ -0,0 +1,85 @@
1
+ require 'ddtrace/version'
2
+ require 'ddtrace/ext/runtime'
3
+
4
+ require 'ddtrace/transport/http/builder'
5
+ require 'ddtrace/transport/http/api'
6
+
7
+ require 'ddtrace/transport/http/adapters/net'
8
+ require 'ddtrace/transport/http/adapters/test'
9
+ require 'ddtrace/transport/http/adapters/unix_socket'
10
+
11
+ module Datadog
12
+ module Transport
13
+ # Namespace for HTTP transport components
14
+ module HTTP
15
+ DEFAULT_AGENT_HOST = '127.0.0.1'.freeze
16
+ DEFAULT_TRACE_AGENT_PORT = 8126
17
+ DEFAULT_HEADERS = {
18
+ 'Datadog-Meta-Lang'.freeze => Datadog::Ext::Runtime::LANG,
19
+ 'Datadog-Meta-Lang-Version'.freeze => Datadog::Ext::Runtime::LANG_VERSION,
20
+ 'Datadog-Meta-Lang-Interpreter'.freeze => Datadog::Ext::Runtime::LANG_INTERPRETER,
21
+ 'Datadog-Meta-Tracer-Version'.freeze => Datadog::Ext::Runtime::TRACER_VERSION
22
+ }.freeze
23
+
24
+ module_function
25
+
26
+ # Builds a new Transport::HTTP::Client
27
+ def new(&block)
28
+ Builder.new(&block).to_client
29
+ end
30
+
31
+ # Builds a new Transport::HTTP::Client with default settings
32
+ # Pass a block to override any settings.
33
+ def default(options = {})
34
+ new do |transport|
35
+ transport.adapter :net_http,
36
+ ENV.fetch('DD_AGENT_HOST', DEFAULT_AGENT_HOST),
37
+ ENV.fetch('DD_TRACE_AGENT_PORT', DEFAULT_TRACE_AGENT_PORT)
38
+
39
+ transport.headers DEFAULT_HEADERS
40
+
41
+ apis = API.defaults
42
+
43
+ transport.api API::V4, apis[API::V4], fallback: API::V3, default: true
44
+ transport.api API::V3, apis[API::V3], fallback: API::V2
45
+ transport.api API::V2, apis[API::V2]
46
+
47
+ # Apply any settings given by options
48
+ unless options.empty?
49
+ # Change hostname/port
50
+ if options.key?(:hostname) || options.key?(:port)
51
+ hostname = options.fetch(:hostname, default_hostname)
52
+ port = options.fetch(:port, default_port)
53
+ transport.adapter :net_http, hostname, port
54
+ end
55
+
56
+ # Change default API
57
+ transport.default_api = options[:api_version] if options.key?(:api_version)
58
+
59
+ # Add headers
60
+ transport.headers options[:headers] if options.key?(:headers)
61
+
62
+ # Execute on_build callback
63
+ options[:on_build].call(transport) if options[:on_build].is_a?(Proc)
64
+ end
65
+
66
+ # Call block to apply any customization, if provided.
67
+ yield(transport) if block_given?
68
+ end
69
+ end
70
+
71
+ def default_hostname
72
+ ENV.fetch('DD_AGENT_HOST', DEFAULT_AGENT_HOST)
73
+ end
74
+
75
+ def default_port
76
+ ENV.fetch('DD_TRACE_AGENT_PORT', DEFAULT_TRACE_AGENT_PORT)
77
+ end
78
+
79
+ # Add adapters to registry
80
+ Builder::REGISTRY.set(Adapters::Net, :net_http)
81
+ Builder::REGISTRY.set(Adapters::Test, :test)
82
+ Builder::REGISTRY.set(Adapters::UnixSocket, :unix)
83
+ end
84
+ end
85
+ end