ddtrace 1.15.0 → 1.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +105 -86
  3. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +6 -2
  4. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +1 -1
  5. data/lib/datadog/core/configuration/settings.rb +1 -1
  6. data/lib/datadog/core/remote/worker.rb +3 -1
  7. data/lib/datadog/opentelemetry/sdk/span_processor.rb +39 -8
  8. data/lib/datadog/opentelemetry/sdk/trace/span.rb +102 -3
  9. data/lib/datadog/opentracer/text_map_propagator.rb +2 -1
  10. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +2 -1
  11. data/lib/datadog/profiling/profiler.rb +26 -2
  12. data/lib/datadog/profiling/scheduler.rb +16 -9
  13. data/lib/datadog/tracing/configuration/ext.rb +3 -0
  14. data/lib/datadog/tracing/configuration/settings.rb +18 -3
  15. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +3 -1
  16. data/lib/datadog/tracing/contrib/concurrent_ruby/context_composite_executor_service.rb +14 -14
  17. data/lib/datadog/tracing/contrib/concurrent_ruby/future_patch.rb +3 -10
  18. data/lib/datadog/tracing/contrib/concurrent_ruby/integration.rb +2 -1
  19. data/lib/datadog/tracing/contrib/concurrent_ruby/patcher.rb +8 -1
  20. data/lib/datadog/tracing/contrib/concurrent_ruby/promises_future_patch.rb +22 -0
  21. data/lib/datadog/tracing/contrib/redis/configuration/settings.rb +1 -1
  22. data/lib/datadog/tracing/contrib/redis/instrumentation.rb +3 -38
  23. data/lib/datadog/tracing/contrib/redis/tags.rb +2 -2
  24. data/lib/datadog/tracing/contrib/redis/trace_middleware.rb +46 -33
  25. data/lib/datadog/tracing/diagnostics/environment_logger.rb +6 -0
  26. data/lib/datadog/tracing/distributed/datadog.rb +0 -1
  27. data/lib/datadog/tracing/distributed/propagation.rb +25 -4
  28. data/lib/datadog/tracing/trace_digest.rb +31 -0
  29. data/lib/ddtrace/version.rb +1 -1
  30. metadata +6 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 18896f24a83a74a5ea6f4adfb3baad9fec922f0d3c3e8d087d28805744584d40
4
- data.tar.gz: 173d93233e49670ed94a9d3cd7501f50c596ceb7624293e4c1992513e6121fa6
3
+ metadata.gz: 2da890705f074bf11ffaa1b5099472a4695d318fc1a1a98238d61a061d6582ec
4
+ data.tar.gz: 8c6db62249ee456939c3eacdeddb0f985148e9daa78adb844144d8da1191e8bb
5
5
  SHA512:
6
- metadata.gz: 174e727aad6f68b2873e88ac71b5a563075c501f4fbd75cad563735c765962d5753f6025a0a75c257f12a7a89fc83713e3ff249f2e2815803786ed709e35daff
7
- data.tar.gz: bc2bbcc9d601473e871dee51e53745045b37204bb902dd2f29deac2ea4c94bdc7aa6c016eecfd2648ccac13393b612400c9167bdbd115271893e561fcb12a8ce
6
+ metadata.gz: a70b6856159a46f30a4ff67bc672011bb016b2aa7b1562834957a325eee90be681f9d0e153ef94c97a4ce675f7b63ac9f5a68f5573b4b9144ee40cf89a70395f
7
+ data.tar.gz: 0d8d1a5d2ee255e53daab1cea7e82ec6f61c8e4a67b89ab1293853786d634c6cde2407a1ea0ceb4ff8bbb3391940dec5bdcfc56dd4b55b7b3826e35bdcff84eb
data/CHANGELOG.md CHANGED
@@ -2,134 +2,128 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
- ## [1.15.0] - 2023-10-09
5
+ ## [1.17.0] - 2023-11-22
6
6
 
