ddtrace 0.23.3 → 0.24.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
  SHA1:
3
- metadata.gz: 58499947435a908a1815f048025f181f4651c3ad
4
- data.tar.gz: 615ab8677d1463ceacdd0e7fa75c7015eb0f3068
3
+ metadata.gz: 634a3b4b1798b771abe9af49ee6ac02c2b64a4bc
4
+ data.tar.gz: 8854e2a87b04f4aeaf426eb8320ce9f728565dd0
5
5
  SHA512:
6
- metadata.gz: 26db8434fb7515336f1db628f76e9e6e04f0a8f6fa1c74a5e1206e8045c6e4b058805cbd3526f41e7f1ce7076122b3ae601f37a52db81b0c556f04be48240922
7
- data.tar.gz: c4ef32d8dbf2aac5629fce033d22acc08808c4ffffa907de0e4c534bce8c2fbbe6dea7790f49cf92e480e31263f3964f534eea25ca49303336b03c94eb79956f
6
+ metadata.gz: a816feb5c86306091e2bf300dc2219f9a6c60cb08e41643bfba4f8a4a5832623c2677eedf48778d40a4557b31fe53dc456f8793f02035cca7dc2115fae909e5c
7
+ data.tar.gz: 203406fe03985bfda50aeb2e886251b74672f066cb43765bbd06450ab6c86d5a98493691876f3cfb8fc1ca69a426276ae770851e39b96a122c2060c2c5ad938e
@@ -4,6 +4,18 @@
4
4
 
5
5
  ## [Unreleased (beta)]
6
6
 
