ddtrace 0.23.3 → 0.24.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
  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