ddtrace 1.11.1 → 1.12.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +73 -1
  3. data/ext/ddtrace_profiling_native_extension/NativeExtensionDesign.md +6 -4
  4. data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +34 -16
  5. data/ext/ddtrace_profiling_native_extension/extconf.rb +19 -3
  6. data/ext/ddtrace_profiling_native_extension/native_extension_helpers.rb +2 -2
  7. data/ext/ddtrace_profiling_native_extension/private_vm_api_access.c +38 -4
  8. data/lib/datadog/appsec/assets/waf_rules/recommended.json +489 -133
  9. data/lib/datadog/appsec/assets/waf_rules/strict.json +2 -47
  10. data/lib/datadog/appsec/configuration/settings.rb +2 -10
  11. data/lib/datadog/appsec/configuration.rb +3 -9
  12. data/lib/datadog/appsec/contrib/rack/ext.rb +0 -1
  13. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +17 -3
  14. data/lib/datadog/appsec/contrib/rack/gateway/response.rb +3 -3
  15. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +27 -45
  16. data/lib/datadog/appsec/contrib/rack/integration.rb +0 -5
  17. data/lib/datadog/appsec/contrib/rack/reactive/request.rb +7 -1
  18. data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +1 -1
  19. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +34 -26
  20. data/lib/datadog/appsec/contrib/rails/ext.rb +0 -1
  21. data/lib/datadog/appsec/contrib/rails/framework.rb +1 -13
  22. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +9 -27
  23. data/lib/datadog/appsec/contrib/rails/integration.rb +0 -5
  24. data/lib/datadog/appsec/contrib/rails/patcher.rb +1 -1
  25. data/lib/datadog/appsec/contrib/sinatra/ext.rb +0 -1
  26. data/lib/datadog/appsec/contrib/sinatra/framework.rb +1 -13
  27. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +18 -36
  28. data/lib/datadog/appsec/contrib/sinatra/integration.rb +0 -5
  29. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +5 -4
  30. data/lib/datadog/appsec/event.rb +37 -37
  31. data/lib/datadog/appsec/ext.rb +1 -0
  32. data/lib/datadog/appsec/extensions.rb +2 -6
  33. data/lib/datadog/appsec/monitor/gateway/watcher.rb +9 -28
  34. data/lib/datadog/appsec/processor/rule_merger.rb +13 -7
  35. data/lib/datadog/appsec/processor.rb +0 -45
  36. data/lib/datadog/appsec/remote.rb +6 -0
  37. data/lib/datadog/appsec/response.rb +13 -9
  38. data/lib/datadog/appsec/scope.rb +61 -0
  39. data/lib/datadog/appsec.rb +6 -0
  40. data/lib/datadog/ci/ext/environment.rb +40 -4
  41. data/lib/datadog/core/configuration/settings.rb +74 -14
  42. data/lib/datadog/core/configuration.rb +5 -1
  43. data/lib/datadog/core/remote/client/capabilities.rb +1 -1
  44. data/lib/datadog/core/remote/client.rb +5 -1
  45. data/lib/datadog/core/telemetry/collector.rb +2 -1
  46. data/lib/datadog/core/telemetry/v1/dependency.rb +2 -1
  47. data/lib/datadog/kit/appsec/events.rb +58 -13
  48. data/lib/datadog/kit/identity.rb +29 -10
  49. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +2 -0
  50. data/lib/datadog/profiling/component.rb +69 -29
  51. data/lib/datadog/profiling.rb +2 -1
  52. data/lib/datadog/tracing/buffer.rb +0 -1
  53. data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +9 -1
  54. data/lib/datadog/tracing/contrib/aws/ext.rb +11 -1
  55. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +7 -0
  56. data/lib/datadog/tracing/contrib/aws/parsed_context.rb +4 -0
  57. data/lib/datadog/tracing/contrib/aws/service/base.rb +16 -0
  58. data/lib/datadog/tracing/contrib/aws/service/dynamodb.rb +22 -0
  59. data/lib/datadog/tracing/contrib/aws/service/eventbridge.rb +22 -0
  60. data/lib/datadog/tracing/contrib/aws/service/kinesis.rb +32 -0
  61. data/lib/datadog/tracing/contrib/aws/service/s3.rb +22 -0
  62. data/lib/datadog/tracing/contrib/aws/service/sns.rb +30 -0
  63. data/lib/datadog/tracing/contrib/aws/service/sqs.rb +27 -0
  64. data/lib/datadog/tracing/contrib/aws/service/states.rb +40 -0
  65. data/lib/datadog/tracing/contrib/aws/services.rb +18 -0
  66. data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +6 -1
  67. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +5 -2
  68. data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +6 -1
  69. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +4 -2
  70. data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +6 -1
  71. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +5 -2
  72. data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +6 -1
  73. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +5 -2
  74. data/lib/datadog/tracing/contrib/patcher.rb +0 -1
  75. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +6 -1
  76. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +5 -2
  77. data/lib/datadog/tracing/contrib/presto/configuration/settings.rb +6 -1
  78. data/lib/datadog/tracing/contrib/presto/instrumentation.rb +4 -2
  79. data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +10 -2
  80. data/lib/datadog/tracing/contrib/racecar/configuration/settings.rb +9 -1
  81. data/lib/datadog/tracing/contrib/racecar/event.rb +3 -1
  82. data/lib/datadog/tracing/contrib/rack/middlewares.rb +3 -1
  83. data/lib/datadog/tracing/contrib/redis/configuration/settings.rb +6 -1
  84. data/lib/datadog/tracing/contrib/redis/tags.rb +4 -1
  85. data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +6 -1
  86. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +4 -1
  87. data/lib/datadog/tracing/contrib/roda/patcher.rb +1 -1
  88. data/lib/datadog/tracing/contrib/sequel/database.rb +4 -1
  89. data/lib/datadog/tracing/contrib/sequel/dataset.rb +4 -1
  90. data/lib/datadog/tracing/contrib/sequel/utils.rb +4 -1
  91. data/lib/datadog/tracing/contrib/status_code_matcher.rb +0 -1
  92. data/lib/datadog/tracing/correlation.rb +0 -1
  93. data/lib/datadog/tracing/distributed/headers/ext.rb +1 -1
  94. data/lib/datadog/tracing/event.rb +0 -2
  95. data/lib/datadog/tracing/pipeline.rb +0 -2
  96. data/lib/datadog/tracing/runtime/metrics.rb +0 -2
  97. data/lib/datadog/tracing/sampling/rate_by_service_sampler.rb +0 -1
  98. data/lib/datadog/tracing/sampling/rate_sampler.rb +0 -2
  99. data/lib/datadog/tracing/sampling/rule.rb +0 -2
  100. data/lib/datadog/tracing/sampling/rule_sampler.rb +0 -2
  101. data/lib/datadog/tracing/span_operation.rb +0 -1
  102. data/lib/datadog/tracing/sync_writer.rb +0 -2
  103. data/lib/datadog/tracing/trace_operation.rb +0 -1
  104. data/lib/datadog/tracing/tracer.rb +0 -1
  105. data/lib/datadog/tracing/workers/trace_writer.rb +0 -1
  106. data/lib/datadog/tracing/workers.rb +0 -2
  107. data/lib/datadog/tracing/writer.rb +0 -2
  108. data/lib/ddtrace/version.rb +1 -1
  109. metadata +18 -19
  110. data/lib/datadog/appsec/contrib/configuration/settings.rb +0 -20
  111. data/lib/datadog/appsec/contrib/rack/configuration/settings.rb +0 -22
  112. data/lib/datadog/appsec/contrib/rails/configuration/settings.rb +0 -22
  113. data/lib/datadog/appsec/contrib/sinatra/configuration/settings.rb +0 -22
