ddtrace 0.15.0 → 0.16.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 83a0125cb996a905f1d25812abb1f64d4703b4ba870b378dbb746675322ea7f4
4
- data.tar.gz: 98dbbbffe51a7912106269183b12f41fea114f1b7fa7dc42e3266ad910ba57ff
3
+ metadata.gz: e4c07e2d55c825aa80fda2d80abd0be7bce884c47a869d54fc5ca32f4e60653e
4
+ data.tar.gz: a58bd25922fa32c3c921395a9a00b37e5b0941399f3a48cf15735ef11935808f
5
5
  SHA512:
6
- metadata.gz: 4f750d5f3418be21196ee9109f1faa9eb904baf7bd5d4f8eb07b00e6bce5f1b7dc393e080a4be53e350bbfbeedf3216823b4b58df7720fa64688fb9c1ab10f64
7
- data.tar.gz: 4c056618940a40462006828d0ede6613a51e78c062555544605480f540a471747a9fb76c9ee496dc3b9b82f7595a77df4a933d14eed3db18457a61c9c76ff880
6
+ metadata.gz: cd1c737a23055b67ccd2a0c081bfd64cd6156d744ca9c6b982623406b04be282de3c7b0328757dafef0bd9916f822c8a59a742be37f04f3225e192574fd7f0b9
7
+ data.tar.gz: 9418e24136ab3721c4404cce92dbf637d86139b5d574356e6918a5cf618de2a881add70f1420ca419afe6a751b1fd9a58871e780d22294187a5327787e5ebba4
data/CHANGELOG.md CHANGED
@@ -4,6 +4,17 @@
4
4
 
5
5
  ## [Unreleased (beta)]
6
6
 
