ddtrace 1.15.0 → 1.16.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 18896f24a83a74a5ea6f4adfb3baad9fec922f0d3c3e8d087d28805744584d40
4
- data.tar.gz: 173d93233e49670ed94a9d3cd7501f50c596ceb7624293e4c1992513e6121fa6
3
+ metadata.gz: 59aaa9c3c11af9219405c9eee4e318df4cafdaae57eb42c7b5105d46764d3293
4
+ data.tar.gz: ef0d6766221287a4e9d42bf42f43fd5ddd81990ea381e536b5083be8a3f0b94d
5
5
  SHA512:
6
- metadata.gz: 174e727aad6f68b2873e88ac71b5a563075c501f4fbd75cad563735c765962d5753f6025a0a75c257f12a7a89fc83713e3ff249f2e2815803786ed709e35daff
7
- data.tar.gz: bc2bbcc9d601473e871dee51e53745045b37204bb902dd2f29deac2ea4c94bdc7aa6c016eecfd2648ccac13393b612400c9167bdbd115271893e561fcb12a8ce
6
+ metadata.gz: 0201e1fd1e96ea0a076cf88af3824cb02f15862312eb26688db474caec2698bdfee9381b947e4ab278bbaa752aeb8a92df2f2188aa7e4ba988e5cdc31331f138
7
+ data.tar.gz: 2a034e4187f41b85a29d7003fbe1098552cdddf0be663e490ab2ca71c4da40a8ad122f3357b11286af10df988f0891cc8d2d71ca59d9949db790dda6011033d0
data/CHANGELOG.md CHANGED
@@ -2,134 +2,90 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
- ## [1.15.0] - 2023-10-09
6
-
7
- ### Highlights
8
-
9
- #### Timeline view for Profiler beta
10
-
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).
5
+ ## [1.16.0] - 2023-11-03
13
6
 
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 🎉
7
+ **This release includes a security change for the Tracing Redis integration:**
15
8
 