7
- ### Highlights
7
+ For W3C Trace Context, this release adds [`tracecontext`](https://www.w3.org/TR/trace-context/) to the default trace propagation extraction and injection styles. The new defaults are:
8
+ * Extraction: `Datadog,b3multi,b3,tracecontext`
9
+ * Injection: `Datadog,tracecontext`
8
10
 
9
- #### Timeline view for Profiler beta
11
+ And to increase interoperability with `tracecontext`, 128-bit Trace ID generation is now the default.
10
12
 
11
- As of ddtrace 1.15.0, the Profiler now supports gathering data for the new
12
- [Timeline view](https://docs.datadoghq.com/profiler/profile_visualizations/#timeline-view).
13
+ For OpenTelemetry, this release adds support for converting [OpenTelemetry Trace Semantic Conventions](https://opentelemetry.io/docs/specs/semconv/general/trace/) into equivalent Datadog trace semantics. Also, it's now possible to configure top-level Datadog span fields using OpenTelemetry span attributes (https://github.com/DataDog/dd-trace-rb/pull/3262).
13
14
 
14
- The Timeline view allows you to look at time-based patterns and work distribution over the period of a single profile: you can look at what individual threads were doing, and when 🎉
15
+ For CI Visibility, you can now manually create CI traces and spans with the [newly released API](https://github.com/DataDog/datadog-ci-rb/releases/tag/v0.4.0).
15
16
 
16
- You can use the timeline view both when looking at individual profiles, as well as when scoped to a given trace.
17
+ ### Added
17
18
 
18
- You can enable it:
19
+ * OpenTelemetry: Parse OpenTelemetry semantic conventions to Datadog's ([#3273][])
20
+ * OpenTelemetry: Support span reserved attribute overrides ([#3262][])
21
+ * Tracing: Ensure W3C `tracestate` is always propagated ([#3255][])
19
22
 
20
- * Using an environment variable by setting `DD_PROFILING_EXPERIMENTAL_TIMELINE_ENABLED=true`
21
- * Or via code by adding to your `Datadog.configure` block:
23
+ ### Changed
22
24
 
23
- ```ruby
24
- Datadog.configure do |c|
25
- # existing configuration
26
- c.profiling.advanced.experimental_timeline_enabled = true
27
- end
28
- ```
25
+ * Tracing: Set 128-bit trace_id to true by default ([#3266][])
26
+ * Tracing: Default trace propagation styles to `Datadog,b3multi,b3,tracecontext` ([#3248][],#3267)
27
+ * Ci-App: Upgraded `datadog-ci` dependency to 0.4 ([#3270][])
28
+
29
+ ## [1.16.2] - 2023-11-10
29
30
 
30
- Give it a try, let us know what you think!
31
+ This release reverts a change to appsec response body parsing that was introduced in [1.16.0 ](https://github.com/DataDog/dd-trace-rb/releases/tag/v1.16.0) that may cause memory leaks.
31
32
 
32
- (Note: We do not recommend enabling this feature prior to 1.15.0!)
33
+ ### Fixed
34
+ * Appsec: [Revert parse response body fix introduced in 1.16.0](https://github.com/DataDog/dd-trace-rb/pull/3153) ([#3252][])
33
35
 
34
- #### google-protobuf dependency is no longer needed by the Profiler
36
+ ## [1.16.1] - 2023-11-08
35
37
 
36
- As of ddtrace version 1.15.0, the `google-protobuf` gem is no longer needed to enable the Profiler.
38
+ ### Fixed
37
39
 
38
- If you've added this gem to your `Gemfile`/`gems.rb` file as part of enabling the Profiler, you can
39
- remove it now. (If you're curious, we've internally replaced this dependency with the `libdatadog` gem.)
40
+ * Tracing: Fix `concurrent-ruby` future propagation without `active_trace` ([#3242][])
41
+ * Tracing: Fix host injection error handling ([#3240][])
40
42
 
41
- #### Configure blocking responses for AppSec via configuration or Remote Configuration
43
+ ## [1.16.0] - 2023-11-03
42
44
 
43
- As of dd-trace-rb 1.15.0, AppSec supports configuring the blocking response.
45
+ **This release includes a security change for the Tracing Redis integration:**
44
46
 
45
- You can configure the blocking response via:
46
- - Using the ENV variables: `DD_APPSEC_HTTP_BLOCKED_TEMPLATE_HTML=#{file_name}`, and `DD_APPSEC_HTTP_BLOCKED_TEMPLATE_JSON=#{file_name}`
47
- - Via code by adding to your `Datadog.configure` block:
47
+ Currently, the Datadog Agent removes command arguments from the resource name. However there are cases, like Redis compressed keys, where this obfuscation cannot correctly remove command arguments. To safeguard that situation, the resource name set by the tracer will only be the command (e.g. `SET`) with no arguments. To retain the previous behavior and keep arguments in the span resource, with the potential risk of some command arguments not being fully obfuscated, set ``DD_REDIS_COMMAND_ARGS=true`` or set the option `c.instrument :redis, command_args: true`.
48
48
 
49
- ```ruby
50
- Datadog.configure do |c|
51
- # … existing configuration …
52
- c.appsec.block.templates.html = "#{file_name}"
53
- c.appsec.block.templates.json = "#{file_name}"
54
- end
55
- ```
49
+ ### Added
56
50
 
57
- - Using the Remote configuration UI. This option allow you to configure the status code and the blocking behaviour. You can redirect malicious attacker to custome pages.
58
- You can find more information on the [official documentation](https://docs.datadoghq.com/security/application_security/threats/protection/#customize-protection-behavior)
51
+ * Tracing: Propagate trace through `Concurrent::Promises.future` ([#1522][])
52
+ * Core: Name `Datadog::Core::Remote::Worker` thread ([#3207][])
59
53
 
60
- #### Configure agentless mode for CI visibility
54
+ ### Changed
61
55
 
62
- If you are using CI visibility with a cloud CI provider without access to the underlying worker nodes, such as GitHub Actions or CircleCI, configure the library to use the Agentless mode.
56
+ * Tracing: Redis - Omit command arguments from span.resource by default ([#3235][])
57
+ * Ci-app: Bump `datadog-ci` dependency from 0.2.0 to 0.3.0 ([#3223][])
63
58
 
64
- For this, set the following environment variables:
65
- - DD_CIVISIBILITY_AGENTLESS_ENABLED=true
66
- - DD_API_KEY=<your_api_key>
59
+ ### Fixed
67
60
 
68
- Additionally, configure which Datadog site you want to send your data to:
69
- - DD_SITE (default: datadoghq.com)
61
+ * Appsec: ASM parse response body ([#3153][])
62
+ * Appsec: ASM make sure to append content type and length information ([#3204][])
63
+ * Appsec: Make sure function that checks content-type header value accepts nil content-type header value ([#3234][])
64
+ * Profiling: Shut down profiler if any components failed ([#3197][])
65
+ * Tracing: Fix `ActiveSupport` instrumentation of custom cache stores ([#3206][])
70
66
 
71
- You can also enable agentless mode with `Datadog.configure` block:
67
+ ## [1.15.0] - 2023-10-09
72
68
 
73
- ```ruby
74
- Datadog.configure do |c|
75
- # existing configuration
76
- c.ci.agentless_mode_enabled = true
77
- # don't forget to set DD_API_KEY env variable!
78
- end
79
- ```
69
+ ### Highlights
70
+
71
+ * Timeline view for Profiler beta
72
+ * Configure AppSec blocking responses via configuration or Remote Configuration
73
+ * CI visibility to configure with agentless mode
74
+
75
+ For more details, check the [release notes](https://github.com/DataDog/dd-trace-rb/releases/tag/v1.15.0)
80
76
 
81
77
  ### Added
82
- * Profiling: Import java-profiler PID controller and port it to C ([#3190][])
83
- * Tracing: Support Opensearch 3 ([#3189][])
84
- * bump datadog-ci dependency to 0.2 ([#3186][])
78
+
85
79
  * Enable allocation counting feature by default for some Ruby 3 versions ([#3176][])
86
- * Tracing: Introduce async configuration for test mode to use standard writer when needed ([#3158][])
87
- * Appsec: Update AppSec rules to 1.8.0 ([#3140][])
88
- * Appsec: Update AppSec rules to 1.7.2 ([#3139][])
89
- * Appsec: ASM API security. Schema extraction ([#3131][], [#3166][], [#3177][])
90
- * Tracing: peer.service adjustment for sql propagation with DBM ([#3127][])
91
- * Ci-app: CI visibility: validate git tags ([#3100][])
92
- * Appsec: Enable configuring blocking response via Remote Configuration ([#3099][])
80
+ * Detect `WebMock` `Cucumber` and `Rails.env` to disable telemetry and remote configuration for development environment ([#3065][], [#3062][], [#3145][])
81
+ * Profiling: Import java-profiler PID controller and port it to C ([#3190][])
93
82
  * Profiling: Record allocation type when sampling objects ([#3096][])
94
- * Appsec: Uppgrade libddwaf-rb version to 1.11.0 ([#3087][])
95
- * Profiling: Mergequeue-status: removed: Include 'ruby vm type' in profiler allocation samples ([#3074][])
96
- * Detect WebMock to disable telemetry and remote configuration ([#3065][])
97
- * Ensure Rails testing is considered a development environment ([#3062][])
83
+ * Profiling: Include `ruby vm type` in profiler allocation samples ([#3074][])
84
+ * Tracing: Support `Rack` 3 ([#3132][])
85
+ * Tracing: Support `Opensearch` 3 ([#3189][])
86
+ * Tracing: `grpc` adds `client_error_handler` option ([#3095][])
87
+ * Tracing: Add `async` option for `test_mode` configuration ([#3158][])
98
88
  * Tracing: Implements `_dd.base_service` tag ([#3018][])
99
89
  * Appsec: Allow blocking response template configuration via ENV variables ([#2975][])
100
- * Ci-app: agentless mode ([#3186][])
90
+ * Appsec: ASM API security. Schema extraction ([#3131][], [#3166][], [#3177][])
91
+ * Appsec: Enable configuring blocking response via Remote Configuration ([#3099][])
92
+ * Ci-app: Validate git tags ([#3100][])
93
+ * Ci-app: Add agentless mode ([#3186][])
101
94
 
102
95
  ### Changed
103
- * Appsec: skip passing waf addresses when the value is empty ([#3188][])
104
- * Tracing: Disable memcached command tag by default ([#3171][])
105
- * Profiling: Upgrade to libdatadog 5 ([#3169][])
96
+
97
+ * Appsec: Skip passing waf addresses when the value is empty ([#3188][])
106
98
  * Profiling: Restore support for Ruby 3.3 ([#3167][])
107
- * Bump debase-ruby_core_source dependency to 3.2.2 ([#3163][])
108
99
  * Profiling: Add approximate thread state categorization for timeline ([#3162][])
109
- * Tracing: remove variable helpers module from our configuration DSL ([#3152][])
110
- * Profiling: Tracing: ekump/decouple core transport ([#3150][])
111
- * Test:Remove explicit dependency on `addressable` ([#3148][])
112
- * Detect Cucumber as a development environment ([#3145][])
113
- * Tracing: rename core configuration option on_set to after_set ([#3107][])
114
- * Profiling: Upgrade to libdatadog 4 ([#3104][])
115
100
  * Profiling: Wire up allocation sampling into `CpuAndWallTimeWorker` ([#3103][])
116
- * Tracing: rename experimental_default_proc to default_proc ([#3091][])
117
- * remove `delegate_to` configuration option from our DSL ([#3086][])
118
- * Ci-app: Fix Datadog::CI::Environment to support the new CI specs ([#3080][])
101
+ * Tracing: `dalli` disable memcached command tag by default ([#3171][])
119
102
  * Tracing: Use first valid extracted style for distributed tracing ([#2879][])
103
+ * Tracing: Rename configuration option `on_set` to `after_set` ([#3107][])
104
+ * Tracing: Rename `experimental_default_proc` to `default_proc` ([#3091][])
105
+ * Tracing: Use `peer.service` for sql comment propagation ([#3127][])
106
+ * Ci-app: Fix `Datadog::CI::Environment` to support the new CI specs ([#3080][])
107
+ * Bump `datadog-ci` dependency to 0.2 ([#3186][])
108
+ * Bump `debase-ruby_core_source` dependency to 3.2.2 ([#3163][])
109
+ * Upgrade `libdatadog` 5 ([#3169][], [#3104][])
110
+ * Upgrade `libddwaf-rb` 1.11.0 ([#3087][])
111
+ * Update AppSec rules to 1.8.0 ([#3140][], [#3139][])
120
112
 
121
113
  ### Fixed
114
+
122
115
  * Profiling: Add workaround for incorrect invoke location when logging gem is in use ([#3183][])
123
- * Profiling: Fix missing endpoint profiling when request_queuing is enabled in rack instrumentation ([#3109][])
124
- * Appsec: Fix a bug with ASM span tags reporting the number of WAF failed loaded rules ([#3106][])
116
+ * Profiling: Fix missing endpoint profiling when `request_queuing` is enabled in `rack` instrumentation ([#3109][])
117
+ * Appsec: Span tags reporting the number of WAF failed loaded rules ([#3106][])
125
118
  * Tracing: Fix tagging with empty data ([#3102][])
126
- * Tracing: fix(grpc): allow custom error_handler for client interceptor ([#3095][])
127
- * Tracing: Correctly set `rails.cache.backend` span tag for multiple stores ([#3060][])
119
+ * Tracing: Fix `rails.cache.backend` span tag with multiple stores ([#3060][])
128
120
 
129
121
  ### Removed
122
+
130
123
  * Profiling: Remove legacy profiler codepath ([#3172][])
131
- * Ci-app: remove CI module and add a dependency on datadog-ci gem ([#3128][])
132
- * Tracing: Remove `depends_on` from Core Configuration option ([#3085][])
124
+ * Ci-app: Remove CI module and add a dependency on [`datadog-ci` gem](https://github.com/DataDog/datadog-ci-rb) ([#3128][])
125
+ * Tracing: Remove `depends_on` option from configuration DSL ([#3085][])
126
+ * Tracing: Remove `delegate_to` option from configuration DSL ([#3086][])
133
127
 
134
128
  ## [1.14.0] - 2023-08-24
135
129
 
@@ -2663,7 +2657,12 @@ Release notes: https://github.com/DataDog/dd-trace-rb/releases/tag/v0.3.1
2663
2657
 
2664
2658
  Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
2665
2659
 
2666
- [Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v1.15.0...master
2660
+
2661
+ [Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v1.17.0...master
2662
+ [1.17.0]: https://github.com/DataDog/dd-trace-rb/compare/v1.16.2...v1.17.0
2663
+ [1.16.2]: https://github.com/DataDog/dd-trace-rb/compare/v1.16.1...v1.16.2
2664
+ [1.16.1]: https://github.com/DataDog/dd-trace-rb/compare/v1.16.0...v1.16.1
2665
+ [1.16.0]: https://github.com/DataDog/dd-trace-rb/compare/v1.15.0...v1.16.0
2667
2666
  [1.15.0]: https://github.com/DataDog/dd-trace-rb/compare/v1.14.0...v1.15.0
2668
2667
  [1.14.0]: https://github.com/DataDog/dd-trace-rb/compare/v1.13.1...1.14.0
2669
2668
  [1.13.1]: https://github.com/DataDog/dd-trace-rb/compare/v1.13.0...1.13.1
@@ -3401,6 +3400,7 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
3401
3400
  [#1509]: https://github.com/DataDog/dd-trace-rb/issues/1509
3402
3401
  [#1510]: https://github.com/DataDog/dd-trace-rb/issues/1510
3403
3402
  [#1511]: https://github.com/DataDog/dd-trace-rb/issues/1511
3403
+ [#1522]: https://github.com/DataDog/dd-trace-rb/issues/1522
3404
3404
  [#1523]: https://github.com/DataDog/dd-trace-rb/issues/1523
3405
3405
  [#1524]: https://github.com/DataDog/dd-trace-rb/issues/1524
3406
3406
  [#1529]: https://github.com/DataDog/dd-trace-rb/issues/1529
@@ -3842,12 +3842,14 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
3842
3842
  [#3127]: https://github.com/DataDog/dd-trace-rb/issues/3127
3843
3843
  [#3128]: https://github.com/DataDog/dd-trace-rb/issues/3128
3844
3844
  [#3131]: https://github.com/DataDog/dd-trace-rb/issues/3131
3845
+ [#3132]: https://github.com/DataDog/dd-trace-rb/issues/3132
3845
3846
  [#3139]: https://github.com/DataDog/dd-trace-rb/issues/3139
3846
3847
  [#3140]: https://github.com/DataDog/dd-trace-rb/issues/3140
3847
3848
  [#3145]: https://github.com/DataDog/dd-trace-rb/issues/3145
3848
3849
  [#3148]: https://github.com/DataDog/dd-trace-rb/issues/3148
3849
3850
  [#3150]: https://github.com/DataDog/dd-trace-rb/issues/3150
3850
3851
  [#3152]: https://github.com/DataDog/dd-trace-rb/issues/3152
3852
+ [#3153]: https://github.com/DataDog/dd-trace-rb/issues/3153
3851
3853
  [#3158]: https://github.com/DataDog/dd-trace-rb/issues/3158
3852
3854
  [#3162]: https://github.com/DataDog/dd-trace-rb/issues/3162
3853
3855
  [#3163]: https://github.com/DataDog/dd-trace-rb/issues/3163
@@ -3863,6 +3865,23 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
3863
3865
  [#3188]: https://github.com/DataDog/dd-trace-rb/issues/3188
3864
3866
  [#3189]: https://github.com/DataDog/dd-trace-rb/issues/3189
3865
3867
  [#3190]: https://github.com/DataDog/dd-trace-rb/issues/3190
3868
+ [#3197]: https://github.com/DataDog/dd-trace-rb/issues/3197
3869
+ [#3204]: https://github.com/DataDog/dd-trace-rb/issues/3204
3870
+ [#3206]: https://github.com/DataDog/dd-trace-rb/issues/3206
3871
+ [#3207]: https://github.com/DataDog/dd-trace-rb/issues/3207
3872
+ [#3223]: https://github.com/DataDog/dd-trace-rb/issues/3223
3873
+ [#3234]: https://github.com/DataDog/dd-trace-rb/issues/3234
3874
+ [#3235]: https://github.com/DataDog/dd-trace-rb/issues/3235
3875
+ [#3240]: https://github.com/DataDog/dd-trace-rb/issues/3240
3876
+ [#3242]: https://github.com/DataDog/dd-trace-rb/issues/3242
3877
+ [#3248]: https://github.com/DataDog/dd-trace-rb/issues/3248
3878
+ [#3252]: https://github.com/DataDog/dd-trace-rb/issues/3252
3879
+ [#3255]: https://github.com/DataDog/dd-trace-rb/issues/3255
3880
+ [#3262]: https://github.com/DataDog/dd-trace-rb/issues/3262
3881
+ [#3266]: https://github.com/DataDog/dd-trace-rb/issues/3266
3882
+ [#3267]: https://github.com/DataDog/dd-trace-rb/issues/3267
3883
+ [#3270]: https://github.com/DataDog/dd-trace-rb/issues/3270
3884
+ [#3273]: https://github.com/DataDog/dd-trace-rb/issues/3273
3866
3885
  [@AdrianLC]: https://github.com/AdrianLC
3867
3886
  [@Azure7111]: https://github.com/Azure7111
3868
3887
  [@BabyGroot]: https://github.com/BabyGroot
@@ -40,9 +40,13 @@ module Datadog
40
40
  end
41
41
 
42
42
  def headers
43
- request.env.each_with_object({}) do |(k, v), h|
44
- h[k.gsub(/^HTTP_/, '').downcase.tr('_', '-')] = v if k =~ /^HTTP_/
43
+ result = request.env.each_with_object({}) do |(k, v), h|
44
+ h[k.gsub(/^HTTP_/, '').downcase!.tr('_', '-')] = v if k =~ /^HTTP_/
45
45
  end
46
+
47
+ result['content-type'] = request.content_type if request.content_type
48
+ result['content-length'] = request.content_length if request.content_length
49
+ result
46
50
  end
47
51
 
48
52
  def body
@@ -70,7 +70,7 @@ module Datadog
70
70
  request_return[2],
71
71
  request_return[0],
72
72
  request_return[1],
73
- scope: scope
73
+ scope: scope,
74
74
  )
75
75
 
76
76
  _response_return, response_response = Instrumentation.gateway.push('rack.response', gateway_response)
@@ -330,7 +330,7 @@ module Datadog
330
330
  # Caveat 3 (severe):
331
331
  # Ruby 3.2.0 to 3.2.2 have a bug in the newobj tracepoint (https://bugs.ruby-lang.org/issues/19482,
332
332
  # https://github.com/ruby/ruby/pull/7464) so that's an extra reason why it's not safe on those Rubies.
333
- # This bug is fixed on Ruby versions 3.2.2 and 3.3.0.
333
+ # This bug is fixed on Ruby versions 3.2.3 and 3.3.0.
334
334
  #
335
335
  # @default `true` on Ruby 2.x and 3.1.4+, 3.2.3+ and 3.3.0+; `false` for Ruby 3.0 and unpatched Rubies.
336
336
  option :allocation_counting_enabled do |o|
@@ -28,7 +28,9 @@ module Datadog
28
28
 
29
29
  @starting = true
30
30
 
31
- @thr = Thread.new { poll(@interval) }
31
+ thread = Thread.new { poll(@interval) }
32
+ thread.name = self.class.name unless Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.3')
33
+ @thr = thread
32
34
 
33
35
  @started = true
34
36
  @starting = false
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'trace/span'
4
+
3
5
  module Datadog
4
6
  module OpenTelemetry
5
7
  module SDK
@@ -76,16 +78,11 @@ module Datadog
76
78
  end
77
79
 
78
80
  def start_datadog_span(span)
79
- tags = span.resource.attribute_enumerator.to_h
81
+ attributes = span.attributes.dup # Dup to allow modification of frozen Hash
80
82
 
81
- kind = span.kind || 'internal'
82
- tags[Tracing::Metadata::Ext::TAG_KIND] = kind
83
+ name, kwargs = span_arguments(span, attributes)
83
84
 
84
- datadog_span = Tracing.trace(
85
- span.name,
86
- tags: tags,
87
- start_time: ns_to_time(span.start_timestamp)
88
- )
85
+ datadog_span = Tracing.trace(name, **kwargs)
89
86
 
90
87
  datadog_span.set_error([nil, span.status.description]) unless span.status.ok?
91
88
  datadog_span.set_tags(span.attributes)
@@ -93,6 +90,40 @@ module Datadog
93
90
  datadog_span
94
91
  end
95
92
 
93
+ # Some special attributes can override Datadog Span fields
94
+ def span_arguments(span, attributes)
95
+ if attributes.key?('analytics.event') && (analytics_event = attributes['analytics.event']).respond_to?(:casecmp)
96
+ attributes[Tracing::Metadata::Ext::Analytics::TAG_SAMPLE_RATE] = analytics_event.casecmp('true') == 0 ? 1 : 0
97
+ end
98
+ attributes[Tracing::Metadata::Ext::TAG_KIND] = span.kind || 'internal'
99
+
100
+ kwargs = { start_time: ns_to_time(span.start_timestamp) }
101
+
102
+ name = if attributes.key?('operation.name')
103
+ attributes['operation.name']
104
+ elsif (rich_name = Datadog::OpenTelemetry::Trace::Span.enrich_name(span.kind, attributes))
105
+ rich_name.downcase
106
+ else
107
+ span.kind
108
+ end
109
+
110
+ kwargs[:resource] = attributes.key?('resource.name') ? attributes['resource.name'] : span.name
111
+ kwargs[:service] = attributes['service.name'] if attributes.key?('service.name')
112
+ kwargs[:type] = attributes['span.type'] if attributes.key?('span.type')
113
+
114
+ attributes.reject! { |key, _| OpenTelemetry::Trace::Span::DATADOG_SPAN_ATTRIBUTE_OVERRIDES.include?(key) }
115
+
116
+ # DEV: There's no `flat_map!`; we have to split it into two operations
117
+ attributes = attributes.map do |key, value|
118
+ Datadog::OpenTelemetry::Trace::Span.serialize_attribute(key, value)
119
+ end
120
+ attributes.flatten!(1)
121
+
122
+ kwargs[:tags] = attributes
123
+
124
+ [name, kwargs]
125
+ end
126
+
96
127
  # From nanoseconds, used by OpenTelemetry, to a {Time} object, used by the Datadog Tracer.
97
128
  def ns_to_time(timestamp_ns)
98
129
  Time.at(timestamp_ns / 1000000000.0)
@@ -36,6 +36,78 @@ module Datadog
36
36
  span.set_error(status.description) if status && status.code == ::OpenTelemetry::Trace::Status::ERROR
37
37
  end
38
38
 
39
+ # Serialize values into Datadog span tags and metrics.
40
+ # Notably, arrays are exploded into many keys, each with
41
+ # a numeric suffix representing the array index, for example:
42
+ # `'foo' => ['a','b']` becomes `'foo.0' => 'a', 'foo.1' => 'b'`
43
+ def self.serialize_attribute(key, value)
44
+ if value.is_a?(Array)
45
+ value.flat_map.with_index do |v, idx|
46
+ serialize_attribute("#{key}.#{idx}", v)
47
+ end
48
+ elsif value.is_a?(TrueClass) || value.is_a?(FalseClass)
49
+ [[key, value.to_s]]
50
+ else
51
+ [[key, value]]
52
+ end
53
+ end
54
+
55
+ # Create a meaningful Datadog operation name from the OpenTelemetry
56
+ # semantic convention for span kind and span attributes.
57
+ # @see https://opentelemetry.io/docs/specs/semconv/general/trace/
58
+
59
+ # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
60
+ def self.enrich_name(kind, attrs)
61
+ if attrs.key?('http.request.method')
62
+ return 'http.server.request' if kind == :server
63
+ return 'http.client.request' if kind == :client
64
+ end
65
+
66
+ return "#{attrs['db.system']}.query" if attrs.key?('db.system') && kind == :client
67
+
68
+ if (attrs.key?('messaging.system') || attrs.key?('messaging.operation')) &&
69
+ [:consumer, :producer, :server, :client].include?(kind)
70
+
71
+ return "#{attrs['messaging.system']}.#{attrs['messaging.operation']}"
72
+ end
73
+
74
+ if attrs.key?('rpc.system')
75
+ if attrs['rpc.system'] == 'aws-api' && kind == :client
76
+ service = attrs['rpc.service']
77
+ return "aws.#{service || 'client'}.request"
78
+ end
79
+
80
+ if kind == :client
81
+ return "#{attrs['rpc.system']}.client.request"
82
+ elsif kind == :server
83
+ return "#{attrs['rpc.system']}.server.request"
84
+ end
85
+ end
86
+
87
+ if attrs.key?('faas.invoked_provider') && attrs.key?('faas.invoked_name') && kind == :client
88
+ provider = attrs['faas.invoked_provider']
89
+ name = attrs['faas.invoked_name']
90
+ return "#{provider}.#{name}.invoke"
91
+ end
92
+
93
+ return "#{attrs['faas.trigger']}.invoke" if attrs.key?('faas.trigger') && kind == :server
94
+
95
+ return 'graphql.server.request' if attrs.key?('graphql.operation.type')
96
+
97
+ if kind == :server
98
+ protocol = attrs['network.protocol.name']
99
+ return protocol ? "#{protocol}.server.request" : 'server.request'
100
+ end
101
+
102
+ if kind == :client
103
+ protocol = attrs['network.protocol.name']
104
+ return protocol ? "#{protocol}.client.request" : 'client.request'
105
+ end
106
+
107
+ kind.to_s
108
+ end
109
+ # rubocop:enable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
110
+
39
111
  private
40
112
 
41
113
  def datadog_set_attribute(key)
@@ -46,10 +118,18 @@ module Datadog
46
118
  # DEV: Accesses `@attributes` directly, since using `#attributes`
47
119
  # DEV: clones the hash, causing unnecessary overhead.
48
120
  if @attributes.key?(key)
49
- value = @attributes[key]
50
- span.set_tag(key, value)
121
+ # Try to find a richer operation name, unless an explicit override was provided.
122
+ if !@attributes.key?('operation.name') && (rich_name = Span.enrich_name(kind, @attributes))
123
+ span.name = rich_name.downcase
124
+ end
125
+
126
+ Span.serialize_attribute(key, @attributes[key]).each do |new_key, value|
127
+ override_datadog_values(span, new_key, value)
51
128
 
52
- span.service = value if key == 'service.name'
129
+ # When an attribute is used to override a Datadog Span property,
130
+ # it should NOT be set as a Datadog Span tag.
131
+ span.set_tag(new_key, value) unless DATADOG_SPAN_ATTRIBUTE_OVERRIDES.include?(new_key)
132
+ end
53
133
  else
54
134
  span.clear_tag(key)
55
135
 
@@ -61,6 +141,25 @@ module Datadog
61
141
  end
62
142
  end
63
143
 
144
+ # Some special attributes can override Datadog Span fields beyond tags and metrics.
145
+ # @return [Boolean] true if the key is a Datadog Span override attribute, false otherwise
146
+ def override_datadog_values(span, key, value)
147
+ span.name = value if key == 'operation.name'
148
+ span.resource = value if key == 'resource.name'
149
+ span.service = value if key == 'service.name'
150
+ span.type = value if key == 'span.type'
151
+
152
+ if key == 'analytics.event' && value.respond_to?(:casecmp)
153
+ Datadog::Tracing::Analytics.set_sample_rate(
154
+ span,
155
+ value.casecmp('true') == 0 ? 1 : 0
156
+ )
157
+ end
158
+ end
159
+
160
+ DATADOG_SPAN_ATTRIBUTE_OVERRIDES = ['analytics.event', 'operation.name', 'resource.name', 'service.name',
161
+ 'span.type'].freeze
162
+
64
163
  ::OpenTelemetry::SDK::Trace::Span.prepend(self)
65
164
  end
66
165
  end
@@ -33,7 +33,8 @@ module Datadog
33
33
  carrier[Tracing::Distributed::Datadog::ORIGIN_KEY] = digest.trace_origin
34
34
  carrier[Tracing::Distributed::Datadog::PARENT_ID_KEY] = digest.span_id
35
35
  carrier[Tracing::Distributed::Datadog::SAMPLING_PRIORITY_KEY] = digest.trace_sampling_priority
36
- carrier[Tracing::Distributed::Datadog::TRACE_ID_KEY] = digest.trace_id
36
+ carrier[Tracing::Distributed::Datadog::TRACE_ID_KEY] =
37
+ Datadog::Tracing::Utils::TraceId.to_low_order(digest.trace_id)
37
38
 
38
39
  nil
39
40
  end
@@ -53,7 +53,7 @@ module Datadog
53
53
  @idle_sampling_helper = idle_sampling_helper
54
54
  end
55
55
 
56
- def start
56
+ def start(on_failure_proc: nil)
57
57
  @start_stop_mutex.synchronize do
58
58
  return if @worker_thread && @worker_thread.alive?
59
59
 
@@ -74,6 +74,7 @@ module Datadog
74
74
  'CpuAndWallTimeWorker thread error. ' \
75
75
  "Cause: #{e.class.name} #{e.message} Location: #{Array(e.backtrace).first}"
76
76
  )
77
+ on_failure_proc&.call
77
78
  end
78
79
  end
79
80
  @worker_thread.name = self.class.name # Repeated from above to make sure thread gets named asap
@@ -21,18 +21,42 @@ module Datadog
21
21
  scheduler.reset_after_fork
22
22
  end
23
23
 
24
- worker.start
25
- scheduler.start
24
+ worker.start(on_failure_proc: proc { component_failed(:worker) })
25
+ scheduler.start(on_failure_proc: proc { component_failed(:scheduler) })
26
26
  end
27
27
 
28
28
  def shutdown!
29
29
  Datadog.logger.debug('Shutting down profiler')
30
30
 
31
+ stop_worker
32
+ stop_scheduler
33
+ end
34
+
35
+ private
36
+
37
+ def stop_worker
31
38
  worker.stop
39
+ end
32
40
 
41
+ def stop_scheduler
33
42
  scheduler.enabled = false
34
43
  scheduler.stop(true)
35
44
  end
45
+
46
+ def component_failed(failed_component)
47
+ Datadog.logger.warn(
48
+ "Detected issue with profiler (#{failed_component} component), stopping profiling. " \
49
+ 'See previous log messages for details.'
50
+ )
51
+
52
+ if failed_component == :worker
53
+ stop_scheduler
54
+ elsif failed_component == :scheduler
55
+ stop_worker
56
+ else
57
+ raise ArgumentError, "Unexpected failed_component: #{failed_component.inspect}"
58
+ end
59
+ end
36
60
  end
37
61
  end
38
62
  end
@@ -45,20 +45,27 @@ module Datadog
45
45
  self.enabled = enabled
46
46
  end
47
47
 
48
- def start
49
- perform
48
+ def start(on_failure_proc: nil)
49
+ perform(on_failure_proc)
50
50
  end
51
51
 
52
- def perform
53
- # A profiling flush may be called while the VM is shutting down, to report the last profile. When we do so,
54
- # we impose a strict timeout. This means this last profile may or may not be sent, depending on if the flush can
55
- # successfully finish in the strict timeout.
56
- # This can be somewhat confusing (why did it not get reported?), so let's at least log what happened.
57
- interrupted = true
58
-
52
+ def perform(on_failure_proc)
59
53
  begin
54
+ # A profiling flush may be called while the VM is shutting down, to report the last profile. When we do so,
55
+ # we impose a strict timeout. This means this last profile may or may not be sent, depending on if the flush can
56
+ # successfully finish in the strict timeout.
57
+ # This can be somewhat confusing (why did it not get reported?), so let's at least log what happened.
58
+ interrupted = true
59
+
60
60
  flush_and_wait
61
61
  interrupted = false
62
+ rescue Exception => e # rubocop:disable Lint/RescueException
63
+ Datadog.logger.warn(
64
+ 'Profiling::Scheduler thread error. ' \
65
+ "Cause: #{e.class.name} #{e.message} Location: #{Array(e.backtrace).first}"
66
+ )
67
+ on_failure_proc&.call
68
+ raise
62
69
  ensure
63
70
  Datadog.logger.debug('#flush was interrupted or failed before it could complete') if interrupted
64
71
  end
@@ -62,6 +62,9 @@ module Datadog
62
62
  # @see https://opentelemetry.io/docs/concepts/sdk-configuration/general-sdk-configuration/#get_otel__propagators
63
63
  PROPAGATION_STYLE_NONE = 'none'
64
64
 
65
+ # Strictly stop at the first successfully serialized style.
66
+ EXTRACT_FIRST = 'DD_TRACE_PROPAGATION_EXTRACT_FIRST'
67
+
65
68
  ENV_X_DATADOG_TAGS_MAX_LENGTH = 'DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH'
66
69
  end
67
70
 
@@ -63,6 +63,7 @@ module Datadog
63
63
  Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_DATADOG,
64
64
  Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_B3_MULTI_HEADER,
65
65
  Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_B3_SINGLE_HEADER,
66
+ Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_TRACE_CONTEXT,
66
67
  ]
67
68
  )
68
69
  o.after_set do |styles|
@@ -93,7 +94,10 @@ module Datadog
93
94
  o.deprecated_env Tracing::Configuration::Ext::Distributed::ENV_PROPAGATION_STYLE_INJECT_OLD
94
95
  o.env Tracing::Configuration::Ext::Distributed::ENV_PROPAGATION_STYLE_INJECT
95
96
  # DEV-2.0: Change default value to `tracecontext, Datadog`.
96
- o.default [Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_DATADOG]
97
+ o.default [
98
+ Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_DATADOG,
99
+ Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_TRACE_CONTEXT,
100
+ ]
97
101
  o.after_set do |styles|
98
102
  # Modernize B3 options
99
103
  # DEV-2.0: Can be removed with the removal of deprecated B3 constants.
@@ -142,6 +146,17 @@ module Datadog
142
146
  set_option(:propagation_inject_style, styles)
143
147
  end
144
148
  end
149
+
150
+ # Strictly stop at the first successfully serialized style.
151
+ # This prevents the tracer from enriching the extracted context with information from
152
+ # other valid propagations styles present in the request.
153
+ # @default `DD_TRACE_PROPAGATION_EXTRACT_FIRST` environment variable, otherwise `false`.
154
+ # @return [Boolean]
155
+ option :propagation_extract_first do |o|
156
+ o.env Tracing::Configuration::Ext::Distributed::EXTRACT_FIRST
157
+ o.default false
158
+ o.type :bool
159
+ end
145
160
  end
146
161
 
147
162
  # Enable trace collection and span generation.
@@ -177,11 +192,11 @@ module Datadog
177
192
 
178
193
  # Enable 128 bit trace id generation.
179
194
  #
180
- # @default `DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED` environment variable, otherwise `false`
195
+ # @default `DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED` environment variable, otherwise `true`
181
196
  # @return [Boolean]
182
197
  option :trace_id_128_bit_generation_enabled do |o|
183
198
  o.env Tracing::Configuration::Ext::ENV_TRACE_ID_128_BIT_GENERATION_ENABLED
184
- o.default false
199
+ o.default true
185
200
  o.type :bool
186
201
  end
187
202
 
@@ -101,7 +101,9 @@ module Datadog
101
101
 
102
102
  # DEV: String#underscore is available through ActiveSupport, and is
103
103
  # DEV: the exact reverse operation to `#camelize`.
104
- @store_name = self.class.name.underscore.match(%r{active_support/cache/(.*)})[1]
104
+ # DEV: String#demodulize is available through ActiveSupport, and is
105
+ # DEV: used to remove the module ('*::') part of a constant name.
106
+ @store_name = self.class.name.demodulize.underscore
105
107
  end
106
108
  end
107
109
 
@@ -6,7 +6,7 @@ module Datadog
6
6
  module Tracing
7
7
  module Contrib
8
8
  module ConcurrentRuby
9
- # wraps existing executor to carry over trace context
9
+ # Wraps existing executor to carry over trace context
10
10
  class ContextCompositeExecutorService
11
11
  include Concurrent::ExecutorService
12
12
 
@@ -16,20 +16,20 @@ module Datadog
16
16
  @composited_executor = composited_executor
17
17
  end
18
18
 
19
- # post method runs the task within composited executor - in a different thread
19
+ # post method runs the task within composited executor - in a different thread. The original arguments are
20
+ # captured to be propagated to the composited executor post method
20
21
  def post(*args, &task)
21
- tracer = Tracing.send(:tracer)
22
- parent_context = tracer.provider.context
23
-
24
- @composited_executor.post(*args) do
25
- begin
26
- original_context = tracer.provider.context
27
- tracer.provider.context = parent_context
28
- yield
29
- ensure
30
- # Restore context in case the current thread gets reused
31
- tracer.provider.context = original_context
32
- end
22
+ digest = Tracing.active_trace && Tracing.active_trace.to_digest
23
+ executor = @composited_executor.is_a?(Symbol) ? Concurrent.executor(@composited_executor) : @composited_executor
24
+
25
+ # Pass the original arguments to the composited executor, which
26
+ # pushes them (possibly transformed) as block args
27
+ executor.post(*args) do |*block_args|
28
+ Tracing.continue_trace!(digest)
29
+
30
+ # Pass the executor-provided block args as they should have been
31
+ # originally passed without composition, see ChainPromise#on_resolvable
32
+ yield(*block_args)
33
33
  end
34
34
  end
35
35
 
@@ -8,17 +8,10 @@ module Datadog
8
8
  module ConcurrentRuby
9
9
  # This patches the Future - to wrap executor service using ContextCompositeExecutorService
10
10
  module FuturePatch
11
- def self.included(base)
12
- base.class_eval do
13
- alias_method :ns_initialize_without_datadog, :ns_initialize
14
- remove_method(:ns_initialize)
11
+ def ns_initialize(value, opts)
12
+ super(value, opts)
15
13
 
16
- def ns_initialize(value, opts)
17
- ns_initialize_without_datadog(value, opts)
18
-
19
- @executor = ContextCompositeExecutorService.new(@executor)
20
- end
21
- end
14
+ @executor = ContextCompositeExecutorService.new(@executor)
22
15
  end
23
16
  end
24
17
  end
@@ -20,7 +20,8 @@ module Datadog
20
20
  end
21
21
 
22
22
  def self.loaded?
23
- !defined?(::Concurrent::Future).nil?
23
+ # Concurrent::Future is deprecated in favour of Concurrent::Promises::Future
24
+ !defined?(::Concurrent::Promises::Future).nil? || !defined?(::Concurrent::Future).nil?
24
25
  end
25
26
 
26
27
  def self.compatible?
@@ -19,11 +19,18 @@ module Datadog
19
19
  def patch
20
20
  require_relative 'future_patch'
21
21
  patch_future
22
+ require_relative 'promises_future_patch'
23
+ patch_promises_future
22
24
  end
23
25
 
24
26
  # Propagate tracing context in Concurrent::Future
25
27
  def patch_future
26
- ::Concurrent::Future.include(FuturePatch)
28
+ ::Concurrent::Future.prepend(FuturePatch) if defined?(::Concurrent::Future)
29
+ end
30
+
31
+ # Propagate tracing context in Concurrent::Promises::Future
32
+ def patch_promises_future
33
+ ::Concurrent::Promises.singleton_class.prepend(PromisesFuturePatch) if defined?(::Concurrent::Promises::Future)
27
34
  end
28
35
  end
29
36
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'context_composite_executor_service'
4
+
5
+ module Datadog
6
+ module Tracing
7
+ module Contrib
8
+ module ConcurrentRuby
9
+ # This patches the Future - to wrap executor service using ContextCompositeExecutorService
10
+ module PromisesFuturePatch
11
+ def future_on(default_executor, *args, &task)
12
+ unless default_executor.is_a?(ContextCompositeExecutorService)
13
+ default_executor = ContextCompositeExecutorService.new(default_executor)
14
+ end
15
+
16
+ super(default_executor, *args, &task)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -32,7 +32,7 @@ module Datadog
32
32
  option :command_args do |o|
33
33
  o.type :bool
34
34
  o.env Ext::ENV_COMMAND_ARGS
35
- o.default true
35
+ o.default false
36
36
  end
37
37
 
38
38
  option :service_name do |o|
@@ -3,6 +3,7 @@ require_relative 'configuration/resolver'
3
3
  require_relative 'ext'
4
4
  require_relative 'quantize'
5
5
  require_relative 'tags'
6
+ require_relative 'trace_middleware'
6
7
 
7
8
  module Datadog
8
9
  module Tracing
@@ -17,31 +18,11 @@ module Datadog
17
18
  # InstanceMethods - implementing instrumentation
18
19
  module InstanceMethods
19
20
  def call(*args, &block)
20
- show_command_args = command_args?
21
-
22
- Tracing.trace(Contrib::Redis::Ext::SPAN_COMMAND) do |span|
23
- span.service = service_name
24
- span.span_type = Contrib::Redis::Ext::TYPE
25
- span.resource = get_command(args, show_command_args)
26
- Contrib::Redis::Tags.set_common_tags(self, span, show_command_args)
27
-
28
- super
29
- end
21
+ TraceMiddleware.call(self, args[0], service_name, command_args?) { super }
30
22
  end
31
23
 
32
24
  def call_pipeline(*args, &block)
33
- show_command_args = command_args?
34
-
35
- Tracing.trace(Contrib::Redis::Ext::SPAN_COMMAND) do |span|
36
- span.service = service_name
37
- span.span_type = Contrib::Redis::Ext::TYPE
38
- commands = get_pipeline_commands(args, show_command_args)
39
- span.resource = commands.any? ? commands.join("\n") : '(none)'
40
- span.set_metric Contrib::Redis::Ext::METRIC_PIPELINE_LEN, commands.length
41
- Contrib::Redis::Tags.set_common_tags(self, span, show_command_args)
42
-
43
- super
44
- end
25
+ TraceMiddleware.call_pipelined(self, args[0].commands, service_name, command_args?) { super }
45
26
  end
46
27
 
47
28
  private
@@ -59,22 +40,6 @@ module Datadog
59
40
  datadog_configuration[:service_name]
60
41
  end
61
42
 
62
- def get_command(args, show_command_args)
63
- if show_command_args
64
- Contrib::Redis::Quantize.format_command_args(*args)
65
- else
66
- Contrib::Redis::Quantize.get_verb(*args)
67
- end
68
- end
69
-
70
- def get_pipeline_commands(args, show_command_args)
71
- if show_command_args
72
- args[0].commands.map { |c| Contrib::Redis::Quantize.format_command_args(c) }
73
- else
74
- args[0].commands.map { |c| Contrib::Redis::Quantize.get_verb(c) }
75
- end
76
- end
77
-
78
43
  def datadog_configuration
79
44
  Datadog.configuration.tracing[:redis, options]
80
45
  end
@@ -12,7 +12,7 @@ module Datadog
12
12
  # Tags handles generic common tags assignment.
13
13
  module Tags
14
14
  class << self
15
- def set_common_tags(client, span, show_command_args)
15
+ def set_common_tags(client, span, raw_command)
16
16
  if datadog_configuration[:peer_service]
17
17
  span.set_tag(
18
18
  Tracing::Metadata::Ext::TAG_PEER_SERVICE,
@@ -42,7 +42,7 @@ module Datadog
42
42
 
43
43
  span.set_tag Ext::TAG_DATABASE_INDEX, client.db.to_s
44
44
  span.set_tag Ext::TAG_DB, client.db
45
- span.set_tag Ext::TAG_RAW_COMMAND, span.resource if show_command_args
45
+ span.set_tag Ext::TAG_RAW_COMMAND, raw_command
46
46
 
47
47
  Contrib::SpanAttributeSchema.set_peer_service!(span, Ext::PEER_SERVICE_SOURCES)
48
48
  end
@@ -9,55 +9,68 @@ module Datadog
9
9
  module Redis
10
10
  # Instrumentation for Redis 5+
11
11
  module TraceMiddleware
12
- def call(commands, redis_config)
13
- Tracing.trace(Contrib::Redis::Ext::SPAN_COMMAND) do |span|
14
- datadog_configuration = resolve(redis_config)
15
- resource = get_command(commands, datadog_configuration[:command_args])
12
+ # Instruments {RedisClient::ConnectionMixin#call}.
13
+ def call(command, redis_config)
14
+ config = resolve(redis_config)
15
+ TraceMiddleware.call(redis_config, command, config[:service_name], config[:command_args]) { super }
16
+ end
17
+
18
+ # Instruments {RedisClient::ConnectionMixin#call_pipelined}.
19
+ def call_pipelined(commands, redis_config)
20
+ config = resolve(redis_config)
21
+ TraceMiddleware.call_pipelined(redis_config, commands, config[:service_name], config[:command_args]) { super }
22
+ end
16
23
 
17
- span.service = datadog_configuration[:service_name]
18
- span.span_type = Contrib::Redis::Ext::TYPE
19
- span.resource = resource
24
+ class << self
25
+ def call(client, command, service_name, command_args)
26
+ Tracing.trace(Redis::Ext::SPAN_COMMAND, type: Redis::Ext::TYPE, service: service_name) do |span|
27
+ raw_command = get_command(command, true)
28
+ span.resource = command_args ? raw_command : get_command(command, false)
20
29
 
21
- Contrib::Redis::Tags.set_common_tags(redis_config, span, datadog_configuration[:command_args])
30
+ Contrib::Redis::Tags.set_common_tags(client, span, raw_command)
22
31
 
23
- super
32
+ yield
33
+ end
24
34
  end
25
- end
26
35
 
27
- def call_pipelined(commands, redis_config)
28
- Tracing.trace(Contrib::Redis::Ext::SPAN_COMMAND) do |span|
29
- datadog_configuration = resolve(redis_config)
30
- pipelined_commands = get_pipeline_commands(commands, datadog_configuration[:command_args])
36
+ def call_pipelined(client, commands, service_name, command_args)
37
+ Tracing.trace(Redis::Ext::SPAN_COMMAND, type: Redis::Ext::TYPE, service: service_name) do |span|
38
+ raw_command = get_pipeline_commands(commands, true)
39
+ span.resource = command_args ? raw_command : get_pipeline_commands(commands, false)
31
40
 
32
- span.service = datadog_configuration[:service_name]
33
- span.span_type = Contrib::Redis::Ext::TYPE
34
- span.resource = pipelined_commands.join("\n")
35
- span.set_metric Contrib::Redis::Ext::METRIC_PIPELINE_LEN, pipelined_commands.length
41
+ span.set_metric Contrib::Redis::Ext::METRIC_PIPELINE_LEN, commands.length
36
42
 
37
- Contrib::Redis::Tags.set_common_tags(redis_config, span, datadog_configuration[:command_args])
43
+ Contrib::Redis::Tags.set_common_tags(client, span, raw_command)
38
44
 
39
- super
45
+ yield
46
+ end
40
47
  end
41
- end
42
48
 
43
- private
49
+ private
44
50
 
45
- def get_command(commands, boolean)
46
- if boolean
47
- Contrib::Redis::Quantize.format_command_args(commands)
48
- else
49
- Contrib::Redis::Quantize.get_verb(commands)
51
+ # Quantizes a single Redis command
52
+ def get_command(command, command_args)
53
+ if command_args
54
+ Contrib::Redis::Quantize.format_command_args(command)
55
+ else
56
+ Contrib::Redis::Quantize.get_verb(command)
57
+ end
50
58
  end
51
- end
52
59
 
53
- def get_pipeline_commands(commands, boolean)
54
- if boolean
55
- commands.map { |c| Contrib::Redis::Quantize.format_command_args(c) }
56
- else
57
- commands.map { |c| Contrib::Redis::Quantize.get_verb(c) }
60
+ # Quantizes a multi-command Redis pipeline execution
61
+ def get_pipeline_commands(commands, command_args)
62
+ list = if command_args
63
+ commands.map { |c| Contrib::Redis::Quantize.format_command_args(c) }
64
+ else
65
+ commands.map { |c| Contrib::Redis::Quantize.get_verb(c) }
66
+ end
67
+
68
+ list.empty? ? '(none)' : list.join("\n")
58
69
  end
59
70
  end
60
71
 
72
+ private
73
+
61
74
  def resolve(redis_config)
62
75
  custom = redis_config.custom[:datadog] || {}
63
76
 
@@ -137,6 +137,12 @@ module Datadog
137
137
  private
138
138
 
139
139
  def instrumented_integrations
140
+ # `instrumented_integrations` method is added only if tracing contrib extensions are required.
141
+ # If tracing is used in manual mode without integrations enabled this method does not exist.
142
+ # CI visibility gem datadog-ci uses Datadog::Tracer without requiring datadog/tracing/contrib folder
143
+ # nor enabling any integrations by default which causes EnvironmentCollector to fail.
144
+ return {} unless Datadog.configuration.tracing.respond_to?(:instrumented_integrations)
145
+
140
146
  Datadog.configuration.tracing.instrumented_integrations
141
147
  end
142
148
 
@@ -49,7 +49,6 @@ module Datadog
49
49
  build_tags(digest).tap do |tags|
50
50
  inject_tags!(tags, data) unless tags.empty?
51
51
  end
52
-
53
52
  data
54
53
  end
55
54
 
@@ -83,19 +83,40 @@ module Datadog
83
83
 
84
84
  extracted_trace_digest = nil
85
85
 
86
- ::Datadog.configuration.tracing.distributed_tracing.propagation_extract_style.each do |style|
86
+ config = ::Datadog.configuration.tracing.distributed_tracing
87
+
88
+ config.propagation_extract_style.each do |style|
87
89
  propagator = @propagation_styles[style]
88
90
  next if propagator.nil?
89
91
 
90
92
  begin
91
- extracted_trace_digest = propagator.extract(data)
93
+ if extracted_trace_digest
94
+ # Return if we are only inspecting the first valid style.
95
+ next if config.propagation_extract_first
96
+
97
+ # Continue parsing styles to find the W3C `tracestate` header, if present.
98
+ # `tracestate` must always be propagated, as it might contain pass-through data that we don't control.
99
+ # @see https://www.w3.org/TR/2021/REC-trace-context-1-20211123/#mutating-the-tracestate-field
100
+ next if style != Configuration::Ext::Distributed::PROPAGATION_STYLE_TRACE_CONTEXT
101
+
102
+ if (tracecontext_digest = propagator.extract(data))
103
+ # Only parse if it represent the same trace as the successfully extracted one
104
+ next unless tracecontext_digest.trace_id == extracted_trace_digest.trace_id
105
+
106
+ # Preserve the `tracestate`
107
+ extracted_trace_digest = extracted_trace_digest.merge(
108
+ trace_state: tracecontext_digest.trace_state,
109
+ trace_state_unknown_fields: tracecontext_digest.trace_state_unknown_fields
110
+ )
111
+ end
112
+ else
113
+ extracted_trace_digest = propagator.extract(data)
114
+ end
92
115
  rescue => e
93
116
  ::Datadog.logger.error(
94
117
  "Error extracting distributed trace data. Cause: #{e} Location: #{Array(e.backtrace).first}"
95
118
  )
96
119
  end
97
-
98
- break if extracted_trace_digest
99
120
  end
100
121
 
101
122
  extracted_trace_digest
@@ -141,6 +141,37 @@ module Datadog
141
141
 
142
142
  freeze
143
143
  end
144
+
145
+ # Creates a copy of this object, modifying the provided fields.
146
+ # @param field_value_pairs [Hash<String>] the fields to be overwritten
147
+ # @return [TraceDigest] returns a copy of this object with the `field_value_pairs` modified
148
+ def merge(field_value_pairs)
149
+ # DEV: Because we want to sometimes freeze the values provided to `TraceDigest`, it's best
150
+ # DEV: to let `#initialize` decide how to handle each field, instead of duplicating that logic here.
151
+ TraceDigest.new(
152
+ **{
153
+ span_id: span_id,
154
+ span_name: span_name,
155
+ span_resource: span_resource,
156
+ span_service: span_service,
157
+ span_type: span_type,
158
+ trace_distributed_tags: trace_distributed_tags,
159
+ trace_hostname: trace_hostname,
160
+ trace_id: trace_id,
161
+ trace_name: trace_name,
162
+ trace_origin: trace_origin,
163
+ trace_process_id: trace_process_id,
164
+ trace_resource: trace_resource,
165
+ trace_runtime_id: trace_runtime_id,
166
+ trace_sampling_priority: trace_sampling_priority,
167
+ trace_service: trace_service,
168
+ trace_distributed_id: trace_distributed_id,
169
+ trace_flags: trace_flags,
170
+ trace_state: trace_state,
171
+ trace_state_unknown_fields: trace_state_unknown_fields,
172
+ }.merge!(field_value_pairs)
173
+ )
174
+ end
144
175
  end
145
176
  end
146
177
  end
@@ -3,7 +3,7 @@
3
3
  module DDTrace
4
4
  module VERSION
5
5
  MAJOR = 1
6
- MINOR = 15
6
+ MINOR = 17
7
7
  PATCH = 0
8
8
  PRE = nil
9
9
  BUILD = nil
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: 1.15.0
4
+ version: 1.17.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Datadog, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-10-09 00:00:00.000000000 Z
11
+ date: 2023-11-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack
@@ -72,14 +72,14 @@ dependencies:
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: 0.2.0
75
+ version: 0.4.0
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: 0.2.0
82
+ version: 0.4.0
83
83
  description: |
84
84
  ddtrace is Datadog's tracing client for Ruby. It is used to trace requests
85
85
  as they flow across web servers, databases and microservices so that developers
@@ -514,6 +514,7 @@ files:
514
514
  - lib/datadog/tracing/contrib/concurrent_ruby/future_patch.rb
515
515
  - lib/datadog/tracing/contrib/concurrent_ruby/integration.rb
516
516
  - lib/datadog/tracing/contrib/concurrent_ruby/patcher.rb
517
+ - lib/datadog/tracing/contrib/concurrent_ruby/promises_future_patch.rb
517
518
  - lib/datadog/tracing/contrib/configurable.rb
518
519
  - lib/datadog/tracing/contrib/configuration/resolver.rb
519
520
  - lib/datadog/tracing/contrib/configuration/resolvers/pattern_resolver.rb
@@ -889,7 +890,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
889
890
  - !ruby/object:Gem::Version
890
891
  version: 2.0.0
891
892
  requirements: []
892
- rubygems_version: 3.4.19
893
+ rubygems_version: 3.4.10
893
894
  signing_key:
894
895
  specification_version: 4
895
896
  summary: Datadog tracing code for your Ruby applications