ddtrace 0.15.0 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
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