7
+ ## [0.16.0] - 2018-09-18
8
+
9
+ Release notes: https://github.com/DataDog/dd-trace-rb/releases/tag/v0.16.0
10
+
11
+ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.15.0...v0.16.0
12
+
13
+ ### Added
14
+
15
+ - OpenTracing support (#517)
16
+ - `middleware` option for disabling Rails trace middleware. (#552)
17
+
7
18
  ## [0.15.0] - 2018-09-12
8
19
 
9
20
  Release notes: https://github.com/DataDog/dd-trace-rb/releases/tag/v0.15.0
@@ -500,8 +511,9 @@ Release notes: https://github.com/DataDog/dd-trace-rb/releases/tag/v0.3.1
500
511
 
501
512
  Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
502
513
 
503
- [Unreleased (stable)]: https://github.com/DataDog/dd-trace-rb/compare/v0.15.0...master
504
- [Unreleased (beta)]: https://github.com/DataDog/dd-trace-rb/compare/v0.15.0...0.16-dev
514
+ [Unreleased (stable)]: https://github.com/DataDog/dd-trace-rb/compare/v0.16.0...master
515
+ [Unreleased (beta)]: https://github.com/DataDog/dd-trace-rb/compare/v0.16.0...0.17-dev
516
+ [0.15.0]: https://github.com/DataDog/dd-trace-rb/compare/v0.15.0...v0.16.0
505
517
  [0.15.0]: https://github.com/DataDog/dd-trace-rb/compare/v0.14.2...v0.15.0
506
518
  [0.14.2]: https://github.com/DataDog/dd-trace-rb/compare/v0.14.1...v0.14.2
507
519
  [0.14.1]: https://github.com/DataDog/dd-trace-rb/compare/v0.14.0...v0.14.1
data/Rakefile CHANGED
@@ -15,7 +15,11 @@ namespace :spec do
15
15
 
16
16
  RSpec::Core::RakeTask.new(:main) do |t|
17
17
  t.pattern = 'spec/**/*_spec.rb'
18
- t.exclude_pattern = 'spec/**/{contrib,benchmark,redis}/**/*_spec.rb'
18
+ t.exclude_pattern = 'spec/**/{contrib,benchmark,redis,opentracer}/**/*_spec.rb'
19
+ end
20
+
21
+ RSpec::Core::RakeTask.new(:opentracer) do |t|
22
+ t.pattern = 'spec/ddtrace/opentracer/**/*_spec.rb'
19
23
  end
20
24
 
21
25
  RSpec::Core::RakeTask.new(:rails) do |t|
@@ -311,6 +315,7 @@ task :ci do
311
315
  sh 'bundle exec rake test:main'
312
316
  sh 'bundle exec rake spec:main'
313
317
  sh 'bundle exec rake spec:contrib'
318
+ sh 'bundle exec rake spec:opentracer'
314
319
 
315
320
  if RUBY_PLATFORM != 'java'
316
321
  # Contrib minitests
@@ -366,6 +371,7 @@ task :ci do
366
371
  sh 'bundle exec rake test:main'
367
372
  sh 'bundle exec rake spec:main'
368
373
  sh 'bundle exec rake spec:contrib'
374
+ sh 'bundle exec rake spec:opentracer'
369
375
 
370
376
  if RUBY_PLATFORM != 'java'
371
377
  # Contrib minitests
@@ -432,6 +438,7 @@ task :ci do
432
438
  sh 'bundle exec rake test:main'
433
439
  sh 'bundle exec rake spec:main'
434
440
  sh 'bundle exec rake spec:contrib'
441
+ sh 'bundle exec rake spec:opentracer'
435
442
 
436
443
  if RUBY_PLATFORM != 'java'
437
444
  # Contrib minitests
@@ -497,6 +504,7 @@ task :ci do
497
504
  sh 'bundle exec rake test:main'
498
505
  sh 'bundle exec rake spec:main'
499
506
  sh 'bundle exec rake spec:contrib'
507
+ sh 'bundle exec rake spec:opentracer'
500
508
 
501
509
  if RUBY_PLATFORM != 'java'
502
510
  # Contrib minitests
data/ddtrace.gemspec CHANGED
@@ -33,6 +33,8 @@ Gem::Specification.new do |spec|
33
33
  spec.require_paths = ['lib']
34
34
 
35
35
  spec.add_dependency 'msgpack'
36
+ # TODO: Move this to Appraisals?
37
+ spec.add_dependency 'opentracing', '>= 0.4.1'
36
38
 
37
39
  spec.add_development_dependency 'rake', '>= 10.5'
38
40
  spec.add_development_dependency 'rubocop', '= 0.49.1' if RUBY_VERSION >= '2.1.0'
@@ -21,6 +21,7 @@ For descriptions of terminology used in APM, take a look at the [official docume
21
21
  - [Installation](#installation)
22
22
  - [Quickstart for Rails applications](#quickstart-for-rails-applications)
23
23
  - [Quickstart for Ruby applications](#quickstart-for-ruby-applications)
24
+ - [Quickstart for OpenTracing](#quickstart-for-opentracing)
24
25
  - [Manual instrumentation](#manual-instrumentation)
25
26
  - [Integration instrumentation](#integration-instrumentation)
26
27
  - [Active Record](#active-record)
@@ -59,6 +60,7 @@ For descriptions of terminology used in APM, take a look at the [official docume
59
60
  - [Processing pipeline](#processing-pipeline)
60
61
  - [Filtering](#filtering)
61
62
  - [Processing](#processing)
63
+ - [OpenTracing](#opentracing)
62
64
 
63
65
  ## Compatibility
64
66
 
@@ -75,10 +77,6 @@ For descriptions of terminology used in APM, take a look at the [official docume
75
77
  | | | 2.4 | Full |
76
78
  | JRuby | http://jruby.org/ | 9.1.5 | Experimental |
77
79
 
78
- *Full* support indicates all tracer features are available.
79
-
80
- *Experimental* indicates most features should be available, but unverified.
81
-
82
80
  **Supported web servers**:
83
81
 
84
82
  | Type | Documentation | Version | Support type |
@@ -87,6 +85,16 @@ For descriptions of terminology used in APM, take a look at the [official docume
87
85
  | Unicorn | https://bogomips.org/unicorn/ | 4.8+ / 5.1+ | Full |
88
86
  | Passenger | https://www.phusionpassenger.com/ | 5.0+ | Full |
89
87
 
88
+ **Supported tracing frameworks**:
89
+
90
+ | Type | Documentation | Version | Support type |
91
+ | ----------- | ----------------------------------------------- | --------------------- | ------------ |
92
+ | OpenTracing | https://github.com/opentracing/opentracing-ruby | 0.4.1+ (w/ Ruby 2.1+) | Experimental |
93
+
94
+ *Full* support indicates all tracer features are available.
95
+
96
+ *Experimental* indicates most features should be available, but unverified.
97
+
90
98
  ## Installation
91
99
 
92
100
  The following steps will help you quickly start tracing your Ruby application.
@@ -136,6 +144,36 @@ The Ruby APM tracer sends trace data through the Datadog Agent.
136
144
  1. Activate integration instrumentation (see [Integration instrumentation](#integration-instrumentation))
137
145
  2. Add manual instrumentation around your code (see [Manual instrumentation](#manual-instrumentation))
138
146
 
147
+ ### Quickstart for OpenTracing
148
+
149
+ 1. Install the gem with `gem install ddtrace`
150
+ 2. To your OpenTracing configuration file, add the following:
151
+
152
+ ```ruby
153
+ require 'opentracing'
154
+ require 'ddtrace'
155
+ require 'ddtrace/opentracer'
156
+
157
+ # Activate the Datadog tracer for OpenTracing
158
+ OpenTracing.global_tracer = Datadog::OpenTracer::Tracer.new
159
+ ```
160
+
161
+ 3. (Optional) Add a configuration block to your Ruby application to configure Datadog with:
162
+
163
+ ```ruby
164
+ Datadog.configure do |c|
165
+ # Configure the Datadog tracer here.
166
+ # Activate integrations, change tracer settings, etc...
167
+ # By default without additional configuration,
168
+ # no additional integrations will be traced, only
169
+ # what you have instrumented with OpenTracing.
170
+ end
171
+ ```
172
+
173
+ 4. (Optional) Add or activate additional instrumentation by doing either of the following:
174
+ 1. Activate Datadog integration instrumentation (see [Integration instrumentation](#integration-instrumentation))
175
+ 2. Add Datadog manual instrumentation around your code (see [Manual instrumentation](#manual-instrumentation))
176
+
139
177
  ### Final steps for installation
140
178
 
141
179
  After setting up, your services will appear on the [APM services page](https://app.datadoghq.com/apm/services) within a few minutes. Learn more about [using the APM UI][visualization docs].
@@ -869,6 +907,7 @@ Where `options` is an optional `Hash` that accepts the following parameters:
869
907
  | ``database_service`` | Database service name used when tracing database activity | ``<app_name>-<adapter_name>`` |
870
908
  | ``exception_controller`` | Class or Module which identifies a custom exception controller class. Tracer provides improved error behavior when it can identify custom exception controllers. By default, without this option, it 'guesses' what a custom exception controller looks like. Providing this option aids this identification. | ``nil`` |
871
909
  | ``distributed_tracing`` | Enables [distributed tracing](#distributed-tracing) so that this service trace is connected with a trace of another service if tracing headers are received | `false` |
910
+ | ``middleware`` | Add the trace middleware to the Rails application. Set to `false` if you don't want the middleware to load. | `true` |
872
911
  | ``middleware_names`` | Enables any short-circuited middleware requests to display the middleware name as resource for the trace. | `false` |
873
912
  | ``template_base_path`` | Used when the template name is parsed. If you don't store your templates in the ``views/`` folder, you may need to change this value | ``views/`` |
874
913
  | ``tracer`` | A ``Datadog::Tracer`` instance used to instrument the application. Usually you don't need to set that. | ``Datadog.tracer`` |
@@ -1510,3 +1549,32 @@ Datadog::Pipeline.before_flush(
1510
1549
  Datadog::Pipeline::SpanProcessor.new { |span| span.resource.gsub!(/password=.*/, '') }
1511
1550
  )
1512
1551
  ```
1552
+
1553
+ ### OpenTracing
1554
+
1555
+ For setting up Datadog with OpenTracing, see out [Quickstart for OpenTracing](#quickstart-for-opentracing) section for details.
1556
+
1557
+ **Configuring Datadog tracer settings**
1558
+
1559
+ The underlying Datadog tracer can be configured by passing options (which match `Datadog::Tracer`) when configuring the global tracer:
1560
+
1561
+ ```ruby
1562
+ # Where `options` is a Hash of options provided to Datadog::Tracer
1563
+ OpenTracing.global_tracer = Datadog::OpenTracer::Tracer.new(options)
1564
+ ```
1565
+
1566
+ It can also be configured by using `Datadog.configure` described in the [Tracer settings](#tracer-settings) section.
1567
+
1568
+ **Activating and configuring integrations**
1569
+
1570
+ By default, configuring OpenTracing with Datadog will not automatically activate any additional instrumentation provided by Datadog. You will only receive spans and traces from OpenTracing instrumentation you have in your application.
1571
+
1572
+ However, additional instrumentation provided by Datadog can be activated alongside OpenTracing using `Datadog.configure`, which can be used to further enhance your tracing. To activate this, see [Integration instrumentation](#integration-instrumentation) for more details.
1573
+
1574
+ **Supported serialization formats**
1575
+
1576
+ | Type | Supported? | Additional information |
1577
+ | ------------------------------ | ---------- | ---------------------- |
1578
+ | `OpenTracing::FORMAT_TEXT_MAP` | Yes | |
1579
+ | `OpenTracing::FORMAT_RACK` | Yes | Because of the loss of resolution in the Rack format, please note that baggage items with names containing either upper case characters or `-` will be converted to lower case and `_` in a round-trip respectively. We recommend avoiding these characters, or accommodating accordingly on the receiving end. |
1580
+ | `OpenTracing::FORMAT_BINARY` | No | |
@@ -39,7 +39,8 @@ module Datadog
39
39
  @patched = true
40
40
  end
41
41
 
42
- if !@middleware_patched && get_option(:middleware_names)
42
+ if (!instance_variable_defined?(:@middleware_patched) || !@middleware_patched) \
43
+ && get_option(:middleware_names)
43
44
  if get_option(:application)
44
45
  enable_middleware_names
45
46
  @middleware_patched = true
@@ -1,4 +1,7 @@
1
1
  require 'ddtrace/contrib/rails/utils'
2
+ require 'ddtrace/contrib/rails/framework'
3
+ require 'ddtrace/contrib/rails/middlewares'
4
+ require 'ddtrace/contrib/rack/middlewares'
2
5
 
3
6
  module Datadog
4
7
  module Contrib
@@ -17,6 +20,7 @@ module Datadog
17
20
  Datadog.configuration[:active_record][:service_name] = value
18
21
  end
19
22
  end
23
+ option :middleware, default: true
20
24
  option :middleware_names, default: false
21
25
  option :distributed_tracing, default: false
22
26
  option :template_base_path, default: 'views/'
@@ -28,7 +32,41 @@ module Datadog
28
32
  class << self
29
33
  def patch
30
34
  return @patched if patched? || !compatible?
31
- require_relative 'framework'
35
+
36
+ # Add a callback hook to add the trace middleware before the application initializes.
37
+ # Otherwise the middleware stack will be frozen.
38
+ do_once(:rails_before_initialize_hook) do
39
+ ::ActiveSupport.on_load(:before_initialize) do
40
+ # Sometimes we don't want to activate middleware e.g. OpenTracing, etc.
41
+ if Datadog.configuration[:rails][:middleware]
42
+ # Add trace middleware
43
+ config.middleware.insert_before(0, Datadog::Contrib::Rack::TraceMiddleware)
44
+
45
+ # Insert right after Rails exception handling middleware, because if it's before,
46
+ # it catches and swallows the error. If it's too far after, custom middleware can find itself
47
+ # between, and raise exceptions that don't end up getting tagged on the request properly.
48
+ # e.g lost stack trace.
49
+ config.middleware.insert_after(
50
+ ActionDispatch::ShowExceptions,
51
+ Datadog::Contrib::Rails::ExceptionMiddleware
52
+ )
53
+ end
54
+ end
55
+ end
56
+
57
+ # Add a callback hook to finish configuring the tracer after the application is initialized.
58
+ # We need to wait for some things, like application name, middleware stack, etc.
59
+ do_once(:rails_after_initialize_hook) do
60
+ ::ActiveSupport.on_load(:after_initialize) do
61
+ Datadog::Contrib::Rails::Framework.setup
62
+
63
+ # Add instrumentation to Rails components
64
+ Datadog::Contrib::Rails::ActionController.instrument
65
+ Datadog::Contrib::Rails::ActionView.instrument
66
+ Datadog::Contrib::Rails::ActiveSupport.instrument
67
+ end
68
+ end
69
+
32
70
  @patched = true
33
71
  rescue => e
34
72
  Datadog::Tracer.log.error("Unable to apply Rails integration: #{e}")
@@ -49,5 +87,3 @@ module Datadog
49
87
  end
50
88
  end
51
89
  end
52
-
53
- require 'ddtrace/contrib/rails/railtie' if Datadog.registry[:rails].compatible?
@@ -5,11 +5,14 @@ require 'ddtrace/contrib/rack/middlewares'
5
5
  module Datadog
6
6
  # Railtie class initializes
7
7
  class Railtie < Rails::Railtie
8
- config.app_middleware.insert_before(0, Datadog::Contrib::Rack::TraceMiddleware)
9
- # Insert right after Rails exception handling middleware, because if it's before,
10
- # it catches and swallows the error. If it's too far after, custom middleware can find itself
11
- # between, and raise exceptions that don't end up getting tagged on the request properly (e.g lost stack trace.)
12
- config.app_middleware.insert_after(ActionDispatch::ShowExceptions, Datadog::Contrib::Rails::ExceptionMiddleware)
8
+ # Add the trace middleware to the application stack
9
+ initializer 'datadog.add_middleware' do |app|
10
+ app.middleware.insert_before(0, Datadog::Contrib::Rack::TraceMiddleware)
11
+ # Insert right after Rails exception handling middleware, because if it's before,
12
+ # it catches and swallows the error. If it's too far after, custom middleware can find itself
13
+ # between, and raise exceptions that don't end up getting tagged on the request properly (e.g lost stack trace.)
14
+ app.middleware.insert_after(ActionDispatch::ShowExceptions, Datadog::Contrib::Rails::ExceptionMiddleware)
15
+ end
13
16
 
14
17
  config.after_initialize do
15
18
  Datadog::Contrib::Rails::Framework.setup
@@ -0,0 +1,40 @@
1
+ module Datadog
2
+ # Namespace for ddtrace OpenTracing implementation
3
+ module OpenTracer
4
+ module_function
5
+
6
+ def supported?
7
+ Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.1')
8
+ end
9
+
10
+ def load_opentracer
11
+ require 'opentracing'
12
+ require 'opentracing/carrier'
13
+ require 'ddtrace'
14
+ require 'ddtrace/opentracer/carrier'
15
+ require 'ddtrace/opentracer/tracer'
16
+ require 'ddtrace/opentracer/span'
17
+ require 'ddtrace/opentracer/span_context'
18
+ require 'ddtrace/opentracer/span_context_factory'
19
+ require 'ddtrace/opentracer/scope'
20
+ require 'ddtrace/opentracer/scope_manager'
21
+ require 'ddtrace/opentracer/thread_local_scope'
22
+ require 'ddtrace/opentracer/thread_local_scope_manager'
23
+ require 'ddtrace/opentracer/distributed_headers'
24
+ require 'ddtrace/opentracer/propagator'
25
+ require 'ddtrace/opentracer/text_map_propagator'
26
+ require 'ddtrace/opentracer/binary_propagator'
27
+ require 'ddtrace/opentracer/rack_propagator'
28
+ require 'ddtrace/opentracer/global_tracer'
29
+
30
+ # Modify the OpenTracing module functions
31
+ OpenTracing.module_eval do
32
+ class << self
33
+ prepend Datadog::OpenTracer::GlobalTracer
34
+ end
35
+ end
36
+ end
37
+
38
+ load_opentracer if supported?
39
+ end
40
+ end
@@ -0,0 +1,24 @@
1
+ module Datadog
2
+ module OpenTracer
3
+ # OpenTracing propagator for Datadog::OpenTracer::Tracer
4
+ module BinaryPropagator
5
+ extend Propagator
6
+
7
+ # Inject a SpanContext into the given carrier
8
+ #
9
+ # @param span_context [SpanContext]
10
+ # @param carrier [Carrier] A carrier object of Binary type
11
+ def self.inject(span_context, carrier)
12
+ nil
13
+ end
14
+
15
+ # Extract a SpanContext in Binary format from the given carrier.
16
+ #
17
+ # @param carrier [Carrier] A carrier object of Binary type
18
+ # @return [SpanContext, nil] the extracted SpanContext or nil if none could be found
19
+ def self.extract(carrier)
20
+ SpanContext::NOOP_INSTANCE
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,6 @@
1
+ module Datadog
2
+ module OpenTracer
3
+ class Carrier < ::OpenTracing::Carrier
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,46 @@
1
+ require 'ddtrace/span'
2
+ require 'ddtrace/ext/distributed'
3
+
4
+ module Datadog
5
+ module OpenTracer
6
+ # DistributedHeaders provides easy access and validation to headers
7
+ class DistributedHeaders
8
+ include Datadog::Ext::DistributedTracing
9
+
10
+ def initialize(carrier)
11
+ @carrier = carrier
12
+ end
13
+
14
+ def valid?
15
+ # Sampling priority is optional.
16
+ !trace_id.nil? && !parent_id.nil?
17
+ end
18
+
19
+ def trace_id
20
+ id HTTP_HEADER_TRACE_ID
21
+ end
22
+
23
+ def parent_id
24
+ id HTTP_HEADER_PARENT_ID
25
+ end
26
+
27
+ def sampling_priority
28
+ hdr = @carrier[HTTP_HEADER_SAMPLING_PRIORITY]
29
+ # It's important to make a difference between no header,
30
+ # and a header defined to zero.
31
+ return unless hdr
32
+ value = hdr.to_i
33
+ return if value < 0
34
+ value
35
+ end
36
+
37
+ private
38
+
39
+ def id(header)
40
+ value = @carrier[header].to_i
41
+ return if value.zero? || value >= Datadog::Span::MAX_ID
42
+ value < 0 ? value + 0x1_0000_0000_0000_0000 : value
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,15 @@
1
+ module Datadog
2
+ module OpenTracer
3
+ # Patch for OpenTracing module
4
+ module GlobalTracer
5
+ def global_tracer=(tracer)
6
+ super.tap do
7
+ if tracer.class <= Datadog::OpenTracer::Tracer
8
+ # Update the Datadog global tracer, too.
9
+ Datadog.instance_variable_set(:@tracer, tracer.datadog_tracer)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,22 @@
1
+ module Datadog
2
+ module OpenTracer
3
+ # OpenTracing propagator for Datadog::OpenTracer::Tracer
4
+ module Propagator
5
+ # Inject a SpanContext into the given carrier
6
+ #
7
+ # @param span_context [SpanContext]
8
+ # @param carrier [Carrier] A carrier object of the type dictated by the specified `format`
9
+ def inject(span_context, carrier)
10
+ raise NotImplementedError
11
+ end
12
+
13
+ # Extract a SpanContext in the given format from the given carrier.
14
+ #
15
+ # @param carrier [Carrier] A carrier object of the type dictated by the specified `format`
16
+ # @return [SpanContext, nil] the extracted SpanContext or nil if none could be found
17
+ def extract(carrier)
18
+ raise NotImplementedError
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,60 @@
1
+ require 'ddtrace/propagation/http_propagator'
2
+
3
+ module Datadog
4
+ module OpenTracer
5
+ # OpenTracing propagator for Datadog::OpenTracer::Tracer
6
+ module RackPropagator
7
+ extend Propagator
8
+ extend Datadog::Ext::DistributedTracing
9
+ include Datadog::Ext::DistributedTracing
10
+
11
+ BAGGAGE_PREFIX = 'ot-baggage-'.freeze
12
+ BAGGAGE_PREFIX_FORMATTED = 'HTTP_OT_BAGGAGE_'.freeze
13
+
14
+ class << self
15
+ # Inject a SpanContext into the given carrier
16
+ #
17
+ # @param span_context [SpanContext]
18
+ # @param carrier [Carrier] A carrier object of Rack type
19
+ def inject(span_context, carrier)
20
+ # Inject Datadog trace properties
21
+ Datadog::HTTPPropagator.inject!(span_context.datadog_context, carrier)
22
+
23
+ # Inject baggage
24
+ span_context.baggage.each do |key, value|
25
+ carrier[BAGGAGE_PREFIX + key] = value
26
+ end
27
+
28
+ nil
29
+ end
30
+
31
+ # Extract a SpanContext in Rack format from the given carrier.
32
+ #
33
+ # @param carrier [Carrier] A carrier object of Rack type
34
+ # @return [SpanContext, nil] the extracted SpanContext or nil if none could be found
35
+ def extract(carrier)
36
+ # First extract & build a Datadog context
37
+ datadog_context = Datadog::HTTPPropagator.extract(carrier)
38
+
39
+ # Then extract any other baggage
40
+ baggage = {}
41
+ carrier.each do |key, value|
42
+ baggage[header_to_baggage(key)] = value if baggage_header?(key)
43
+ end
44
+
45
+ SpanContextFactory.build(datadog_context: datadog_context, baggage: baggage)
46
+ end
47
+
48
+ private
49
+
50
+ def baggage_header?(header)
51
+ header.to_s.start_with?(BAGGAGE_PREFIX_FORMATTED)
52
+ end
53
+
54
+ def header_to_baggage(key)
55
+ key[BAGGAGE_PREFIX_FORMATTED.length, key.length].downcase
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,15 @@
1
+ module Datadog
2
+ module OpenTracer
3
+ # OpenTracing adapter for scope
4
+ class Scope < ::OpenTracing::Scope
5
+ attr_reader \
6
+ :manager,
7
+ :span
8
+
9
+ def initialize(manager:, span:)
10
+ @manager = manager
11
+ @span = span
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,6 @@
1
+ module Datadog
2
+ module OpenTracer
3
+ class ScopeManager < ::OpenTracing::ScopeManager
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,90 @@
1
+ module Datadog
2
+ module OpenTracer
3
+ # OpenTracing adapter for Datadog::Span
4
+ class Span < ::OpenTracing::Span
5
+ attr_reader \
6
+ :datadog_span
7
+
8
+ def initialize(datadog_span:, span_context:)
9
+ @datadog_span = datadog_span
10
+ @span_context = span_context
11
+ end
12
+
13
+ # Set the name of the operation
14
+ #
15
+ # @param [String] name
16
+ def operation_name=(name)
17
+ datadog_span.name = name
18
+ end
19
+
20
+ # Span Context
21
+ #
22
+ # @return [SpanContext]
23
+ def context
24
+ @span_context
25
+ end
26
+
27
+ # Set a tag value on this span
28
+ # @param key [String] the key of the tag
29
+ # @param value [String, Numeric, Boolean] the value of the tag. If it's not
30
+ # a String, Numeric, or Boolean it will be encoded with to_s
31
+ def set_tag(key, value)
32
+ tap { datadog_span.set_tag(key, value) }
33
+ end
34
+
35
+ # Set a baggage item on the span
36
+ # @param key [String] the key of the baggage item
37
+ # @param value [String] the value of the baggage item
38
+ def set_baggage_item(key, value)
39
+ tap do
40
+ # SpanContext is immutable, so to make changes
41
+ # build a new span context.
42
+ @span_context = SpanContextFactory.clone(
43
+ span_context: context,
44
+ baggage: { key => value }
45
+ )
46
+ end
47
+ end
48
+
49
+ # Get a baggage item
50
+ # @param key [String] the key of the baggage item
51
+ # @return [String] value of the baggage item
52
+ def get_baggage_item(key)
53
+ context.baggage[key]
54
+ end
55
+
56
+ # @deprecated Use {#log_kv} instead.
57
+ # Reason: event is an optional standard log field defined in spec and not required. Also,
58
+ # method name {#log_kv} is more consistent with other language implementations such as Python and Go.
59
+ #
60
+ # Add a log entry to this span
61
+ # @param event [String] event name for the log
62
+ # @param timestamp [Time] time of the log
63
+ # @param fields [Hash] Additional information to log
64
+ def log(event: nil, timestamp: Time.now, **fields)
65
+ super # Log deprecation warning
66
+
67
+ # If the fields specify an error
68
+ if fields.key?(:'error.object')
69
+ datadog_span.set_error(fields[:'error.object'])
70
+ end
71
+ end
72
+
73
+ # Add a log entry to this span
74
+ # @param timestamp [Time] time of the log
75
+ # @param fields [Hash] Additional information to log
76
+ def log_kv(timestamp: Time.now, **fields)
77
+ # If the fields specify an error
78
+ if fields.key?(:'error.object')
79
+ datadog_span.set_error(fields[:'error.object'])
80
+ end
81
+ end
82
+
83
+ # Finish the {Span}
84
+ # @param end_time [Time] custom end time, if not now
85
+ def finish(end_time: Time.now)
86
+ datadog_span.finish(end_time)
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,14 @@
1
+ module Datadog
2
+ module OpenTracer
3
+ # OpenTracing adapter for SpanContext
4
+ class SpanContext < ::OpenTracing::SpanContext
5
+ attr_reader \
6
+ :datadog_context
7
+
8
+ def initialize(datadog_context:, baggage: {})
9
+ @datadog_context = datadog_context
10
+ @baggage = baggage.freeze
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,23 @@
1
+ module Datadog
2
+ module OpenTracer
3
+ # Creates new Datadog::OpenTracer::SpanContext
4
+ module SpanContextFactory
5
+ module_function
6
+
7
+ def build(datadog_context:, baggage: {})
8
+ SpanContext.new(
9
+ datadog_context: datadog_context,
10
+ baggage: baggage.dup
11
+ )
12
+ end
13
+
14
+ def clone(span_context:, baggage: {})
15
+ SpanContext.new(
16
+ datadog_context: span_context.datadog_context,
17
+ # Merge baggage from previous SpanContext
18
+ baggage: span_context.baggage.merge(baggage)
19
+ )
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,73 @@
1
+ require 'ddtrace/ext/distributed'
2
+
3
+ module Datadog
4
+ module OpenTracer
5
+ # OpenTracing propagator for Datadog::OpenTracer::Tracer
6
+ module TextMapPropagator
7
+ extend Propagator
8
+ extend Datadog::Ext::DistributedTracing
9
+ include Datadog::Ext::DistributedTracing
10
+
11
+ BAGGAGE_PREFIX = 'ot-baggage-'.freeze
12
+
13
+ class << self
14
+ # Inject a SpanContext into the given carrier
15
+ #
16
+ # @param span_context [SpanContext]
17
+ # @param carrier [Carrier] A carrier object of Rack type
18
+ def inject(span_context, carrier)
19
+ # Inject Datadog trace properties
20
+ span_context.datadog_context.tap do |datadog_context|
21
+ carrier[HTTP_HEADER_TRACE_ID] = datadog_context.trace_id
22
+ carrier[HTTP_HEADER_PARENT_ID] = datadog_context.span_id
23
+ carrier[HTTP_HEADER_SAMPLING_PRIORITY] = datadog_context.sampling_priority
24
+ end
25
+
26
+ # Inject baggage
27
+ span_context.baggage.each do |key, value|
28
+ carrier[BAGGAGE_PREFIX + key] = value
29
+ end
30
+
31
+ nil
32
+ end
33
+
34
+ # Extract a SpanContext in TextMap format from the given carrier.
35
+ #
36
+ # @param carrier [Carrier] A carrier object of TextMap type
37
+ # @return [SpanContext, nil] the extracted SpanContext or nil if none could be found
38
+ def extract(carrier)
39
+ # First extract & build a Datadog context
40
+ headers = DistributedHeaders.new(carrier)
41
+
42
+ datadog_context = if headers.valid?
43
+ Datadog::Context.new(
44
+ trace_id: headers.trace_id,
45
+ span_id: headers.parent_id,
46
+ sampling_priority: headers.sampling_priority
47
+ )
48
+ else
49
+ Datadog::Context.new
50
+ end
51
+
52
+ # Then extract any other baggage
53
+ baggage = {}
54
+ carrier.each do |key, value|
55
+ baggage[item_to_baggage(key)] = value if baggage_item?(key)
56
+ end
57
+
58
+ SpanContextFactory.build(datadog_context: datadog_context, baggage: baggage)
59
+ end
60
+
61
+ private
62
+
63
+ def baggage_item?(item)
64
+ item.to_s.start_with?(BAGGAGE_PREFIX)
65
+ end
66
+
67
+ def item_to_baggage(key)
68
+ key[BAGGAGE_PREFIX.length, key.length]
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,30 @@
1
+ module Datadog
2
+ module OpenTracer
3
+ # OpenTracing adapter for thread local scopes
4
+ class ThreadLocalScope < Scope
5
+ attr_reader \
6
+ :finish_on_close
7
+
8
+ def initialize(
9
+ manager:,
10
+ span:,
11
+ finish_on_close: true
12
+ )
13
+ super(manager: manager, span: span)
14
+ @finish_on_close = finish_on_close
15
+ @previous_scope = manager.active
16
+ end
17
+
18
+ # Mark the end of the active period for the current thread and Scope,
19
+ # updating the ScopeManager#active in the process.
20
+ #
21
+ # NOTE: Calling close more than once on a single Scope instance leads to
22
+ # undefined behavior.
23
+ def close
24
+ return unless equal?(manager.active)
25
+ span.finish if finish_on_close
26
+ manager.send(:set_scope, @previous_scope)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,40 @@
1
+ module Datadog
2
+ module OpenTracer
3
+ # OpenTracing adapter for thread local scope management
4
+ class ThreadLocalScopeManager < ScopeManager
5
+ # Make a span instance active.
6
+ #
7
+ # @param span [Span] the Span that should become active
8
+ # @param finish_on_close [Boolean] whether the Span should automatically be
9
+ # finished when Scope#close is called
10
+ # @return [Scope] instance to control the end of the active period for the
11
+ # Span. It is a programming error to neglect to call Scope#close on the
12
+ # returned instance.
13
+ def activate(span, finish_on_close: true)
14
+ ThreadLocalScope.new(
15
+ manager: self,
16
+ span: span,
17
+ finish_on_close: finish_on_close
18
+ ).tap do |scope|
19
+ set_scope(scope)
20
+ end
21
+ end
22
+
23
+ # @return [Scope] the currently active Scope which can be used to access the
24
+ # currently active Span.
25
+ #
26
+ # If there is a non-null Scope, its wrapped Span becomes an implicit parent
27
+ # (as Reference#CHILD_OF) of any newly-created Span at Tracer#start_active_span
28
+ # or Tracer#start_span time.
29
+ def active
30
+ Thread.current[object_id.to_s]
31
+ end
32
+
33
+ private
34
+
35
+ def set_scope(scope)
36
+ Thread.current[object_id.to_s] = scope
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,208 @@
1
+ require 'ddtrace/tracer'
2
+
3
+ module Datadog
4
+ module OpenTracer
5
+ # OpenTracing adapter for Datadog::Tracer
6
+ class Tracer < ::OpenTracing::Tracer
7
+ extend Forwardable
8
+
9
+ attr_reader \
10
+ :datadog_tracer
11
+
12
+ def_delegators \
13
+ :datadog_tracer,
14
+ :configure
15
+
16
+ def initialize(options = {})
17
+ super()
18
+ @datadog_tracer = Datadog::Tracer.new(options)
19
+ end
20
+
21
+ # @return [ScopeManager] the current ScopeManager.
22
+ def scope_manager
23
+ @scope_manager ||= ThreadLocalScopeManager.new
24
+ end
25
+
26
+ # Returns a newly started and activated Scope.
27
+ #
28
+ # If the Tracer's ScopeManager#active is not nil, no explicit references
29
+ # are provided, and `ignore_active_scope` is false, then an inferred
30
+ # References#CHILD_OF reference is created to the ScopeManager#active's
31
+ # SpanContext when start_active is invoked.
32
+ #
33
+ # @param operation_name [String] The operation name for the Span
34
+ # @param child_of [SpanContext, Span] SpanContext that acts as a parent to
35
+ # the newly-started Span. If a Span instance is provided, its
36
+ # context is automatically substituted. See [Reference] for more
37
+ # information.
38
+ #
39
+ # If specified, the `references` parameter must be omitted.
40
+ # @param references [Array<Reference>] An array of reference
41
+ # objects that identify one or more parent SpanContexts.
42
+ # @param start_time [Time] When the Span started, if not now
43
+ # @param tags [Hash] Tags to assign to the Span at start time
44
+ # @param ignore_active_scope [Boolean] whether to create an implicit
45
+ # References#CHILD_OF reference to the ScopeManager#active.
46
+ # @param finish_on_close [Boolean] whether span should automatically be
47
+ # finished when Scope#close is called
48
+ # @yield [Scope] If an optional block is passed to start_active it will
49
+ # yield the newly-started Scope. If `finish_on_close` is true then the
50
+ # Span will be finished automatically after the block is executed.
51
+ # @return [Scope] The newly-started and activated Scope
52
+ def start_active_span(operation_name,
53
+ child_of: nil,
54
+ references: nil,
55
+ start_time: Time.now,
56
+ tags: nil,
57
+ ignore_active_scope: false,
58
+ finish_on_close: true)
59
+
60
+ # When meant to automatically determine the parent,
61
+ # Use the active scope first, otherwise fall back to any
62
+ # context generated by Datadog, so as to append to it and gain
63
+ # the benefit of any out-of-the-box tracing from Datadog preceding
64
+ # the OpenTracer::Tracer.
65
+ #
66
+ # We do this here instead of in #start_span because #start_span generates
67
+ # spans that are not assigned to a scope, a.k.a not supposed to be used by
68
+ # subsequent spans implicitly. By using the existing Datadog context, the span
69
+ # effectively ends up "assigned to a scope", by virtue of being added to the
70
+ # Context. Hence, it would behave more like an active span, which is why it
71
+ # should only be here.
72
+ unless child_of || ignore_active_scope
73
+ child_of = if scope_manager.active
74
+ scope_manager.active.span.context
75
+ else
76
+ SpanContextFactory.build(datadog_context: datadog_tracer.call_context)
77
+ end
78
+ end
79
+
80
+ # Create the span, and auto-add it to the Datadog context.
81
+ span = start_span(
82
+ operation_name,
83
+ child_of: child_of,
84
+ references: references,
85
+ start_time: start_time,
86
+ tags: tags,
87
+ ignore_active_scope: ignore_active_scope
88
+ )
89
+
90
+ # Overwrite the tracer context with the OpenTracing managed context.
91
+ # This is mostly for the benefit of any out-of-the-box tracing from Datadog,
92
+ # such that spans generated by that tracing will be attached to the OpenTracer
93
+ # parent span.
94
+ datadog_tracer.provider.context = span.datadog_span.context
95
+
96
+ scope_manager.activate(span, finish_on_close: finish_on_close).tap do |scope|
97
+ if block_given?
98
+ begin
99
+ yield(scope)
100
+ ensure
101
+ scope.close
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ # Like #start_active_span, but the returned Span has not been registered via the
108
+ # ScopeManager.
109
+ #
110
+ # @param operation_name [String] The operation name for the Span
111
+ # @param child_of [SpanContext, Span] SpanContext that acts as a parent to
112
+ # the newly-started Span. If a Span instance is provided, its
113
+ # context is automatically substituted. See [Reference] for more
114
+ # information.
115
+ #
116
+ # If specified, the `references` parameter must be omitted.
117
+ # @param references [Array<Reference>] An array of reference
118
+ # objects that identify one or more parent SpanContexts.
119
+ # @param start_time [Time] When the Span started, if not now
120
+ # @param tags [Hash] Tags to assign to the Span at start time
121
+ # @param ignore_active_scope [Boolean] whether to create an implicit
122
+ # References#CHILD_OF reference to the ScopeManager#active.
123
+ # @return [Span] the newly-started Span instance, which has not been
124
+ # automatically registered via the ScopeManager
125
+ def start_span(operation_name,
126
+ child_of: nil,
127
+ references: nil,
128
+ start_time: Time.now,
129
+ tags: nil,
130
+ ignore_active_scope: false)
131
+
132
+ # Derive the OpenTracer::SpanContext to inherit from.
133
+ parent_span_context = inherited_span_context(child_of, ignore_active_scope: ignore_active_scope)
134
+
135
+ # Retrieve Datadog::Context from parent SpanContext.
136
+ datadog_context = parent_span_context.nil? ? nil : parent_span_context.datadog_context
137
+
138
+ # Build the new Datadog span
139
+ datadog_span = datadog_tracer.start_span(
140
+ operation_name,
141
+ child_of: datadog_context,
142
+ start_time: start_time,
143
+ tags: tags || {}
144
+ )
145
+
146
+ # Build or extend the OpenTracer::SpanContext
147
+ span_context = if parent_span_context
148
+ SpanContextFactory.clone(span_context: parent_span_context)
149
+ else
150
+ SpanContextFactory.build(datadog_context: datadog_span.context)
151
+ end
152
+
153
+ # Wrap the Datadog span and OpenTracer::Span context in a OpenTracer::Span
154
+ Span.new(datadog_span: datadog_span, span_context: span_context)
155
+ end
156
+
157
+ # Inject a SpanContext into the given carrier
158
+ #
159
+ # @param span_context [SpanContext]
160
+ # @param format [OpenTracing::FORMAT_TEXT_MAP, OpenTracing::FORMAT_BINARY, OpenTracing::FORMAT_RACK]
161
+ # @param carrier [Carrier] A carrier object of the type dictated by the specified `format`
162
+ def inject(span_context, format, carrier)
163
+ case format
164
+ when OpenTracing::FORMAT_TEXT_MAP
165
+ TextMapPropagator.inject(span_context, carrier)
166
+ when OpenTracing::FORMAT_BINARY
167
+ BinaryPropagator.inject(span_context, carrier)
168
+ when OpenTracing::FORMAT_RACK
169
+ RackPropagator.inject(span_context, carrier)
170
+ else
171
+ warn 'Unknown inject format'
172
+ end
173
+ end
174
+
175
+ # Extract a SpanContext in the given format from the given carrier.
176
+ #
177
+ # @param format [OpenTracing::FORMAT_TEXT_MAP, OpenTracing::FORMAT_BINARY, OpenTracing::FORMAT_RACK]
178
+ # @param carrier [Carrier] A carrier object of the type dictated by the specified `format`
179
+ # @return [SpanContext, nil] the extracted SpanContext or nil if none could be found
180
+ def extract(format, carrier)
181
+ case format
182
+ when OpenTracing::FORMAT_TEXT_MAP
183
+ TextMapPropagator.extract(carrier)
184
+ when OpenTracing::FORMAT_BINARY
185
+ BinaryPropagator.extract(carrier)
186
+ when OpenTracing::FORMAT_RACK
187
+ RackPropagator.extract(carrier)
188
+ else
189
+ warn 'Unknown extract format'
190
+ nil
191
+ end
192
+ end
193
+
194
+ private
195
+
196
+ def inherited_span_context(parent, ignore_active_scope: false)
197
+ case parent
198
+ when Span
199
+ parent.context
200
+ when SpanContext
201
+ parent
202
+ else
203
+ ignore_active_scope ? nil : scope_manager.active && scope_manager.active.span.context
204
+ end
205
+ end
206
+ end
207
+ end
208
+ end
@@ -1,7 +1,7 @@
1
1
  module Datadog
2
2
  module VERSION
3
3
  MAJOR = 0
4
- MINOR = 15
4
+ MINOR = 16
5
5
  PATCH = 0
6
6
  PRE = nil
7
7
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ddtrace
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.0
4
+ version: 0.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Datadog, Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-09-12 00:00:00.000000000 Z
11
+ date: 2018-09-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: opentracing
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.4.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 0.4.1
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: rake
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -400,6 +414,22 @@ files:
400
414
  - lib/ddtrace/ext/sql.rb
401
415
  - lib/ddtrace/logger.rb
402
416
  - lib/ddtrace/monkey.rb
417
+ - lib/ddtrace/opentracer.rb
418
+ - lib/ddtrace/opentracer/binary_propagator.rb
419
+ - lib/ddtrace/opentracer/carrier.rb
420
+ - lib/ddtrace/opentracer/distributed_headers.rb
421
+ - lib/ddtrace/opentracer/global_tracer.rb
422
+ - lib/ddtrace/opentracer/propagator.rb
423
+ - lib/ddtrace/opentracer/rack_propagator.rb
424
+ - lib/ddtrace/opentracer/scope.rb
425
+ - lib/ddtrace/opentracer/scope_manager.rb
426
+ - lib/ddtrace/opentracer/span.rb
427
+ - lib/ddtrace/opentracer/span_context.rb
428
+ - lib/ddtrace/opentracer/span_context_factory.rb
429
+ - lib/ddtrace/opentracer/text_map_propagator.rb
430
+ - lib/ddtrace/opentracer/thread_local_scope.rb
431
+ - lib/ddtrace/opentracer/thread_local_scope_manager.rb
432
+ - lib/ddtrace/opentracer/tracer.rb
403
433
  - lib/ddtrace/patcher.rb
404
434
  - lib/ddtrace/pin.rb
405
435
  - lib/ddtrace/pipeline.rb