16
- You can use the timeline view both when looking at individual profiles, as well as when scoped to a given trace.
17
-
18
- You can enable it:
19
-
20
- * Using an environment variable by setting `DD_PROFILING_EXPERIMENTAL_TIMELINE_ENABLED=true`
21
- * Or via code by adding to your `Datadog.configure` block:
22
-
23
- ```ruby
24
- Datadog.configure do |c|
25
- # … existing configuration …
26
- c.profiling.advanced.experimental_timeline_enabled = true
27
- end
28
- ```
9
+ 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`.
29
10
 
30
- Give it a try, let us know what you think!
31
-
32
- (Note: We do not recommend enabling this feature prior to 1.15.0!)
33
-
34
- #### google-protobuf dependency is no longer needed by the Profiler
35
-
36
- As of ddtrace version 1.15.0, the `google-protobuf` gem is no longer needed to enable the Profiler.
37
-
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
-
41
- #### Configure blocking responses for AppSec via configuration or Remote Configuration
42
-
43
- As of dd-trace-rb 1.15.0, AppSec supports configuring the blocking response.
11
+ ### Added
44
12
 
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:
13
+ * Tracing: Propagate trace through `Concurrent::Promises.future` ([#1522][])
14
+ * Core: Name `Datadog::Core::Remote::Worker` thread ([#3207][])
48
15
 
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
- ```
16
+ ### Changed
56
17
 
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)
18
+ * Tracing: Redis - Omit command arguments from span.resource by default ([#3235][])
19
+ * Ci-app: Bump `datadog-ci` dependency from 0.2.0 to 0.3.0 ([#3223][])
59
20
 
60
- #### Configure agentless mode for CI visibility
21
+ ### Fixed
61
22
 
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.
23
+ * Appsec: ASM parse response body ([#3153][])
24
+ * Appsec: ASM make sure to append content type and length information ([#3204][])
25
+ * Appsec: Make sure function that checks content-type header value accepts nil content-type header value ([#3234][])
26
+ * Profiling: Shut down profiler if any components failed ([#3197][])
27
+ * Tracing: Fix `ActiveSupport` instrumentation of custom cache stores ([#3206][])
63
28
 
64
- For this, set the following environment variables:
65
- - DD_CIVISIBILITY_AGENTLESS_ENABLED=true
66
- - DD_API_KEY=<your_api_key>
29
+ ## [1.15.0] - 2023-10-09
67
30
 
68
- Additionally, configure which Datadog site you want to send your data to:
69
- - DD_SITE (default: datadoghq.com)
31
+ ### Highlights
70
32
 
71
- You can also enable agentless mode with `Datadog.configure` block:
33
+ * Timeline view for Profiler beta
34
+ * Configure AppSec blocking responses via configuration or Remote Configuration
35
+ * CI visibility to configure with agentless mode
72
36
 
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
- ```
37
+ For more details, check the [release notes](https://github.com/DataDog/dd-trace-rb/releases/tag/v1.15.0)
80
38
 
81
39
  ### 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][])
40
+
85
41
  * 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][])
42
+ * Detect `WebMock` `Cucumber` and `Rails.env` to disable telemetry and remote configuration for development environment ([#3065][], [#3062][], [#3145][])
43
+ * Profiling: Import java-profiler PID controller and port it to C ([#3190][])
93
44
  * 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][])
45
+ * Profiling: Include `ruby vm type` in profiler allocation samples ([#3074][])
46
+ * Tracing: Support `Rack` 3 ([#3132][])
47
+ * Tracing: Support `Opensearch` 3 ([#3189][])
48
+ * Tracing: `grpc` adds `client_error_handler` option ([#3095][])
49
+ * Tracing: Add `async` option for `test_mode` configuration ([#3158][])
98
50
  * Tracing: Implements `_dd.base_service` tag ([#3018][])
99
51
  * Appsec: Allow blocking response template configuration via ENV variables ([#2975][])
100
- * Ci-app: agentless mode ([#3186][])
52
+ * Appsec: ASM API security. Schema extraction ([#3131][], [#3166][], [#3177][])
53
+ * Appsec: Enable configuring blocking response via Remote Configuration ([#3099][])
54
+ * Ci-app: Validate git tags ([#3100][])
55
+ * Ci-app: Add agentless mode ([#3186][])
101
56
 
102
57
  ### 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][])
58
+
59
+ * Appsec: Skip passing waf addresses when the value is empty ([#3188][])
106
60
  * Profiling: Restore support for Ruby 3.3 ([#3167][])
107
- * Bump debase-ruby_core_source dependency to 3.2.2 ([#3163][])
108
61
  * 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
62
  * 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][])
63
+ * Tracing: `dalli` disable memcached command tag by default ([#3171][])
119
64
  * Tracing: Use first valid extracted style for distributed tracing ([#2879][])
65
+ * Tracing: Rename configuration option `on_set` to `after_set` ([#3107][])
66
+ * Tracing: Rename `experimental_default_proc` to `default_proc` ([#3091][])
67
+ * Tracing: Use `peer.service` for sql comment propagation ([#3127][])
68
+ * Ci-app: Fix `Datadog::CI::Environment` to support the new CI specs ([#3080][])
69
+ * Bump `datadog-ci` dependency to 0.2 ([#3186][])
70
+ * Bump `debase-ruby_core_source` dependency to 3.2.2 ([#3163][])
71
+ * Upgrade `libdatadog` 5 ([#3169][], [#3104][])
72
+ * Upgrade `libddwaf-rb` 1.11.0 ([#3087][])
73
+ * Update AppSec rules to 1.8.0 ([#3140][], [#3139][])
120
74
 
121
75
  ### Fixed
76
+
122
77
  * 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][])
78
+ * Profiling: Fix missing endpoint profiling when `request_queuing` is enabled in `rack` instrumentation ([#3109][])
79
+ * Appsec: Span tags reporting the number of WAF failed loaded rules ([#3106][])
125
80
  * 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][])
81
+ * Tracing: Fix `rails.cache.backend` span tag with multiple stores ([#3060][])
128
82
 
129
83
  ### Removed
84
+
130
85
  * 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][])
86
+ * Ci-app: Remove CI module and add a dependency on [`datadog-ci` gem](https://github.com/DataDog/datadog-ci-rb) ([#3128][])
87
+ * Tracing: Remove `depends_on` option from configuration DSL ([#3085][])
88
+ * Tracing: Remove `delegate_to` option from configuration DSL ([#3086][])
133
89
 
134
90
  ## [1.14.0] - 2023-08-24
135
91
 
@@ -2663,7 +2619,9 @@ Release notes: https://github.com/DataDog/dd-trace-rb/releases/tag/v0.3.1
2663
2619
 
2664
2620
  Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
2665
2621
 
2666
- [Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v1.15.0...master
2622
+
2623
+ [Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v1.16.0...master
2624
+ [1.16.0]: https://github.com/DataDog/dd-trace-rb/compare/v1.15.0...v1.16.0
2667
2625
  [1.15.0]: https://github.com/DataDog/dd-trace-rb/compare/v1.14.0...v1.15.0
2668
2626
  [1.14.0]: https://github.com/DataDog/dd-trace-rb/compare/v1.13.1...1.14.0
2669
2627
  [1.13.1]: https://github.com/DataDog/dd-trace-rb/compare/v1.13.0...1.13.1
@@ -3401,6 +3359,7 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
3401
3359
  [#1509]: https://github.com/DataDog/dd-trace-rb/issues/1509
3402
3360
  [#1510]: https://github.com/DataDog/dd-trace-rb/issues/1510
3403
3361
  [#1511]: https://github.com/DataDog/dd-trace-rb/issues/1511
3362
+ [#1522]: https://github.com/DataDog/dd-trace-rb/issues/1522
3404
3363
  [#1523]: https://github.com/DataDog/dd-trace-rb/issues/1523
3405
3364
  [#1524]: https://github.com/DataDog/dd-trace-rb/issues/1524
3406
3365
  [#1529]: https://github.com/DataDog/dd-trace-rb/issues/1529
@@ -3842,12 +3801,14 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
3842
3801
  [#3127]: https://github.com/DataDog/dd-trace-rb/issues/3127
3843
3802
  [#3128]: https://github.com/DataDog/dd-trace-rb/issues/3128
3844
3803
  [#3131]: https://github.com/DataDog/dd-trace-rb/issues/3131
3804
+ [#3132]: https://github.com/DataDog/dd-trace-rb/issues/3132
3845
3805
  [#3139]: https://github.com/DataDog/dd-trace-rb/issues/3139
3846
3806
  [#3140]: https://github.com/DataDog/dd-trace-rb/issues/3140
3847
3807
  [#3145]: https://github.com/DataDog/dd-trace-rb/issues/3145
3848
3808
  [#3148]: https://github.com/DataDog/dd-trace-rb/issues/3148
3849
3809
  [#3150]: https://github.com/DataDog/dd-trace-rb/issues/3150
3850
3810
  [#3152]: https://github.com/DataDog/dd-trace-rb/issues/3152
3811
+ [#3153]: https://github.com/DataDog/dd-trace-rb/issues/3153
3851
3812
  [#3158]: https://github.com/DataDog/dd-trace-rb/issues/3158
3852
3813
  [#3162]: https://github.com/DataDog/dd-trace-rb/issues/3162
3853
3814
  [#3163]: https://github.com/DataDog/dd-trace-rb/issues/3163
@@ -3863,6 +3824,13 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
3863
3824
  [#3188]: https://github.com/DataDog/dd-trace-rb/issues/3188
3864
3825
  [#3189]: https://github.com/DataDog/dd-trace-rb/issues/3189
3865
3826
  [#3190]: https://github.com/DataDog/dd-trace-rb/issues/3190
3827
+ [#3197]: https://github.com/DataDog/dd-trace-rb/issues/3197
3828
+ [#3204]: https://github.com/DataDog/dd-trace-rb/issues/3204
3829
+ [#3206]: https://github.com/DataDog/dd-trace-rb/issues/3206
3830
+ [#3207]: https://github.com/DataDog/dd-trace-rb/issues/3207
3831
+ [#3223]: https://github.com/DataDog/dd-trace-rb/issues/3223
3832
+ [#3234]: https://github.com/DataDog/dd-trace-rb/issues/3234
3833
+ [#3235]: https://github.com/DataDog/dd-trace-rb/issues/3235
3866
3834
  [@AdrianLC]: https://github.com/AdrianLC
3867
3835
  [@Azure7111]: https://github.com/Azure7111
3868
3836
  [@BabyGroot]: https://github.com/BabyGroot
@@ -188,6 +188,12 @@ module Datadog
188
188
  end
189
189
  end
190
190
  end
191
+
192
+ option :parse_response_body do |o|
193
+ o.type :bool
194
+ o.env 'DD_API_SECURITY_PARSE_RESPONSE_BODY'
195
+ o.default true
196
+ end
191
197
  end
192
198
  end
193
199
  end
@@ -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
@@ -19,9 +19,55 @@ module Datadog
19
19
  @scope = scope
20
20
  end
21
21
 
22
+ def parsed_body
23
+ return unless Datadog.configuration.appsec.parse_response_body
24
+
25
+ unless body.instance_of?(Array)
26
+ Datadog.logger.debug do
27
+ "Response body type unsupported: #{body.class}"
28
+ end
29
+ return
30
+ end
31
+
32
+ return unless json_content_type?
33
+
34
+ result = ''.dup
35
+ all_body_parts_are_string = true
36
+
37
+ body.each do |body_part|
38
+ if body_part.is_a?(String)
39
+ result.concat(body_part)
40
+ else
41
+ all_body_parts_are_string = false
42
+ break
43
+ end
44
+ end
45
+
46
+ return unless all_body_parts_are_string
47
+
48
+ begin
49
+ JSON.parse(result)
50
+ rescue JSON::ParserError => e
51
+ Datadog.logger.debug { "Failed to parse response body. Error #{e.class}. Message #{e.message}" }
52
+ nil
53
+ end
54
+ end
55
+
22
56
  def response
23
57
  @response ||= ::Rack::Response.new(body, status, headers)
24
58
  end
59
+
60
+ private
61
+
62
+ VALID_JSON_TYPES = [
63
+ 'application/json',
64
+ 'text/json'
65
+ ].freeze
66
+
67
+ def json_content_type?
68
+ content_type = headers['content-type']
69
+ VALID_JSON_TYPES.include?(content_type)
70
+ end
25
71
  end
26
72
  end
27
73
  end
@@ -10,6 +10,7 @@ module Datadog
10
10
  ADDRESSES = [
11
11
  'response.status',
12
12
  'response.headers',
13
+ 'response.body',
13
14
  ].freeze
14
15
  private_constant :ADDRESSES
15
16
 
@@ -17,6 +18,7 @@ module Datadog
17
18
  catch(:block) do
18
19
  op.publish('response.status', gateway_response.status)
19
20
  op.publish('response.headers', gateway_response.headers)
21
+ op.publish('response.body', gateway_response.parsed_body)
20
22
 
21
23
  nil
22
24
  end
@@ -29,6 +31,7 @@ module Datadog
29
31
  response_status = values[0]
30
32
  response_headers = values[1]
31
33
  response_headers_no_cookies = response_headers.dup.tap { |h| h.delete('set-cookie') }
34
+ response_body = values[2]
32
35
 
33
36
  waf_args = {
34
37
  'server.response.status' => response_status.to_s,
@@ -36,6 +39,8 @@ module Datadog
36
39
  'server.response.headers.no_cookies' => response_headers_no_cookies,
37
40
  }
38
41
 
42
+ waf_args['server.response.body'] = response_body if response_body
43
+
39
44
  waf_timeout = Datadog.configuration.appsec.waf_timeout
40
45
  result = waf_context.run(waf_args, waf_timeout)
41
46
 
@@ -66,11 +66,21 @@ module Datadog
66
66
  request_return = AppSec::Response.negotiate(env, blocked_event.last[:actions]).to_rack if blocked_event
67
67
  end
68
68
 
69
+ if request_return[2].respond_to?(:to_ary)
70
+ # Following the Rack specification. The response body should only call :each once.
71
+ # Calling :to_ary returns an array with identical content as the produced when calling :each
72
+ # replacing request_return[2] with that new value allow us to safely operate on the response body.
73
+ # On Gateway::Response#parsed_body we might iterate over the reposne body using :each
74
+ # https://github.com/rack/rack/blob/main/SPEC.rdoc#enumerable-body-
75
+ consumed_body = request_return[2].to_ary
76
+ request_return[2] = consumed_body if consumed_body
77
+ end
78
+
69
79
  gateway_response = Gateway::Response.new(
70
80
  request_return[2],
71
81
  request_return[0],
72
82
  request_return[1],
73
- scope: scope
83
+ scope: scope,
74
84
  )
75
85
 
76
86
  _response_return, response_response = Instrumentation.gateway.push('rack.response', gateway_response)
@@ -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
@@ -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
@@ -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.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
 
@@ -3,7 +3,7 @@
3
3
  module DDTrace
4
4
  module VERSION
5
5
  MAJOR = 1
6
- MINOR = 15
6
+ MINOR = 16
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.16.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-03 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.3.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.3.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.1
893
894
  signing_key:
894
895
  specification_version: 4
895
896
  summary: Datadog tracing code for your Ruby applications