7
+ ## [0.24.0] - 2019-05-21
8
+
9
+ Release notes: https://github.com/DataDog/dd-trace-rb/releases/tag/v0.24.0
10
+
11
+ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.23.3...v0.24.0
12
+
13
+ ### Added
14
+
15
+ - B3 header support (#753)
16
+ - Hostname tagging option (#760)
17
+ - Contribution and development guides (#754)
18
+
7
19
  ## [0.23.3] - 2019-05-16
8
20
 
9
21
  Release notes: https://github.com/DataDog/dd-trace-rb/releases/tag/v0.23.3
@@ -0,0 +1,85 @@
1
+ # Contributing
2
+
3
+ Community contributions to the Datadog tracing library for Ruby are welcome! See below for some basic guidelines.
4
+
5
+ ## Want to request a new feature?
6
+
7
+ Many great ideas for new features come from the community, and we'd be happy to consider yours!
8
+
9
+ To share your request, you can [open a Github issue](https://github.com/DataDog/dd-trace-rb/issues/new) with the details about what you'd like to see. At a minimum, please provide:
10
+
11
+ - The goal of the new feature
12
+ - A description of how it might be used or behave
13
+ - Links to any important resources (e.g. Github repos, websites, screenshots, specifications, diagrams)
14
+
15
+ Additionally, if you can, include:
16
+
17
+ - A description of how it could be accomplished
18
+ - Code snippets that might demonstrate its use or implementation
19
+ - Screenshots or mockups that visually demonstrate the feature
20
+ - Links to similar features that would serve as a good comparison
21
+ - (Any other details that would be useful for implementing this feature!)
22
+
23
+ Feature requests will be reviewed, discussed, and then added to [our Community project](https://github.com/DataDog/dd-trace-rb/projects/2).
24
+
25
+ ## Found a bug?
26
+
27
+ For any urgent matters (such as outages) or issues concerning the Datadog service or UI, contact our support team via https://docs.datadoghq.com/help/ for direct, faster assistance.
28
+
29
+ You may submit bug reports concerning the Datadog tracing library for Ruby by [opening a Github issue](https://github.com/DataDog/dd-trace-rb/issues/new). At a minimum, please provide:
30
+
31
+ - A description of the problem
32
+ - Steps to reproduce
33
+ - Expected behavior
34
+ - Actual behavior
35
+ - Errors (with stack traces) or warnings received
36
+ - Any details you can share about your configuration including:
37
+ - Ruby version & platform
38
+ - `ddtrace` version
39
+ - Versions of any other relevant gems (or a `Gemfile.lock` if available)
40
+ - Any configuration settings for the trace library (e.g. `initializers/datadog.rb`)
41
+
42
+ If at all possible, also provide:
43
+
44
+ - Logs (from the tracer/application/agent) or other diagnostics
45
+ - Screenshots, links, or other visual aids that are publicly accessible
46
+ - Code sample or test that reproduces the problem
47
+ - An explanation of what causes the bug and/or how it can be fixed
48
+
49
+ Reports that include rich detail are better, and ones with code that reproduce the bug are best. Bug requests are triaged, reviewed, and added to [our Community project](https://github.com/DataDog/dd-trace-rb/projects/2).
50
+
51
+ ## Have a patch?
52
+
53
+ We welcome code contributions to the library, which you can [submit as a pull request](https://github.com/DataDog/dd-trace-rb/pull/new/master). To create a pull request:
54
+
55
+ 1. **Fork the repository** from https://github.com/DataDog/dd-trace-rb
56
+ 2. **Make any changes** for your patch.
57
+ 3. **Write tests** that demonstrate how the feature works or how the bug is fixed.
58
+ 4. **Update any documentation** such as `docs/GettingStarted.md`, especially for new features.
59
+ 5. **Submit the pull request** from your fork back to https://github.com/DataDog/dd-trace-rb. Bugs should merge back to `master`, and new features should merge back to the latest `dev` branch (e.g. `0.24-dev`).
60
+
61
+ The pull request will be run through our CI pipeline, and a project member will review the changes with you. At a minimum, to be accepted and merged, pull requests must:
62
+
63
+ - Have a stated goal and detailed description of the changes made
64
+ - Include thorough test coverage and documentation, where applicable
65
+ - Pass all tests and code quality checks (linting/coverage/benchmarks) on CI
66
+ - Receive at least one approval from a project member with push permissions
67
+
68
+ We also recommend that you share in your description:
69
+
70
+ - Any motivations or intent for the contribution
71
+ - Links to any issues/pull requests it might be related to
72
+ - Links to any webpages or other external resources that might be related to the change
73
+ - Screenshots, code samples, or other visual aids that demonstrate the changes or how they are implemented
74
+ - Benchmarks if the feature is anticipated to have performance implications
75
+ - Any limitations, constraints or risks that are important to consider
76
+
77
+ Pull requests will be reviewed and added to [our Community project](https://github.com/DataDog/dd-trace-rb/projects/2).
78
+
79
+ For more information on common topics such as debugging locally, or how to write new integrations, check out [our development guide](https://github.com/DataDog/dd-trace-rb/docs/DevelopmentGuide.md). If at any point you have a question or need assistance with your pull request, feel free to mention a project member! We're always happy to help contributors with their pull requests.
80
+
81
+ ## Final word
82
+
83
+ Many thanks to all of our contributors, and looking forward to seeing you on Github! :tada:
84
+
85
+ - Datadog Ruby APM Team
data/README.md CHANGED
@@ -13,41 +13,11 @@ For installation, configuration, and details about using the API, check out our
13
13
 
14
14
  For descriptions of terminology used in APM, take a look at the [official documentation][visualization docs].
15
15
 
16
+ For contributing, checkout the [contribution guidelines][contribution docs] and [development guide][development docs].
17
+
16
18
  [setup docs]: https://docs.datadoghq.com/tracing/setup/ruby/
17
19
  [api docs]: https://github.com/DataDog/dd-trace-rb/blob/master/docs/GettingStarted.md
18
20
  [gem docs]: http://gems.datadoghq.com/trace/docs/
19
21
  [visualization docs]: https://docs.datadoghq.com/tracing/visualization/
20
-
21
- ## Development
22
-
23
- ### Testing
24
-
25
- Configure your environment through:
26
-
27
- $ bundle install
28
- $ appraisal install
29
-
30
- You can launch tests using the following Rake commands:
31
-
32
- $ rake test:main # tracer tests
33
- $ appraisal rails<version>-<database> rake test:rails # tests Rails matrix
34
- $ appraisal contrib rake test:redis # tests Redis integration
35
- ...
36
-
37
- Run ``rake --tasks`` for the list of available Rake tasks.
38
- Run ``appraisal list`` for the list of available appraisals.
39
-
40
- The test suite requires many backing services (PostgreSQL, MySQL, Redis, ...) and we're using
41
- ``docker`` and ``docker-compose`` to start these services in the CI.
42
- To launch properly the test matrix, please [install docker][2] and [docker-compose][3] using
43
- the instructions provided by your platform. Then launch them through:
44
-
45
- $ docker-compose up -d
46
-
47
- We also enforce the Ruby [community-driven style guide][1] through Rubocop. Simply launch:
48
-
49
- $ rake rubocop
50
-
51
- [1]: https://github.com/bbatsov/ruby-style-guide
52
- [2]: https://www.docker.com/products/docker
53
- [3]: https://www.docker.com/products/docker-compose
22
+ [contribution docs]: https://github.com/DataDog/dd-trace-rb/blob/master/CONTRIBUTING.md
23
+ [development docs]: https://github.com/DataDog/dd-trace-rb/blob/master/docs/DevelopmentGuide.md
@@ -0,0 +1,132 @@
1
+ # Developing
2
+
3
+ This guide covers some of the common how-tos and technical reference material for developing changes within the trace library.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Setting up](#setting-up)
8
+ - [Testing](#testing)
9
+ - [Writing tests](#writing-tests)
10
+ - [Running tests](#running-tests)
11
+ - [Checking code quality](#checking-code-quality)
12
+ - [Appendix](#appendix)
13
+ - [Writing new integrations](#writing-new-integrations)
14
+
15
+ ## Setting up
16
+
17
+ *NOTE: To test locally, you must have `Docker` and `Docker Compose` installed. See the [Docker documentation](https://docs.docker.com/compose/install/) for details.*
18
+
19
+ The trace library uses Docker Compose to create a Ruby environment to develop and test within, as well as containers for any dependencies that might be necessary for certain kinds of tests.
20
+
21
+ To start a development environment, choose a target Ruby version (e.g. between `1.9` and `2.4`) then run the following:
22
+
23
+ ```
24
+ # In the root directory of the project...
25
+ cd ~/dd-trace-rb
26
+
27
+ # Create and start a Ruby 2.3 test environment with its dependencies
28
+ docker-compose run --rm tracer-2.3 /bin/bash
29
+
30
+ # Then inside the container (e.g. `root@2a73c6d8673e:/app`)...
31
+ # Install the library dependencies
32
+ bundle install
33
+
34
+ # Install build targets
35
+ appraisal install
36
+ ```
37
+
38
+ Then within this container you can [run tests](#running-tests), or [run code quality checks](#checking-code-quality).
39
+
40
+ ## Testing
41
+
42
+ The test suite uses both [Minitest](https://github.com/seattlerb/minitest) and [RSpec](https://rspec.info/) tests to verify the correctness of both the core trace library and its integrations.
43
+
44
+ Minitest is deprecated in favor of RSpec; all new tests should be written in RSpec, and only existing minitests should be updated.
45
+
46
+ ### Writing tests
47
+
48
+ New tests should be written as RSpec tests in the `spec/ddtrace` folder. Test files should generally mirror the structure of `lib`.
49
+
50
+ All changes should be covered by a corresponding RSpec tests. Unit tests are preferred, and integration tests are accepted where appropriate (e.g. acceptance tests, verifying compatibility with datastores, etc) but should be kept to a minimum.
51
+
52
+ **Considerations for CI**
53
+
54
+ All tests should run in CI. When adding new `spec.rb` files, you may need to add a test task to ensure your test file is run in CI.
55
+
56
+ - Ensure that there is a corresponding Rake task defined in `Rakefile` under the the `spec` namespace, whose pattern matches your test file.
57
+ - Verify the Rake task is configured to run for the appropriate Ruby runtimes in the `ci` Rake task.
58
+
59
+ ### Running tests
60
+
61
+ Simplest way to run tests is to run `bundle exec rake ci`, which will run the entire test suite, just as CI does.
62
+
63
+ **For the core library**
64
+
65
+ Run the tests for the core library with:
66
+
67
+ ```
68
+ # Run Minitest
69
+ $ bundle exec rake test:main
70
+ # Run RSpec
71
+ $ bundle exec rake spec:main
72
+ ```
73
+
74
+ **For integrations**
75
+
76
+ Integrations which interact with dependencies not listed in the `ddtrace` gemspec will need to load these dependencies to run their tests.
77
+
78
+ To do so, load the dependencies using [Appraisal](https://github.com/thoughtbot/appraisal). You can see a list of available appraisals with `bundle exec appraisal list`, or examine the `Appraisals` file.
79
+
80
+ Then to run tests, prefix the test commain with the appraisal. For example:
81
+
82
+ ```
83
+ # Runs tests for Rails 3.2 + Postgres
84
+ $ bundle exec appraisal rails32-postgres spec:rails
85
+ # Runs tests for Redis
86
+ $ bundle exec appraisal contrib rake spec:redis
87
+ ```
88
+
89
+ **Passing arguments to tests**
90
+
91
+ When running RSpec tests, you may pass additional args as parameters to the Rake task. For example:
92
+
93
+ ```
94
+ # Runs Redis tests with seed 1234
95
+ $ bundle exec appraisal contrib rake spec:redis'[--seed,1234]'
96
+ ```
97
+
98
+ This can be useful for replicating conditions from CI or isolating certain tests.
99
+
100
+ ### Checking code quality
101
+
102
+ **Linting**
103
+
104
+ The trace library uses Rubocop to enforce [code style](https://github.com/bbatsov/ruby-style-guide) and quality. To check, run:
105
+
106
+ ```
107
+ $ bundle exec rake rubocop
108
+ ```
109
+
110
+ ## Appendix
111
+
112
+ ### Writing new integrations
113
+
114
+ Integrations are extensions to the trace library that add support for external dependencies (gems); they typically add auto-instrumentation to popular gems and frameworks. You will find many of our integrations in the `contrib` folder.
115
+
116
+ Some general guidelines for adding new integrations:
117
+
118
+ - An integration can either be added directly to `dd-trace-rb`, or developed as its own gem that depends on `ddtrace`.
119
+ - Integrations should implement the configuration API for easy, consistent implementation. (See existing integrations as examples of this.)
120
+ - All new integrations require documentation, unit/integration tests written in RSpec, and passing CI builds.
121
+ - It's highly encouraged to share screenshots or other demos of how the new integration looks and works.
122
+
123
+ To get started quickly, it's perfectly fine to copy-paste an existing integration to use as a template, then modify it to match your needs. This is usually the fastest, easiest way to bootstrap a new integration and makes the time-to-first-trace often very quick, usually less than an hour if it's a simple implementation.
124
+
125
+ Once you have it working in your application, you can [add unit tests](#writing-tests), [run them locally](#running-tests), and [check for code quality](#checking-code-quality) using Docker Compose.
126
+
127
+ Then [open a pull request](https://github.com/DataDog/dd-trace-rb/CONTRIBUTING.md#have-a-patch) and be sure to add the following to the description:
128
+
129
+ - [Documentation](https://github.com/DataDog/dd-trace-rb/docs/GettingStarted.md) for the integration, including versions supported.
130
+ - Links to the repository/website of the library being integrated
131
+ - Screenshots showing a sample trace
132
+ - Any additional code snippets, sample apps, benchmarks, or other resources that demonstrate its implementation are a huge plus!
@@ -11,9 +11,13 @@ For details about contributing, check out the [development guide][development do
11
11
 
12
12
  For descriptions of terminology used in APM, take a look at the [official documentation][visualization docs].
13
13
 
14
+ For contributing, checkout the [contribution guidelines][contribution docs] and [development guide][development docs].
15
+
14
16
  [setup docs]: https://docs.datadoghq.com/tracing/setup/ruby/
15
17
  [development docs]: https://github.com/DataDog/dd-trace-rb/blob/master/README.md#development
16
18
  [visualization docs]: https://docs.datadoghq.com/tracing/visualization/
19
+ [contribution docs]: https://github.com/DataDog/dd-trace-rb/blob/master/CONTRIBUTING.md
20
+ [development docs]: https://github.com/DataDog/dd-trace-rb/blob/master/docs/DevelopmentGuide.md
17
21
 
18
22
  ## Table of Contents
19
23
 
@@ -1,4 +1,5 @@
1
1
  require 'ddtrace/ext/analytics'
2
+ require 'ddtrace/ext/distributed'
2
3
  require 'ddtrace/ext/runtime'
3
4
  require 'ddtrace/configuration/options'
4
5
 
@@ -17,10 +18,32 @@ module Datadog
17
18
  default: -> { env_to_bool(Ext::Analytics::ENV_TRACE_ANALYTICS_ENABLED, nil) },
18
19
  lazy: true
19
20
 
21
+ option :report_hostname,
22
+ default: -> { env_to_bool(Ext::NET::ENV_REPORT_HOSTNAME, false) },
23
+ lazy: true
24
+
20
25
  option :runtime_metrics_enabled,
21
26
  default: -> { env_to_bool(Ext::Runtime::Metrics::ENV_ENABLED, false) },
22
27
  lazy: true
23
28
 
29
+ # Look for all headers by default
30
+ option :propagation_extract_style,
31
+ default: lambda {
32
+ env_to_list(Ext::DistributedTracing::PROPAGATION_EXTRACT_STYLE_ENV,
33
+ [Ext::DistributedTracing::PROPAGATION_STYLE_DATADOG,
34
+ Ext::DistributedTracing::PROPAGATION_STYLE_B3,
35
+ Ext::DistributedTracing::PROPAGATION_STYLE_B3_SINGLE_HEADER])
36
+ },
37
+ lazy: true
38
+
39
+ # Only inject Datadog headers by default
40
+ option :propagation_inject_style,
41
+ default: lambda {
42
+ env_to_list(Ext::DistributedTracing::PROPAGATION_INJECT_STYLE_ENV,
43
+ [Ext::DistributedTracing::PROPAGATION_STYLE_DATADOG])
44
+ },
45
+ lazy: true
46
+
24
47
  option :tracer, default: Tracer.new
25
48
 
26
49
  def initialize(options = {})
@@ -36,6 +59,12 @@ module Datadog
36
59
  yield(self) if block_given?
37
60
  end
38
61
 
62
+ def distributed_tracing
63
+ # TODO: Move distributed tracing configuration to it's own Settings sub-class
64
+ # DEV: We do this to fake `Datadog.configuration.distributed_tracing.propagation_inject_style`
65
+ self
66
+ end
67
+
39
68
  def runtime_metrics(options = nil)
40
69
  runtime_metrics = get_option(:tracer).writer.runtime_metrics
41
70
  return runtime_metrics if options.nil?
@@ -0,0 +1,44 @@
1
+ require 'ddtrace/ext/distributed'
2
+ require 'ddtrace/distributed_tracing/headers/headers'
3
+ require 'ddtrace/distributed_tracing/headers/helpers'
4
+
5
+ module Datadog
6
+ module DistributedTracing
7
+ module Headers
8
+ # B3 provides helpers to inject or extract headers for B3 style headers
9
+ module B3
10
+ include Ext::DistributedTracing
11
+
12
+ def self.inject!(context, env)
13
+ return if context.nil?
14
+
15
+ # DEV: We need these to be hex encoded
16
+ env[B3_HEADER_TRACE_ID] = context.trace_id.to_s(16)
17
+ env[B3_HEADER_SPAN_ID] = context.span_id.to_s(16)
18
+
19
+ unless context.sampling_priority.nil?
20
+ sampling_priority = DistributedTracing::Headers::Helpers.clamp_sampling_priority(context.sampling_priority)
21
+ env[B3_HEADER_SAMPLED] = sampling_priority.to_s
22
+ end
23
+ end
24
+
25
+ def self.extract(env)
26
+ # Extract values from headers
27
+ # DEV: B3 doesn't have "origin"
28
+ headers = Headers.new(env)
29
+ trace_id = headers.id(B3_HEADER_TRACE_ID, 16)
30
+ span_id = headers.id(B3_HEADER_SPAN_ID, 16)
31
+ # We don't need to try and convert sampled since B3 supports 0/1 (AUTO_REJECT/AUTO_KEEP)
32
+ sampling_priority = headers.number(B3_HEADER_SAMPLED)
33
+
34
+ # Return early if this propagation is not valid
35
+ return unless trace_id && span_id
36
+
37
+ ::Datadog::Context.new(trace_id: trace_id,
38
+ span_id: span_id,
39
+ sampling_priority: sampling_priority)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,56 @@
1
+ require 'ddtrace/ext/distributed'
2
+ require 'ddtrace/distributed_tracing/headers/headers'
3
+ require 'ddtrace/distributed_tracing/headers/helpers'
4
+
5
+ module Datadog
6
+ module DistributedTracing
7
+ module Headers
8
+ # B3Single provides helpers to inject or extract headers for B3 single header style headers
9
+ module B3Single
10
+ include Ext::DistributedTracing
11
+
12
+ def self.inject!(context, env)
13
+ return if context.nil?
14
+
15
+ # Header format:
16
+ # b3: {TraceId}-{SpanId}-{SamplingState}-{ParentSpanId}
17
+ # https://github.com/apache/incubator-zipkin-b3-propagation/tree/7c6e9f14d6627832bd80baa87ac7dabee7be23cf#single-header
18
+ # DEV: `{SamplingState}` and `{ParentSpanId`}` are optional
19
+
20
+ # DEV: We need these to be hex encoded
21
+ header = "#{context.trace_id.to_s(16)}-#{context.span_id.to_s(16)}"
22
+
23
+ unless context.sampling_priority.nil?
24
+ sampling_priority = DistributedTracing::Headers::Helpers.clamp_sampling_priority(context.sampling_priority)
25
+ header += "-#{sampling_priority}"
26
+ end
27
+
28
+ env[B3_HEADER_SINGLE] = header
29
+ end
30
+
31
+ def self.extract(env)
32
+ # Header format:
33
+ # b3: {TraceId}-{SpanId}-{SamplingState}-{ParentSpanId}
34
+ # https://github.com/apache/incubator-zipkin-b3-propagation/tree/7c6e9f14d6627832bd80baa87ac7dabee7be23cf#single-header
35
+ # DEV: `{SamplingState}` and `{ParentSpanId`}` are optional
36
+
37
+ headers = Headers.new(env)
38
+ value = headers.header(B3_HEADER_SINGLE)
39
+ return if value.nil?
40
+
41
+ parts = value.split('-')
42
+ trace_id = headers.value_to_id(parts[0], 16) unless parts.empty?
43
+ span_id = headers.value_to_id(parts[1], 16) if parts.length > 1
44
+ sampling_priority = headers.value_to_number(parts[2]) if parts.length > 2
45
+
46
+ # Return early if this propagation is not valid
47
+ return unless trace_id && span_id
48
+
49
+ ::Datadog::Context.new(trace_id: trace_id,
50
+ span_id: span_id,
51
+ sampling_priority: sampling_priority)
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,42 @@
1
+ require 'ddtrace/ext/distributed'
2
+ require 'ddtrace/distributed_tracing/headers/headers'
3
+
4
+ module Datadog
5
+ module DistributedTracing
6
+ module Headers
7
+ # Datadog provides helpers to inject or extract headers for Datadog style headers
8
+ module Datadog
9
+ include Ext::DistributedTracing
10
+
11
+ def self.inject!(context, env)
12
+ return if context.nil?
13
+
14
+ env[HTTP_HEADER_TRACE_ID] = context.trace_id.to_s
15
+ env[HTTP_HEADER_PARENT_ID] = context.span_id.to_s
16
+ env[HTTP_HEADER_SAMPLING_PRIORITY] = context.sampling_priority.to_s unless context.sampling_priority.nil?
17
+ env[HTTP_HEADER_ORIGIN] = context.origin.to_s unless context.origin.nil?
18
+ end
19
+
20
+ def self.extract(env)
21
+ # Extract values from headers
22
+ headers = Headers.new(env)
23
+ trace_id = headers.id(HTTP_HEADER_TRACE_ID)
24
+ parent_id = headers.id(HTTP_HEADER_PARENT_ID)
25
+ origin = headers.header(HTTP_HEADER_ORIGIN)
26
+ sampling_priority = headers.number(HTTP_HEADER_SAMPLING_PRIORITY)
27
+
28
+ # Return early if this propagation is not valid
29
+ # DEV: To be valid we need to have a trace id and a parent id or when it is a synthetics trace, just the trace id
30
+ # DEV: `DistributedHeaders#id` will not return 0
31
+ return unless (trace_id && parent_id) || (origin == 'synthetics' && trace_id)
32
+
33
+ # Return new context
34
+ ::Datadog::Context.new(trace_id: trace_id,
35
+ span_id: parent_id,
36
+ origin: origin,
37
+ sampling_priority: sampling_priority)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,68 @@
1
+ require 'ddtrace/configuration'
2
+ require 'ddtrace/span'
3
+ require 'ddtrace/ext/distributed'
4
+
5
+ module Datadog
6
+ module DistributedTracing
7
+ module Headers
8
+ # Headers provides easy access and validation methods for Rack headers
9
+ class Headers
10
+ include Ext::DistributedTracing
11
+
12
+ def initialize(env)
13
+ @env = env
14
+ end
15
+
16
+ def header(name)
17
+ rack_header = "http-#{name}".upcase!.tr('-', '_')
18
+
19
+ hdr = @env[rack_header]
20
+
21
+ # Only return the value if it is not an empty string
22
+ hdr if hdr != ''
23
+ end
24
+
25
+ def id(hdr, base = 10)
26
+ value_to_id(header(hdr), base)
27
+ end
28
+
29
+ def value_to_id(value, base = 10)
30
+ id = value_to_number(value, base)
31
+
32
+ # Return early if we could not parse a number
33
+ return if id.nil?
34
+
35
+ # Zero or greater than max allowed value of 2**64
36
+ return if id.zero? || id > Span::EXTERNAL_MAX_ID
37
+ id < 0 ? id + (2**64) : id
38
+ end
39
+
40
+ def number(hdr, base = 10)
41
+ value_to_number(header(hdr), base)
42
+ end
43
+
44
+ def value_to_number(value, base = 10)
45
+ # It's important to make a difference between no header,
46
+ # and a header defined to zero.
47
+ return if value.nil?
48
+
49
+ # Be sure we have a string
50
+ value = value.to_s
51
+
52
+ # If we are parsing base16 number then truncate to 64-bit
53
+ value = DistributedTracing::Headers::Helpers.truncate_base16_number(value) if base == 16
54
+
55
+ # Convert header to an integer
56
+ # DEV: Ruby `.to_i` will return `0` if a number could not be parsed
57
+ num = value.to_i(base)
58
+
59
+ # Ensure the parsed number is the same as the original string value
60
+ # e.g. We want to make sure to throw away `'nan'.to_i == 0`
61
+ return unless num.to_s(base) == value
62
+
63
+ num
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,45 @@
1
+ require 'ddtrace/configuration'
2
+ require 'ddtrace/span'
3
+ require 'ddtrace/ext/priority'
4
+
5
+ module Datadog
6
+ module DistributedTracing
7
+ module Headers
8
+ # Helpers module provides common helper functions for distributed tracing headers
9
+ module Helpers
10
+ # Base provides common methods for distributed header helper classes
11
+ def self.clamp_sampling_priority(sampling_priority)
12
+ # B3 doesn't have our -1 (USER_REJECT) and 2 (USER_KEEP) priorities so convert to acceptable 0/1
13
+ if sampling_priority < 0
14
+ sampling_priority = Ext::Priority::AUTO_REJECT
15
+ elsif sampling_priority > 1
16
+ sampling_priority = Ext::Priority::AUTO_KEEP
17
+ end
18
+
19
+ sampling_priority
20
+ end
21
+
22
+ def self.truncate_base16_number(value)
23
+ # Lowercase if we want to parse base16 e.g. 3E8 => 3e8
24
+ # DEV: Ruby will parse `3E8` just fine, but to test
25
+ # `num.to_s(base) == value` we need to lowercase
26
+ value = value.downcase
27
+
28
+ # Truncate to trailing 16 characters if length is greater than 16
29
+ # https://github.com/apache/incubator-zipkin/blob/21fe362899fef5c593370466bc5707d3837070c2/zipkin/src/main/java/zipkin2/storage/StorageComponent.java#L49-L53
30
+ # DEV: This ensures we truncate B3 128-bit trace and span ids to 64-bit
31
+ value = value[value.length - 16, 16] if value.length > 16
32
+
33
+ # Remove any leading zeros
34
+ # DEV: When we call `num.to_s(16)` later Ruby will not add leading zeros
35
+ # for us so we want to make sure the comparision will work as expected
36
+ # DEV: regex, remove all leading zeros up until we find the last 0 in the string
37
+ # or we find the first non-zero, this allows `'0000' -> '0'` and `'00001' -> '1'`
38
+ value = value.sub(/^0*(?=(0$)|[^0])/, '')
39
+
40
+ value
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -10,6 +10,14 @@ module Datadog
10
10
  def env_to_float(var, default = nil)
11
11
  ENV.key?(var) ? ENV[var].to_f : default
12
12
  end
13
+
14
+ def env_to_list(var, default = [])
15
+ if ENV.key?(var)
16
+ ENV[var].split(',').map(&:strip)
17
+ else
18
+ default
19
+ end
20
+ end
13
21
  end
14
22
  end
15
23
  end
@@ -10,6 +10,19 @@ module Datadog
10
10
  HTTP_HEADER_ORIGIN = 'x-datadog-origin'.freeze
11
11
  ORIGIN_KEY = '_dd.origin'.freeze
12
12
 
13
+ # B3 headers used for distributed tracing
14
+ B3_HEADER_TRACE_ID = 'x-b3-traceid'.freeze
15
+ B3_HEADER_SPAN_ID = 'x-b3-spanid'.freeze
16
+ B3_HEADER_SAMPLED = 'x-b3-sampled'.freeze
17
+ B3_HEADER_SINGLE = 'b3'.freeze
18
+
19
+ # Distributed tracing propagation options
20
+ PROPAGATION_STYLE_DATADOG = 'Datadog'.freeze
21
+ PROPAGATION_STYLE_B3 = 'B3'.freeze
22
+ PROPAGATION_STYLE_B3_SINGLE_HEADER = 'B3 single header'.freeze
23
+ PROPAGATION_INJECT_STYLE_ENV = 'DD_PROPAGATION_INJECT_STYLE'.freeze
24
+ PROPAGATION_EXTRACT_STYLE_ENV = 'DD_PROPAGATION_EXTRACT_STYLE'.freeze
25
+
13
26
  # gRPC metadata keys for distributed tracing. https://github.com/grpc/grpc-go/blob/v1.10.x/Documentation/grpc-metadata.md
14
27
  GRPC_METADATA_TRACE_ID = 'x-datadog-trace-id'.freeze
15
28
  GRPC_METADATA_PARENT_ID = 'x-datadog-parent-id'.freeze
@@ -1,6 +1,8 @@
1
1
  module Datadog
2
2
  module Ext
3
3
  module NET
4
+ ENV_REPORT_HOSTNAME = 'DD_TRACE_REPORT_HOSTNAME'.freeze
5
+ TAG_HOSTNAME = '_dd.hostname'.freeze
4
6
  TARGET_HOST = 'out.host'.freeze
5
7
  TARGET_PORT = 'out.port'.freeze
6
8
  end
@@ -1,36 +1,75 @@
1
+ require 'ddtrace/configuration'
1
2
  require 'ddtrace/context'
2
3
  require 'ddtrace/ext/distributed'
3
- require 'ddtrace/propagation/distributed_headers'
4
+ require 'ddtrace/ext/priority'
5
+ require 'ddtrace/distributed_tracing/headers/b3'
6
+ require 'ddtrace/distributed_tracing/headers/b3_single'
7
+ require 'ddtrace/distributed_tracing/headers/datadog'
4
8
 
5
9
  module Datadog
6
10
  # HTTPPropagator helps extracting and injecting HTTP headers.
7
11
  module HTTPPropagator
8
12
  include Ext::DistributedTracing
9
13
 
14
+ PROPAGATION_STYLES = { PROPAGATION_STYLE_B3 => DistributedTracing::Headers::B3,
15
+ PROPAGATION_STYLE_B3_SINGLE_HEADER => DistributedTracing::Headers::B3Single,
16
+ PROPAGATION_STYLE_DATADOG => DistributedTracing::Headers::Datadog }.freeze
17
+
10
18
  # inject! popolates the env with span ID, trace ID and sampling priority
11
19
  def self.inject!(context, env)
12
20
  # Prevent propagation from being attempted if context provided is nil.
13
21
  if context.nil?
14
- Datadog::Tracer.log.debug('Cannot inject context into env to propagate over HTTP: context is nil.'.freeze)
22
+ ::Datadog::Tracer.log.debug('Cannot inject context into env to propagate over HTTP: context is nil.'.freeze)
15
23
  return
16
24
  end
17
25
 
18
- env[HTTP_HEADER_TRACE_ID] = context.trace_id.to_s
19
- env[HTTP_HEADER_PARENT_ID] = context.span_id.to_s
20
- env[HTTP_HEADER_SAMPLING_PRIORITY] = context.sampling_priority.to_s
21
- env[HTTP_HEADER_ORIGIN] = context.origin.to_s if context.origin
22
- env.delete(HTTP_HEADER_SAMPLING_PRIORITY) unless context.sampling_priority
26
+ # Inject all configured propagation styles
27
+ ::Datadog.configuration.propagation_inject_style.each do |style|
28
+ propagator = PROPAGATION_STYLES[style]
29
+ propagator.inject!(context, env) unless propagator.nil?
30
+ end
23
31
  end
24
32
 
25
33
  # extract returns a context containing the span ID, trace ID and
26
34
  # sampling priority defined in env.
27
35
  def self.extract(env)
28
- headers = DistributedHeaders.new(env)
29
- return Datadog::Context.new unless headers.valid?
30
- Datadog::Context.new(trace_id: headers.trace_id,
31
- span_id: headers.parent_id,
32
- sampling_priority: headers.sampling_priority,
33
- origin: headers.origin)
36
+ context = nil
37
+ dd_context = nil
38
+
39
+ ::Datadog.configuration.propagation_extract_style.each do |style|
40
+ propagator = PROPAGATION_STYLES[style]
41
+ next if propagator.nil?
42
+
43
+ # Extract context
44
+ # DEV: `propagator.extract` will return `nil`, where `HTTPPropagator#extract` will not
45
+ extracted_context = propagator.extract(env)
46
+ # Skip this style if no valid headers were found
47
+ next if extracted_context.nil?
48
+
49
+ # Keep track of the Datadog extract context, we want to return
50
+ # this one if we have one
51
+ dd_context = extracted_context if extracted_context && style == PROPAGATION_STYLE_DATADOG
52
+
53
+ # No previously extracted context, use the one we just extracted
54
+ if context.nil?
55
+ context = extracted_context
56
+ else
57
+ unless context.trace_id == extracted_context.trace_id && context.span_id == extracted_context.span_id
58
+ # Return an empty/new context if we have a mismatch in values extracted
59
+ msg = "#{context.trace_id} != #{extracted_context.trace_id} && " \
60
+ "#{context.span_id} != #{extracted_context.span_id}"
61
+ ::Datadog::Tracer.log.debug("Cannot extract context from HTTP: extracted contexts differ, #{msg}".freeze)
62
+ # DEV: This will return from `self.extract` not this `each` block
63
+ return ::Datadog::Context.new
64
+ end
65
+ end
66
+ end
67
+
68
+ # Return the extracted context if we found one or else a new empty context
69
+ # Always return the Datadog context if one exists since it has more
70
+ # information than the B3 headers e.g. origin, expanded priority
71
+ # sampling values, etc
72
+ dd_context || context || ::Datadog::Context.new
34
73
  end
35
74
  end
36
75
  end
@@ -0,0 +1,14 @@
1
+ require 'socket'
2
+
3
+ module Datadog
4
+ module Runtime
5
+ # For runtime identity
6
+ module Socket
7
+ module_function
8
+
9
+ def hostname
10
+ ::Socket.gethostname
11
+ end
12
+ end
13
+ end
14
+ end
@@ -1,3 +1,5 @@
1
+ require 'ddtrace/ext/net'
2
+ require 'ddtrace/runtime/socket'
1
3
  require 'ddtrace/runtime/metrics'
2
4
 
3
5
  module Datadog
@@ -44,7 +46,17 @@ module Datadog
44
46
 
45
47
  def flush_trace(trace)
46
48
  processed_traces = Pipeline.process!([trace])
49
+ inject_hostname!(processed_traces.first) if Datadog.configuration.report_hostname
47
50
  transport.send(:traces, processed_traces)
48
51
  end
52
+
53
+ def inject_hostname!(trace)
54
+ unless trace.first.nil?
55
+ hostname = Datadog::Runtime::Socket.hostname
56
+ unless hostname.nil? || hostname.empty?
57
+ trace.first.set_tag(Ext::NET::TAG_HOSTNAME, hostname)
58
+ end
59
+ end
60
+ end
49
61
  end
50
62
  end
@@ -1,8 +1,8 @@
1
1
  module Datadog
2
2
  module VERSION
3
3
  MAJOR = 0
4
- MINOR = 23
5
- PATCH = 3
4
+ MINOR = 24
5
+ PATCH = 0
6
6
  PRE = nil
7
7
 
8
8
  STRING = [MAJOR, MINOR, PATCH, PRE].compact.join('.')
@@ -1,3 +1,6 @@
1
+ require 'ddtrace/ext/net'
2
+ require 'ddtrace/runtime/socket'
3
+
1
4
  require 'ddtrace/transport'
2
5
  require 'ddtrace/encoding'
3
6
  require 'ddtrace/workers'
@@ -70,6 +73,9 @@ module Datadog
70
73
  def send_spans(traces, transport)
71
74
  return true if traces.empty?
72
75
 
76
+ # Inject hostname if configured to do so
77
+ inject_hostname!(traces) if Datadog.configuration.report_hostname
78
+
73
79
  code = transport.send(:traces, traces)
74
80
  status = !transport.server_error?(code)
75
81
  @traces_flushed += traces.length if status
@@ -126,6 +132,17 @@ module Datadog
126
132
 
127
133
  private
128
134
 
135
+ def inject_hostname!(traces)
136
+ traces.each do |trace|
137
+ next if trace.first.nil?
138
+
139
+ hostname = Datadog::Runtime::Socket.hostname
140
+ unless hostname.nil? || hostname.empty?
141
+ trace.first.set_tag(Ext::NET::TAG_HOSTNAME, hostname)
142
+ end
143
+ end
144
+ end
145
+
129
146
  def sampling_updater(action, response, api)
130
147
  return unless action == :traces && response.is_a?(Net::HTTPOK)
131
148
 
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.23.3
4
+ version: 0.24.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: 2019-05-16 00:00:00.000000000 Z
11
+ date: 2019-05-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack
@@ -301,6 +301,7 @@ files:
301
301
  - ".yardopts"
302
302
  - Appraisals
303
303
  - CHANGELOG.md
304
+ - CONTRIBUTING.md
304
305
  - Gemfile
305
306
  - LICENSE
306
307
  - README.md
@@ -309,6 +310,7 @@ files:
309
310
  - benchmarks/sidekiq_test.rb
310
311
  - ddtrace.gemspec
311
312
  - docker-compose.yml
313
+ - docs/DevelopmentGuide.md
312
314
  - docs/GettingStarted.md
313
315
  - lib/ddtrace.rb
314
316
  - lib/ddtrace/analytics.rb
@@ -516,6 +518,11 @@ files:
516
518
  - lib/ddtrace/contrib/sucker_punch/integration.rb
517
519
  - lib/ddtrace/contrib/sucker_punch/patcher.rb
518
520
  - lib/ddtrace/correlation.rb
521
+ - lib/ddtrace/distributed_tracing/headers/b3.rb
522
+ - lib/ddtrace/distributed_tracing/headers/b3_single.rb
523
+ - lib/ddtrace/distributed_tracing/headers/datadog.rb
524
+ - lib/ddtrace/distributed_tracing/headers/headers.rb
525
+ - lib/ddtrace/distributed_tracing/headers/helpers.rb
519
526
  - lib/ddtrace/encoding.rb
520
527
  - lib/ddtrace/environment.rb
521
528
  - lib/ddtrace/error.rb
@@ -555,7 +562,6 @@ files:
555
562
  - lib/ddtrace/pipeline.rb
556
563
  - lib/ddtrace/pipeline/span_filter.rb
557
564
  - lib/ddtrace/pipeline/span_processor.rb
558
- - lib/ddtrace/propagation/distributed_headers.rb
559
565
  - lib/ddtrace/propagation/grpc_propagator.rb
560
566
  - lib/ddtrace/propagation/http_propagator.rb
561
567
  - lib/ddtrace/provider.rb
@@ -565,6 +571,7 @@ files:
565
571
  - lib/ddtrace/runtime/gc.rb
566
572
  - lib/ddtrace/runtime/identity.rb
567
573
  - lib/ddtrace/runtime/metrics.rb
574
+ - lib/ddtrace/runtime/socket.rb
568
575
  - lib/ddtrace/runtime/thread_count.rb
569
576
  - lib/ddtrace/sampler.rb
570
577
  - lib/ddtrace/span.rb
@@ -1,70 +0,0 @@
1
- require 'ddtrace/span'
2
- require 'ddtrace/ext/distributed'
3
-
4
- module Datadog
5
- # DistributedHeaders provides easy access and validation to headers
6
- class DistributedHeaders
7
- include Ext::DistributedTracing
8
-
9
- def initialize(env)
10
- @env = env
11
- end
12
-
13
- def valid?
14
- # Synthetics sends us `X-Datadog-Parent-Id: 0` which normally we would want
15
- # to filter out, but is ok in this context since there is no parent from Synthetics
16
- return true if origin == 'synthetics' && trace_id
17
-
18
- # Sampling priority and origin are optional.
19
- # DEV: We want to explicitly return true/false here
20
- trace_id && parent_id ? true : false
21
- end
22
-
23
- def trace_id
24
- id HTTP_HEADER_TRACE_ID
25
- end
26
-
27
- def parent_id
28
- id HTTP_HEADER_PARENT_ID
29
- end
30
-
31
- def sampling_priority
32
- hdr = header(HTTP_HEADER_SAMPLING_PRIORITY)
33
-
34
- # It's important to make a difference between no header,
35
- # and a header defined to zero.
36
- return if hdr.nil?
37
-
38
- # Convert header to an integer
39
- value = hdr.to_i
40
-
41
- # Ensure the parsed number is the same as the original string value
42
- # e.g. We want to make sure to throw away `'nan'.to_i == 0`
43
- # DEV: Ruby `.to_i` will return `0` if a number could not be parsed
44
- return unless value.to_s == hdr.to_s
45
-
46
- value
47
- end
48
-
49
- def origin
50
- hdr = header(HTTP_HEADER_ORIGIN)
51
- # Only return the value if it is not an empty string
52
- hdr if hdr != ''
53
- end
54
-
55
- private
56
-
57
- def header(name)
58
- rack_header = "http-#{name}".upcase!.tr('-', '_')
59
-
60
- @env[rack_header]
61
- end
62
-
63
- def id(header)
64
- value = header(header).to_i
65
- # Zero or greater than max allowed value of 2**64
66
- return if value.zero? || value > Span::EXTERNAL_MAX_ID
67
- value < 0 ? value + 0x1_0000_0000_0000_0000 : value
68
- end
69
- end
70
- end