@@ -36,30 +36,34 @@ module Datadog
36
36
  Response.new(
37
37
  status: 403,
38
38
  headers: { 'Content-Type' => content_type },
39
- body: [Datadog::AppSec::Assets.blocked(format: FORMAT_MAP[content_type])]
39
+ body: [Datadog::AppSec::Assets.blocked(format: CONTENT_TYPE_TO_FORMAT[content_type])]
40
40
  )
41
41
  end
42
42
 
43
43
  private
44
44
 
45
- FORMAT_MAP = {
46
- 'text/plain' => :text,
47
- 'text/html' => :html,
45
+ CONTENT_TYPE_TO_FORMAT = {
48
46
  'application/json' => :json,
47
+ 'text/html' => :html,
48
+ 'text/plain' => :text,
49
49
  }.freeze
50
50
 
51
- DEFAULT_CONTENT_TYPE = 'text/plain'
51
+ DEFAULT_CONTENT_TYPE = 'application/json'
52
52
 
53
53
  def content_type(env)
54
54
  return DEFAULT_CONTENT_TYPE unless env.key?('HTTP_ACCEPT')
55
55
 
56
- accepted = env['HTTP_ACCEPT'].split(',').map { |m| Utils::HTTP::MediaRange.new(m) }.sort!.reverse!
56
+ accept_types = env['HTTP_ACCEPT'].split(',').map(&:strip)
57
57
 
58
- accepted.each_with_object(DEFAULT_CONTENT_TYPE) do |range, _default|
59
- match = FORMAT_MAP.keys.find { |type| range === type }
58
+ accepted = accept_types.map { |m| Utils::HTTP::MediaRange.new(m) }.sort!.reverse!
60
59
 
61
- return match if match
60
+ accepted.each do |range|
61
+ type_match = CONTENT_TYPE_TO_FORMAT.keys.find { |type| range === type }
62
+
63
+ return type_match if type_match
62
64
  end
65
+
66
+ DEFAULT_CONTENT_TYPE
63
67
  rescue Datadog::AppSec::Utils::HTTP::MediaRange::ParseError
64
68
  DEFAULT_CONTENT_TYPE
65
69
  end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'processor'
4
+
5
+ module Datadog
6
+ module AppSec
7
+ # Capture context essential to consistently call processor and report via traces
8
+ class Scope
9
+ attr_reader :trace, :service_entry_span, :processor_context
10
+
11
+ def initialize(trace, service_entry_span, processor_context)
12
+ @trace = trace
13
+ @service_entry_span = service_entry_span
14
+ @processor_context = processor_context
15
+ end
16
+
17
+ def finalize
18
+ @processor_context.finalize
19
+ end
20
+
21
+ class << self
22
+ def activate_scope(trace, service_entry_span, processor)
23
+ raise ActiveScopeError, 'another scope is active, nested scopes are not supported' if active_scope
24
+
25
+ context = Datadog::AppSec::Processor::Context.new(processor)
26
+
27
+ self.active_scope = new(trace, service_entry_span, context)
28
+ end
29
+
30
+ def deactivate_scope
31
+ raise InactiveScopeError, 'no scope is active, nested scopes are not supported' unless active_scope
32
+
33
+ scope = active_scope
34
+
35
+ reset_active_scope
36
+
37
+ scope.finalize
38
+ end
39
+
40
+ def active_scope
41
+ Thread.current[:datadog_appsec_active_scope]
42
+ end
43
+
44
+ private
45
+
46
+ def active_scope=(scope)
47
+ raise ArgumentError, 'not a Datadog::AppSec::Scope' unless scope.instance_of?(Scope)
48
+
49
+ Thread.current[:datadog_appsec_active_scope] = scope
50
+ end
51
+
52
+ def reset_active_scope
53
+ Thread.current[:datadog_appsec_active_scope] = nil
54
+ end
55
+ end
56
+
57
+ class InactiveScopeError < StandardError; end
58
+ class ActiveScopeError < StandardError; end
59
+ end
60
+ end
61
+ end
@@ -2,6 +2,8 @@
2
2
 
3
3
  require_relative 'appsec/configuration'
4
4
  require_relative 'appsec/extensions'
5
+ require_relative 'appsec/scope'
6
+ require_relative 'appsec/ext'
5
7
 
6
8
  module Datadog
7
9
  # Namespace for Datadog AppSec instrumentation
@@ -13,6 +15,10 @@ module Datadog
13
15
  Datadog.configuration.appsec.enabled
14
16
  end
15
17
 
18
+ def active_scope
19
+ Datadog::AppSec::Scope.active_scope
20
+ end
21
+
16
22
  def processor
17
23
  appsec_component = components.appsec
18
24
 
@@ -19,6 +19,8 @@ module Datadog
19
19
  TAG_PROVIDER_NAME = 'ci.provider.name'
20
20
  TAG_STAGE_NAME = 'ci.stage.name'
21
21
  TAG_WORKSPACE_PATH = 'ci.workspace_path'
22
+ TAG_NODE_LABELS = 'ci.node.labels'
23
+ TAG_NODE_NAME = 'ci.node.name'
22
24
  TAG_CI_ENV_VARS = '_dd.ci.env_vars'
23
25
 
24
26
  PROVIDERS = [
@@ -33,7 +35,8 @@ module Datadog
33
35
  ['JENKINS_URL', :extract_jenkins],
34
36
  ['TEAMCITY_VERSION', :extract_teamcity],
35
37
  ['TRAVIS', :extract_travis],
36
- ['BITRISE_BUILD_SLUG', :extract_bitrise]
38
+ ['BITRISE_BUILD_SLUG', :extract_bitrise],
39
+ ['CF_BUILD_ID', :extract_codefresh]
37
40
  ].freeze
38
41
 
39
42
  module_function
@@ -196,7 +199,7 @@ module Datadog
196
199
  end
197
200
 
198
201
  def extract_buildkite(env)
199
- {
202
+ tags = {
200
203
  Core::Git::Ext::TAG_BRANCH => env['BUILDKITE_BRANCH'],
201
204
  Core::Git::Ext::TAG_COMMIT_SHA => env['BUILDKITE_COMMIT'],
202
205
  Core::Git::Ext::TAG_REPOSITORY_URL => env['BUILDKITE_REPO'],
@@ -211,11 +214,21 @@ module Datadog
211
214
  Core::Git::Ext::TAG_COMMIT_AUTHOR_NAME => env['BUILDKITE_BUILD_AUTHOR'],
212
215
  Core::Git::Ext::TAG_COMMIT_AUTHOR_EMAIL => env['BUILDKITE_BUILD_AUTHOR_EMAIL'],
213
216
  Core::Git::Ext::TAG_COMMIT_MESSAGE => env['BUILDKITE_MESSAGE'],
217
+ TAG_NODE_NAME => env['BUILDKITE_AGENT_ID'],
214
218
  TAG_CI_ENV_VARS => {
215
219
  'BUILDKITE_BUILD_ID' => env['BUILDKITE_BUILD_ID'],
216
220
  'BUILDKITE_JOB_ID' => env['BUILDKITE_JOB_ID']
217
221
  }.to_json
218
222
  }
223
+
224
+ extra_tags = env
225
+ .select { |key| key.start_with?('BUILDKITE_AGENT_META_DATA_') }
226
+ .map { |key, value| "#{key.to_s.sub('BUILDKITE_AGENT_META_DATA_', '').downcase}:#{value}" }
227
+ .sort_by(&:length)
228
+
229
+ tags[TAG_NODE_LABELS] = extra_tags.to_json unless extra_tags.empty?
230
+
231
+ tags
219
232
  end
220
233
 
221
234
  def extract_circle_ci(env)
@@ -274,7 +287,6 @@ module Datadog
274
287
  def extract_gitlab(env)
275
288
  commit_author_name, commit_author_email = extract_name_email(env['CI_COMMIT_AUTHOR'])
276
289
 
277
- url = env['CI_PIPELINE_URL']
278
290
  {
279
291
  Core::Git::Ext::TAG_BRANCH => env['CI_COMMIT_REF_NAME'],
280
292
  Core::Git::Ext::TAG_COMMIT_SHA => env['CI_COMMIT_SHA'],
@@ -289,9 +301,11 @@ module Datadog
289
301
  TAG_PIPELINE_ID => env['CI_PIPELINE_ID'],
290
302
  TAG_PIPELINE_NAME => env['CI_PROJECT_PATH'],
291
303
  TAG_PIPELINE_NUMBER => env['CI_PIPELINE_IID'],
292
- TAG_PIPELINE_URL => (url.gsub(%r{/-/pipelines/}, '/pipelines/') if url),
304
+ TAG_PIPELINE_URL => env['CI_PIPELINE_URL'],
293
305
  TAG_PROVIDER_NAME => 'gitlab',
294
306
  TAG_WORKSPACE_PATH => env['CI_PROJECT_DIR'],
307
+ TAG_NODE_LABELS => env['CI_RUNNER_TAGS'],
308
+ TAG_NODE_NAME => env['CI_RUNNER_ID'],
295
309
  Core::Git::Ext::TAG_COMMIT_MESSAGE => env['CI_COMMIT_MESSAGE'],
296
310
  TAG_CI_ENV_VARS => {
297
311
  'CI_PROJECT_URL' => env['CI_PROJECT_URL'],
@@ -308,6 +322,9 @@ module Datadog
308
322
  name = name.gsub("/#{normalize_ref(branch)}", '') if branch
309
323
  name = name.split('/').reject { |v| v.nil? || v.include?('=') }.join('/')
310
324
  end
325
+
326
+ node_labels = env['NODE_LABELS'].split.to_json unless env['NODE_LABELS'].nil?
327
+
311
328
  {
312
329
  Core::Git::Ext::TAG_BRANCH => branch,
313
330
  Core::Git::Ext::TAG_COMMIT_SHA => env['GIT_COMMIT'],
@@ -319,6 +336,8 @@ module Datadog
319
336
  TAG_PIPELINE_URL => env['BUILD_URL'],
320
337
  TAG_PROVIDER_NAME => 'jenkins',
321
338
  TAG_WORKSPACE_PATH => env['WORKSPACE'],
339
+ TAG_NODE_LABELS => node_labels,
340
+ TAG_NODE_NAME => env['NODE_NAME'],
322
341
  TAG_CI_ENV_VARS => {
323
342
  'DD_CUSTOM_TRACE_ID' => env['DD_CUSTOM_TRACE_ID']
324
343
  }.to_json
@@ -380,6 +399,23 @@ module Datadog
380
399
  }
381
400
  end
382
401
 
402
+ def extract_codefresh(env)
403
+ branch, tag = branch_or_tag(env['CF_BRANCH'])
404
+
405
+ {
406
+ TAG_PROVIDER_NAME => 'codefresh',
407
+ TAG_PIPELINE_ID => env['CF_BUILD_ID'],
408
+ TAG_PIPELINE_NAME => env['CF_PIPELINE_NAME'],
409
+ TAG_PIPELINE_URL => env['CF_BUILD_URL'],
410
+ TAG_JOB_NAME => env['CF_STEP_NAME'],
411
+ Core::Git::Ext::TAG_BRANCH => branch,
412
+ Core::Git::Ext::TAG_TAG => tag,
413
+ TAG_CI_ENV_VARS => {
414
+ 'CF_BUILD_ID' => env['CF_BUILD_ID'],
415
+ }.to_json
416
+ }
417
+ end
418
+
383
419
  def extract_user_defined_git(env)
384
420
  {
385
421
  Core::Git::Ext::TAG_REPOSITORY_URL => env[Core::Git::Ext::ENV_REPOSITORY_URL],
@@ -154,6 +154,9 @@ module Datadog
154
154
  # @default `DD_ENV` environment variable, otherwise `nil`
155
155
  # @return [String,nil]
156
156
  option :env do |o|
157
+ # DEV-2.0: Remove this conversion for symbol.
158
+ o.setter { |v| v.to_s if v }
159
+
157
160
  # NOTE: env also gets set as a side effect of tags. See the WORKAROUND note in #initialize for details.
158
161
  o.default { ENV.fetch(Core::Environment::Ext::ENV_ENVIRONMENT, nil) }
159
162
  o.lazy
@@ -202,12 +205,23 @@ module Datadog
202
205
 
203
206
  # @public_api
204
207
  settings :advanced do
208
+ # @deprecated This setting is ignored when CPU Profiling 2.0 is in use, and will be removed on dd-trace-rb 2.0.
209
+ #
205
210
  # This should never be reduced, as it can cause the resulting profiles to become biased.
206
211
  # The default should be enough for most services, allowing 16 threads to be sampled around 30 times
207
212
  # per second for a 60 second period.
208
- #
209
- # @deprecated This setting is ignored when CPU Profiling 2.0 is in use.
210
- option :max_events, default: 32768
213
+ option :max_events do |o|
214
+ o.default 32768
215
+ o.on_set do |value|
216
+ if value != 32768
217
+ Datadog.logger.warn(
218
+ 'The profiling.advanced.max_events setting has been deprecated for removal. It no longer does ' \
219
+ 'anything unless you the `force_enable_legacy_profiler` option is in use. ' \
220
+ 'Please remove it from your Datadog.configure block.'
221
+ )
222
+ end
223
+ end
224
+ end
211
225
 
212
226
  # Controls the maximum number of frames for each thread sampled. Can be tuned to avoid omitted frames in the
213
227
  # produced profiles. Increasing this may increase the overhead of profiling.
@@ -250,28 +264,39 @@ module Datadog
250
264
  end
251
265
  end
252
266
 
253
- # Forces enabling the new CPU Profiling 2.0 profiler (see ddtrace release notes for more details).
254
- #
255
- # Note that setting this to "false" (or not setting it) will not prevent the new profiler from
256
- # being automatically used.
257
- # This option will be deprecated for removal once the legacy profiler is removed.
267
+ # @deprecated No longer does anything, and will be removed on dd-trace-rb 2.0.
258
268
  #
259
- # @default `DD_PROFILING_FORCE_ENABLE_NEW` environment variable, otherwise `false`
269
+ # This was used prior to the GA of the new CPU Profiling 2.0 profiler. Using CPU Profiling 2.0 is now the
270
+ # default and this doesn't do anything.
260
271
  option :force_enable_new_profiler do |o|
261
- o.default { env_to_bool('DD_PROFILING_FORCE_ENABLE_NEW', false) }
262
- o.lazy
272
+ o.on_set do
273
+ Datadog.logger.warn(
274
+ 'The profiling.advanced.force_enable_new_profiler setting has been deprecated for removal and no ' \
275
+ 'longer does anything. Please remove it from your Datadog.configure block.'
276
+ )
277
+ end
263
278
  end
264
279
 
265
- # Forces enabling the *legacy* (non-CPU Profiling 2.0 profiler) even when it would otherwise NOT be enabled.
280
+ # @deprecated Will be removed for dd-trace-rb 2.0.
266
281
  #
267
- # Temporarily added to ease migration to the new CPU Profiling 2.0 profiler, and will be removed soon.
282
+ # Forces enabling the *legacy* non-CPU Profiling 2.0 profiler.
268
283
  # Do not use unless instructed to by support.
269
- # This option will be deprecated for removal once the legacy profiler is removed.
270
284
  #
271
285
  # @default `DD_PROFILING_FORCE_ENABLE_LEGACY` environment variable, otherwise `false`
272
286
  option :force_enable_legacy_profiler do |o|
273
287
  o.default { env_to_bool('DD_PROFILING_FORCE_ENABLE_LEGACY', false) }
274
288
  o.lazy
289
+ o.on_set do |value|
290
+ if value
291
+ Datadog.logger.warn(
292
+ 'The profiling.advanced.force_enable_legacy_profiler setting has been deprecated for removal. ' \
293
+ 'Do not use unless instructed to by support. ' \
294
+ 'If you needed to use it due to incompatibilities with the CPU Profiling 2.0 profiler, consider ' \
295
+ 'using the profiling.advanced.no_signals_workaround_enabled setting instead. ' \
296
+ 'See <https://dtdg.co/ruby-profiler-troubleshooting> for details.'
297
+ )
298
+ end
299
+ end
275
300
  end
276
301
 
277
302
  # Forces enabling of profiling of time/resources spent in Garbage Collection.
@@ -317,6 +342,30 @@ module Datadog
317
342
  o.default { env_to_bool('DD_PROFILING_SKIP_MYSQL2_CHECK', false) }
318
343
  o.lazy
319
344
  end
345
+
346
+ # The profiler gathers data by sending `SIGPROF` unix signals to Ruby application threads.
347
+ #
348
+ # Sending `SIGPROF` is a common profiling approach, and may cause system calls from native
349
+ # extensions/libraries to be interrupted with a system
350
+ # [EINTR error code.](https://man7.org/linux/man-pages/man7/signal.7.html#:~:text=Interruption%20of%20system%20calls%20and%20library%20functions%20by%20signal%20handlers)
351
+ # Rarely, native extensions or libraries called by them may have missing or incorrect error handling for the
352
+ # `EINTR` error code.
353
+ #
354
+ # The "no signals" workaround, when enabled, enables an alternative mode for the profiler where it does not
355
+ # send `SIGPROF` unix signals. The downside of this approach is that the profiler data will have lower
356
+ # quality.
357
+ #
358
+ # This workaround is automatically enabled when gems that are known to have issues handling
359
+ # `EINTR` error codes are detected. If you suspect you may be seeing an issue due to the profiler's use of
360
+ # signals, you can try manually enabling this mode as a fallback.
361
+ # Please also report these issues to us on <https://github.com/DataDog/dd-trace-rb/issues/new>, so we can
362
+ # work with the gem authors to fix them!
363
+ #
364
+ # @default `DD_PROFILING_NO_SIGNALS_WORKAROUND_ENABLED` environment variable as a boolean, otherwise `:auto`
365
+ option :no_signals_workaround_enabled do |o|
366
+ o.default { env_to_bool('DD_PROFILING_NO_SIGNALS_WORKAROUND_ENABLED', :auto) }
367
+ o.lazy
368
+ end
320
369
  end
321
370
 
322
371
  # @public_api
@@ -353,6 +402,9 @@ module Datadog
353
402
  # @default `DD_SERVICE` environment variable, otherwise the program name (e.g. `'ruby'`, `'rails'`, `'pry'`)
354
403
  # @return [String]
355
404
  option :service do |o|
405
+ # DEV-2.0: Remove this conversion for symbol.
406
+ o.setter { |v| v.to_s if v }
407
+
356
408
  # NOTE: service also gets set as a side effect of tags. See the WORKAROUND note in #initialize for details.
357
409
  o.default { ENV.fetch(Core::Environment::Ext::ENV_SERVICE, Core::Environment::Ext::FALLBACK_SERVICE_NAME) }
358
410
  o.lazy
@@ -497,6 +549,14 @@ module Datadog
497
549
  o.default { env_to_float(Core::Remote::Ext::ENV_POLL_INTERVAL_SECONDS, 5.0) }
498
550
  o.lazy
499
551
  end
552
+
553
+ # Declare service name to bind to remote configuration. Use when
554
+ # DD_SERVICE does not match the correct integration for which remote
555
+ # configuration applies.
556
+ #
557
+ # @default `nil`.
558
+ # @return [String,nil]
559
+ option :service
500
560
  end
501
561
 
502
562
  # TODO: Tracing should manage its own settings.
@@ -208,7 +208,11 @@ module Datadog
208
208
  # Used internally to ensure a clean environment between test runs.
209
209
  def reset!
210
210
  safely_synchronize do |write_components|
211
- @components.shutdown! if components?
211
+ if components?
212
+ @components.shutdown!
213
+ @temp_logger = nil # Reset to ensure instance and log level are reset for next run
214
+ end
215
+
212
216
  write_components.call(nil)
213
217
  configuration.reset!
214
218
  end
@@ -23,7 +23,7 @@ module Datadog
23
23
  private
24
24
 
25
25
  def register(settings)
26
- if settings.appsec.enabled
26
+ if settings.respond_to?(:appsec) && settings.appsec.enabled
27
27
  register_capabilities(Datadog::AppSec::Remote.capabilities)
28
28
  register_products(Datadog::AppSec::Remote.products)
29
29
  register_receivers(Datadog::AppSec::Remote.receivers)
@@ -136,7 +136,7 @@ module Datadog
136
136
  runtime_id: Core::Environment::Identity.id,
137
137
  language: Core::Environment::Identity.lang,
138
138
  tracer_version: tracer_version_semver2,
139
- service: Datadog.configuration.service,
139
+ service: service_name,
140
140
  env: Datadog.configuration.env,
141
141
  tags: client_tracer_tags,
142
142
  }
@@ -167,6 +167,10 @@ module Datadog
167
167
  }
168
168
  end
169
169
 
170
+ def service_name
171
+ Datadog.configuration.remote.service || Datadog.configuration.service
172
+ end
173
+
170
174
  def tracer_version_semver2
171
175
  @tracer_version_semver2 ||= Core::Environment::Identity.tracer_version_semver2
172
176
  end
@@ -56,7 +56,8 @@ module Datadog
56
56
  def dependencies
57
57
  Gem.loaded_specs.collect do |name, loaded_gem|
58
58
  Datadog::Core::Telemetry::V1::Dependency.new(
59
- name: name, version: loaded_gem.version.to_s, hash: loaded_gem.hash.to_s
59
+ # `hash` should be used when `version` is not available
60
+ name: name, version: loaded_gem.version.to_s, hash: nil
60
61
  )
61
62
  end
62
63
  end
@@ -17,9 +17,10 @@ module Datadog
17
17
 
18
18
  # @param name [String] Module name
19
19
  # @param version [String] Version of resolved module
20
- # @param hash [String] Dependency hash
20
+ # @param hash [String] Dependency hash, in case `version` is not available
21
21
  def initialize(name:, version: nil, hash: nil)
22
22
  raise ArgumentError, ERROR_NIL_NAME_MESSAGE if name.nil?
23
+ raise ArgumentError, 'if both :version and :hash exist, use :version only' if version && hash
23
24
 
24
25
  @hash = hash
25
26
  @name = name
@@ -14,38 +14,68 @@ module Datadog
14
14
  #
15
15
  # This method is experimental and may change in the future.
16
16
  #
17
- # @param trace [TraceOperation] Trace to attach data to.
17
+ # @param trace [TraceOperation] Trace to attach data to. Defaults to
18
+ # active trace.
19
+ # @param span [SpanOperation] Span to attach data to. Defaults to
20
+ # active span on trace. Note that this should be a service entry span.
21
+ # When AppSec is enabled, the expected span and trace are automatically
22
+ # used as defaults.
18
23
  # @param user [Hash<Symbol, String>] User information to pass to
19
24
  # Datadog::Kit::Identity.set_user. Must contain at least :id as key.
20
25
  # @param others [Hash<String || Symbol, String>] Additional free-form
21
26
  # event information to attach to the trace.
22
- def self.track_login_success(trace, user:, **others)
23
- track(LOGIN_SUCCESS_EVENT, trace, **others)
27
+ def self.track_login_success(trace = nil, span = nil, user:, **others)
28
+ if (appsec_scope = Datadog::AppSec.active_scope)
29
+ trace = appsec_scope.trace
30
+ span = appsec_scope.service_entry_span
31
+ end
32
+
33
+ trace ||= Datadog::Tracing.active_trace
34
+ span ||= trace.active_span || Datadog::Tracing.active_span
35
+
36
+ raise ArgumentError, "span #{span.span_id} does not belong to trace #{trace.id}" if trace.id != span.trace_id
37
+
38
+ track(LOGIN_SUCCESS_EVENT, trace, span, **others)
24
39
 
25
40
  user_options = user.dup
26
41
  user_id = user_options.delete(:id)
27
42
 
28
43
  raise ArgumentError, 'missing required key: :user => { :id }' if user_id.nil?
29
44
 
30
- Kit::Identity.set_user(trace, id: user_id, **user_options)
45
+ Kit::Identity.set_user(trace, span, id: user_id, **user_options)
31
46
  end
32
47
 
33
48
  # Attach login failure event information to the trace
34
49
  #
35
50
  # This method is experimental and may change in the future.
36
51
  #
37
- # @param trace [TraceOperation] Trace to attach data to.
52
+ # @param trace [TraceOperation] Trace to attach data to. Defaults to
53
+ # active trace.
54
+ # @param span [SpanOperation] Span to attach data to. Defaults to
55
+ # active span on trace. Note that this should be a service entry span.
56
+ # When AppSec is enabled, the expected span and trace are automatically
57
+ # used as defaults.
38
58
  # @param user_id [String] User id that attempted login
39
59
  # @param user_exists [bool] Whether the user id that did a login attempt exists.
40
60
  # @param others [Hash<String || Symbol, String>] Additional free-form
41
61
  # event information to attach to the trace.
42
- def self.track_login_failure(trace, user_id:, user_exists:, **others)
43
- track(LOGIN_FAILURE_EVENT, trace, **others)
62
+ def self.track_login_failure(trace = nil, span = nil, user_id:, user_exists:, **others)
63
+ if (appsec_scope = Datadog::AppSec.active_scope)
64
+ trace = appsec_scope.trace
65
+ span = appsec_scope.service_entry_span
66
+ end
67
+
68
+ trace ||= Datadog::Tracing.active_trace
69
+ span ||= trace.active_span || Datadog::Tracing.active_span
70
+
71
+ raise ArgumentError, "span #{span.span_id} does not belong to trace #{trace.id}" if trace.id != span.trace_id
72
+
73
+ track(LOGIN_FAILURE_EVENT, trace, span, **others)
44
74
 
45
75
  raise ArgumentError, 'user_id cannot be nil' if user_id.nil?
46
76
 
47
- trace.set_tag('appsec.events.users.login.failure.usr.id', user_id)
48
- trace.set_tag('appsec.events.users.login.failure.usr.exists', user_exists)
77
+ span.set_tag('appsec.events.users.login.failure.usr.id', user_id)
78
+ span.set_tag('appsec.events.users.login.failure.usr.exists', user_exists)
49
79
  end
50
80
 
51
81
  # Attach custom event information to the trace
@@ -53,17 +83,32 @@ module Datadog
53
83
  # This method is experimental and may change in the future.
54
84
  #
55
85
  # @param event [String] Mandatory. Event code.
56
- # @param trace [TraceOperation] Trace to attach data to.
86
+ # @param trace [TraceOperation] Trace to attach data to. Defaults to
87
+ # active trace.
88
+ # @param span [SpanOperation] Span to attach data to. Defaults to
89
+ # active span on trace. Note that this should be a service entry span.
90
+ # When AppSec is enabled, the expected span and trace are automatically
91
+ # used as defaults.
57
92
  # @param others [Hash<Symbol, String>] Additional free-form
58
93
  # event information to attach to the trace. Key must not
59
94
  # be :track.
60
- def self.track(event, trace, **others)
61
- trace.set_tag("appsec.events.#{event}.track", 'true')
95
+ def self.track(event, trace = nil, span = nil, **others)
96
+ if (appsec_scope = Datadog::AppSec.active_scope)
97
+ trace = appsec_scope.trace
98
+ span = appsec_scope.service_entry_span
99
+ end
100
+
101
+ trace ||= Datadog::Tracing.active_trace
102
+ span ||= trace.active_span || Datadog::Tracing.active_span
103
+
104
+ raise ArgumentError, "span #{span.span_id} does not belong to trace #{trace.id}" if trace.id != span.trace_id
105
+
106
+ span.set_tag("appsec.events.#{event}.track", 'true')
62
107
 
63
108
  others.each do |k, v|
64
109
  raise ArgumentError, 'key cannot be :track' if k.to_sym == :track
65
110
 
66
- trace.set_tag("appsec.events.#{event}.#{k}", v) unless v.nil?
111
+ span.set_tag("appsec.events.#{event}.#{k}", v) unless v.nil?
67
112
  end
68
113
 
69
114
  trace.keep!
@@ -8,7 +8,12 @@ module Datadog
8
8
  module Identity
9
9
  # Attach user information to the trace
10
10
  #
11
- # @param trace [TraceOperation] Trace to attach data to.
11
+ # @param trace [TraceOperation] Trace to attach data to. Defaults to
12
+ # active trace.
13
+ # @param span [SpanOperation] Span to attach data to. Defaults to
14
+ # active span on trace. Note that this should be a service entry span.
15
+ # When AppSec is enabled, the expected span and trace are automatically
16
+ # used as defaults.
12
17
  # @param id [String] Mandatory. Username or client id extracted
13
18
  # from the access token or Authorization header in the inbound request
14
19
  # from outside the system.
@@ -29,7 +34,10 @@ module Datadog
29
34
  #
30
35
  # rubocop:disable Metrics/CyclomaticComplexity
31
36
  # rubocop:disable Metrics/PerceivedComplexity
32
- def self.set_user(trace, id:, email: nil, name: nil, session_id: nil, role: nil, scope: nil, **others)
37
+ # rubocop:disable Metrics/AbcSize
38
+ def self.set_user(
39
+ trace = nil, span = nil, id:, email: nil, name: nil, session_id: nil, role: nil, scope: nil, **others
40
+ )
33
41
  raise ArgumentError, 'missing required key: :id' if id.nil?
34
42
 
35
43
  # enforce types
@@ -45,24 +53,35 @@ module Datadog
45
53
  raise TypeError, "#{k.inspect} must be a String" unless v.nil? || v.is_a?(String)
46
54
  end
47
55
 
56
+ if (appsec_scope = Datadog::AppSec.active_scope)
57
+ trace = appsec_scope.trace
58
+ span = appsec_scope.service_entry_span
59
+ end
60
+
61
+ trace ||= Datadog::Tracing.active_trace
62
+ span ||= trace.active_span || Datadog::Tracing.active_span
63
+
64
+ raise ArgumentError, "span #{span.span_id} does not belong to trace #{trace.id}" if trace.id != span.trace_id
65
+
48
66
  # set tags once data is known consistent
49
67
 
50
- trace.set_tag('usr.id', id)
51
- trace.set_tag('usr.email', email) unless email.nil?
52
- trace.set_tag('usr.name', name) unless name.nil?
53
- trace.set_tag('usr.session_id', session_id) unless session_id.nil?
54
- trace.set_tag('usr.role', role) unless role.nil?
55
- trace.set_tag('usr.scope', scope) unless scope.nil?
68
+ span.set_tag('usr.id', id)
69
+ span.set_tag('usr.email', email) unless email.nil?
70
+ span.set_tag('usr.name', name) unless name.nil?
71
+ span.set_tag('usr.session_id', session_id) unless session_id.nil?
72
+ span.set_tag('usr.role', role) unless role.nil?
73
+ span.set_tag('usr.scope', scope) unless scope.nil?
56
74
 
57
75
  others.each do |k, v|
58
- trace.set_tag("usr.#{k}", v) unless v.nil?
76
+ span.set_tag("usr.#{k}", v) unless v.nil?
59
77
  end
60
78
 
61
- if Datadog.configuration.appsec.enabled
79
+ if appsec_scope
62
80
  user = ::Datadog::AppSec::Instrumentation::Gateway::User.new(id)
63
81
  ::Datadog::AppSec::Instrumentation.gateway.push('identity.set_user', user)
64
82
  end
65
83
  end
84
+ # rubocop:enable Metrics/AbcSize
66
85
  # rubocop:enable Metrics/PerceivedComplexity
67
86
  # rubocop:enable Metrics/CyclomaticComplexity
68
87
  end
@@ -20,6 +20,7 @@ module Datadog
20
20
  endpoint_collection_enabled:,
21
21
  gc_profiling_enabled:,
22
22
  allocation_counting_enabled:,
23
+ no_signals_workaround_enabled:,
23
24
  thread_context_collector: ThreadContext.new(
24
25
  recorder: recorder,
25
26
  max_frames: max_frames,
@@ -43,6 +44,7 @@ module Datadog
43
44
  gc_profiling_enabled,
44
45
  idle_sampling_helper,
45
46
  allocation_counting_enabled,
47
+ no_signals_workaround_enabled,
46
48
  dynamic_sampling_rate_enabled,
47
49
  )
48
50
  @worker_thread = nil