datadog 2.32.0 → 2.34.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 (113) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +30 -1
  3. data/ext/datadog_profiling_native_extension/clock_id.h +9 -1
  4. data/ext/datadog_profiling_native_extension/clock_id_from_mach.c +73 -0
  5. data/ext/datadog_profiling_native_extension/clock_id_from_pthread.c +1 -1
  6. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +20 -0
  7. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +5 -1
  8. data/ext/datadog_profiling_native_extension/extconf.rb +3 -0
  9. data/ext/datadog_profiling_native_extension/macos_sampler_thread.h +55 -0
  10. data/ext/datadog_profiling_native_extension/stack_recorder.c +3 -9
  11. data/ext/datadog_profiling_native_extension/time_helpers.h +1 -0
  12. data/ext/libdatadog_api/crashtracker.c +2 -0
  13. data/ext/libdatadog_extconf_helpers.rb +1 -1
  14. data/lib/datadog/ai_guard/autoload.rb +10 -0
  15. data/lib/datadog/ai_guard/component.rb +1 -1
  16. data/lib/datadog/ai_guard/contrib/auto_instrument.rb +24 -0
  17. data/lib/datadog/ai_guard/contrib/rack/integration.rb +42 -0
  18. data/lib/datadog/ai_guard/contrib/rack/patcher.rb +26 -0
  19. data/lib/datadog/ai_guard/contrib/rack/request_middleware.rb +83 -0
  20. data/lib/datadog/ai_guard/contrib/rails/integration.rb +41 -0
  21. data/lib/datadog/ai_guard/contrib/rails/patcher.rb +97 -0
  22. data/lib/datadog/ai_guard/evaluation.rb +1 -0
  23. data/lib/datadog/ai_guard/ext.rb +1 -0
  24. data/lib/datadog/ai_guard.rb +8 -0
  25. data/lib/datadog/appsec/component.rb +4 -1
  26. data/lib/datadog/appsec/compressed_json.rb +2 -2
  27. data/lib/datadog/appsec/contrib/aws_lambda/gateway/watcher.rb +75 -0
  28. data/lib/datadog/appsec/contrib/aws_lambda/integration.rb +39 -0
  29. data/lib/datadog/appsec/contrib/aws_lambda/patcher.rb +30 -0
  30. data/lib/datadog/appsec/contrib/aws_lambda/waf_addresses.rb +111 -0
  31. data/lib/datadog/appsec/contrib/rack/ext.rb +1 -1
  32. data/lib/datadog/appsec.rb +1 -0
  33. data/lib/datadog/core/configuration/components.rb +8 -1
  34. data/lib/datadog/core/configuration/settings.rb +16 -1
  35. data/lib/datadog/core/configuration/supported_configurations.rb +12 -0
  36. data/lib/datadog/core/environment/ext.rb +5 -0
  37. data/lib/datadog/core/environment/identity.rb +15 -1
  38. data/lib/datadog/core/environment/process.rb +48 -27
  39. data/lib/datadog/core/environment/socket.rb +13 -0
  40. data/lib/datadog/core/remote/client/capabilities.rb +11 -2
  41. data/lib/datadog/core/remote/transport/http/config.rb +5 -5
  42. data/lib/datadog/core/telemetry/request.rb +0 -2
  43. data/lib/datadog/core/transport/response.rb +1 -1
  44. data/lib/datadog/core/utils/{base64.rb → base64_codec.rb} +3 -2
  45. data/lib/datadog/core/utils/hash.rb +0 -23
  46. data/lib/datadog/core/utils/spawn_monkey_patch.rb +46 -16
  47. data/lib/datadog/data_streams/pathway_context.rb +3 -3
  48. data/lib/datadog/di/code_tracker.rb +43 -22
  49. data/lib/datadog/di/contrib/active_record.rb +6 -2
  50. data/lib/datadog/di/instrumenter.rb +24 -4
  51. data/lib/datadog/di/probe_notification_builder.rb +1 -1
  52. data/lib/datadog/di/remote.rb +4 -4
  53. data/lib/datadog/di/serializer.rb +5 -5
  54. data/lib/datadog/di/utils.rb +42 -14
  55. data/lib/datadog/opentelemetry/configuration/settings.rb +65 -0
  56. data/lib/datadog/opentelemetry/ext.rb +9 -0
  57. data/lib/datadog/opentelemetry/logs.rb +98 -0
  58. data/lib/datadog/opentelemetry/metrics.rb +10 -37
  59. data/lib/datadog/opentelemetry/sdk/configurator.rb +40 -0
  60. data/lib/datadog/opentelemetry/sdk/id_generator.rb +16 -10
  61. data/lib/datadog/opentelemetry/sdk/logs_exporter.rb +37 -0
  62. data/lib/datadog/opentelemetry/signal_configuration.rb +53 -0
  63. data/lib/datadog/opentelemetry.rb +1 -0
  64. data/lib/datadog/profiling/component.rb +0 -1
  65. data/lib/datadog/profiling/stack_recorder.rb +0 -4
  66. data/lib/datadog/symbol_database/component.rb +409 -0
  67. data/lib/datadog/symbol_database/configuration.rb +2 -2
  68. data/lib/datadog/symbol_database/extractor.rb +45 -26
  69. data/lib/datadog/symbol_database/remote.rb +175 -0
  70. data/lib/datadog/symbol_database/scope.rb +16 -12
  71. data/lib/datadog/symbol_database/scope_batcher.rb +288 -0
  72. data/lib/datadog/symbol_database/service_version.rb +15 -6
  73. data/lib/datadog/symbol_database/symbol.rb +6 -3
  74. data/lib/datadog/symbol_database/uploader.rb +65 -8
  75. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +8 -0
  76. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +0 -4
  77. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +0 -4
  78. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +0 -4
  79. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +0 -5
  80. data/lib/datadog/tracing/contrib/dalli/instrumentation.rb +0 -5
  81. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +0 -5
  82. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +0 -5
  83. data/lib/datadog/tracing/contrib/ethon/multi_patch.rb +0 -8
  84. data/lib/datadog/tracing/contrib/excon/middleware.rb +0 -5
  85. data/lib/datadog/tracing/contrib/ext.rb +2 -3
  86. data/lib/datadog/tracing/contrib/faraday/middleware.rb +0 -5
  87. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +0 -5
  88. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +0 -5
  89. data/lib/datadog/tracing/contrib/http/instrumentation.rb +0 -5
  90. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +0 -5
  91. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +0 -5
  92. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +0 -5
  93. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +0 -5
  94. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +0 -5
  95. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +0 -5
  96. data/lib/datadog/tracing/contrib/presto/instrumentation.rb +0 -5
  97. data/lib/datadog/tracing/contrib/racecar/event.rb +0 -5
  98. data/lib/datadog/tracing/contrib/rack/configuration/settings.rb +6 -0
  99. data/lib/datadog/tracing/contrib/rack/ext.rb +27 -0
  100. data/lib/datadog/tracing/contrib/rack/trace_proxy_middleware.rb +117 -1
  101. data/lib/datadog/tracing/contrib/redis/tags.rb +0 -5
  102. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +0 -5
  103. data/lib/datadog/tracing/contrib/sequel/utils.rb +0 -5
  104. data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +0 -5
  105. data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +0 -13
  106. data/lib/datadog/tracing/distributed/trace_context.rb +0 -28
  107. data/lib/datadog/tracing/metadata/ext.rb +3 -0
  108. data/lib/datadog/tracing/span_operation.rb +13 -0
  109. data/lib/datadog/tracing/trace_operation.rb +22 -0
  110. data/lib/datadog/tracing/tracer.rb +7 -3
  111. data/lib/datadog/version.rb +1 -1
  112. metadata +27 -8
  113. data/ext/datadog_profiling_native_extension/clock_id_noop.c +0 -21
@@ -10,18 +10,17 @@ module Datadog
10
10
  #
11
11
  # @api private
12
12
  module Process
13
- # This method returns a key/value part of serialized tags in the format of k1:v1,k2:v2,k3:v3
14
- # @return [String] comma-separated normalized key:value pairs
13
+ # Returns a comma-separated string of normalized key:value pairs.
14
+ # Includes svc.user or svc.auto based on whether the service was explicitly configured.
15
+ # @return [String]
15
16
  def self.serialized
16
- return @serialized if defined?(@serialized)
17
-
18
- @serialized = tags.join(',').freeze
17
+ tags.join(',').freeze
19
18
  end
20
19
 
21
- # This method returns an array in the format ["k1:v1","k2:v2","k3:v3"]
22
- # @return [Array<String>] array of normalized key:value pairs
20
+ # Returns an array of normalized key:value pair strings.
21
+ # Includes svc.user or svc.auto based on whether the service was explicitly configured.
22
+ # @return [Array<String>]
23
23
  def self.tags
24
- return @tags if defined?(@tags)
25
24
  tags = []
26
25
 
27
26
  workdir = TagNormalizer.normalize_process_value(entrypoint_workdir.to_s)
@@ -35,17 +34,44 @@ module Datadog
35
34
 
36
35
  tags << "#{Environment::Ext::TAG_ENTRYPOINT_TYPE}:#{TagNormalizer.normalize(entrypoint_type, remove_digit_start_char: false)}"
37
36
 
38
- rails_application_name = TagNormalizer.normalize_process_value(@rails_application_name.to_s)
39
- tags << "#{Environment::Ext::TAG_RAILS_APPLICATION}:#{rails_application_name}" unless rails_application_name.empty?
37
+ rails_name = TagNormalizer.normalize_process_value(@rails_application_name.to_s)
38
+ tags << "#{Environment::Ext::TAG_RAILS_APPLICATION}:#{rails_name}" unless rails_name.empty?
39
+
40
+ if defined?(@service_user_configured)
41
+ if @service_user_configured
42
+ tags << "#{Environment::Ext::TAG_SVC_USER}:true"
43
+ else
44
+ svc = TagNormalizer.normalize_process_value(@service_name.to_s)
45
+ tags << "#{Environment::Ext::TAG_SVC_AUTO}:#{svc}" unless svc.empty?
46
+ end
47
+ end
48
+
49
+ tags.freeze
50
+ end
51
+
52
+ # Called via after_set on option :service in settings.rb whenever the service value changes.
53
+ # @param name [String] the service name
54
+ # @param user_configured [Boolean] whether the service was explicitly set by the user
55
+ # @return [void]
56
+ def self.set_service(name, user_configured:)
57
+ @service_name = name
58
+ @service_user_configured = user_configured
59
+ end
40
60
 
41
- @tags = tags.freeze
61
+ # Sets the rails application name from other places in code
62
+ # @param name [String] the rails application name
63
+ # @return [void]
64
+ def self.rails_application_name=(name)
65
+ @rails_application_name = name
42
66
  end
43
67
 
44
68
  # Returns the last segment of the working directory of the process
45
69
  # Example: /app/myapp -> myapp
46
70
  # @return [String] the last segment of the working directory
47
71
  def self.entrypoint_workdir
48
- File.basename(Dir.pwd)
72
+ return @entrypoint_workdir if defined?(@entrypoint_workdir)
73
+
74
+ @entrypoint_workdir = File.basename(Dir.pwd)
49
75
  end
50
76
 
51
77
  # Returns the entrypoint type of the process
@@ -55,10 +81,10 @@ module Datadog
55
81
  Environment::Ext::PROCESS_TYPE
56
82
  end
57
83
 
58
- # Returns the last segment of the base directory of the process
84
+ # Returns the basename of the script being run
59
85
  # Example 1: /bin/mybin -> mybin
60
- # Example 2: ruby /test/myapp.rb -> myapp
61
- # @return [String] the last segment of base directory of the script
86
+ # Example 2: ruby /test/myapp.rb -> myapp.rb
87
+ # @return [String] the basename of the script
62
88
  #
63
89
  # @note Determining true entrypoint name is rather complicated. This method
64
90
  # is the initial implementation but it does not produce optimal output in all cases.
@@ -66,12 +92,14 @@ module Datadog
66
92
  # as their entrypoint name.
67
93
  # We might improve the behavior in the future if there is customer demand for it.
68
94
  def self.entrypoint_name
69
- File.basename($0)
95
+ return @entrypoint_name if defined?(@entrypoint_name)
96
+
97
+ @entrypoint_name = File.basename($0)
70
98
  end
71
99
 
72
- # Returns the last segment of the base directory of the process
100
+ # Returns the last segment of the directory containing the script
73
101
  # Example 1: /bin/mybin -> bin
74
- # Example 2: ruby /test/myapp.js -> test
102
+ # Example 2: ruby /test/myapp.rb -> test
75
103
  # @return [String] the last segment of the base directory of the script
76
104
  #
77
105
  # @note As with entrypoint name, determining true entrypoint directory is complicated.
@@ -80,16 +108,9 @@ module Datadog
80
108
  # the entrypoint basedir is `bin` which is not very helpful.
81
109
  # We might improve this in the future if there is customer demand.
82
110
  def self.entrypoint_basedir
83
- File.basename(File.expand_path(File.dirname($0)))
84
- end
111
+ return @entrypoint_basedir if defined?(@entrypoint_basedir)
85
112
 
86
- # Sets the rails application name from other places in code
87
- # @param name [String] the rails application name
88
- # @return [void]
89
- def self.rails_application_name=(name)
90
- @rails_application_name = name
91
- remove_instance_variable(:@tags) if instance_variable_defined?(:@tags)
92
- remove_instance_variable(:@serialized) if instance_variable_defined?(:@serialized)
113
+ @entrypoint_basedir = File.basename(File.expand_path(File.dirname($0)))
93
114
  end
94
115
 
95
116
  private_class_method :entrypoint_workdir, :entrypoint_type, :entrypoint_name, :entrypoint_basedir
@@ -18,6 +18,19 @@ module Datadog
18
18
 
19
19
  @hostname ||= ::Socket.gethostname.freeze
20
20
  end
21
+
22
+ # Returns the resolved hostname when `report_hostname` is enabled:
23
+ # the configured DD_HOSTNAME if set, otherwise the system hostname.
24
+ # Returns nil when `report_hostname` is disabled or no hostname is available.
25
+ def resolved_hostname(settings)
26
+ return nil unless settings.tracing.report_hostname
27
+
28
+ configured = settings.hostname
29
+ return configured if configured && !configured.empty?
30
+
31
+ resolved_hostname = hostname
32
+ resolved_hostname if resolved_hostname && !resolved_hostname.empty?
33
+ end
21
34
  end
22
35
  end
23
36
  end
@@ -1,8 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../../utils/base64'
3
+ require_relative '../../utils/base64_codec'
4
4
  require_relative '../../../appsec/remote'
5
5
  require_relative '../../../tracing/remote'
6
+ require_relative '../../../di/remote'
7
+ require_relative '../../../symbol_database/remote'
6
8
  require_relative '../../../open_feature/remote'
7
9
 
8
10
  module Datadog
@@ -37,6 +39,13 @@ module Datadog
37
39
  register_capabilities(Datadog::DI::Remote.capabilities)
38
40
  register_products(Datadog::DI::Remote.products)
39
41
  register_receivers(Datadog::DI::Remote.receivers(@telemetry))
42
+
43
+ # Symbol Database
44
+ if settings.respond_to?(:symbol_database) && settings.symbol_database.enabled
45
+ register_capabilities(Datadog::SymbolDatabase::Remote.capabilities)
46
+ register_products(Datadog::SymbolDatabase::Remote.products)
47
+ register_receivers(Datadog::SymbolDatabase::Remote.receivers(@telemetry))
48
+ end
40
49
  end
41
50
 
42
51
  if settings.respond_to?(:open_feature) && settings.open_feature.enabled
@@ -68,7 +77,7 @@ module Datadog
68
77
  cap_to_hexs = capabilities.reduce(:|).to_s(16).tap { |s| s.size.odd? && s.prepend('0') }.scan(/\h\h/)
69
78
  binary = cap_to_hexs.each_with_object([]) { |hex, acc| acc << hex }.map { |e| e.to_i(16) }.pack('C*')
70
79
 
71
- Datadog::Core::Utils::Base64.strict_encode64(binary)
80
+ Datadog::Core::Utils::Base64Codec.strict_encode64(binary)
72
81
  end
73
82
  end
74
83
  end
@@ -4,7 +4,7 @@ require 'json'
4
4
 
5
5
  require_relative '../../../transport/http/api/endpoint'
6
6
  require_relative '../../../transport/http/response'
7
- require_relative '../../../utils/base64'
7
+ require_relative '../../../utils/base64_codec'
8
8
  require_relative '../../../utils/truncation'
9
9
 
10
10
  module Datadog
@@ -35,7 +35,7 @@ module Datadog
35
35
 
36
36
  # TODO: these fallbacks should be improved
37
37
  roots = payload[:roots] || []
38
- targets = payload[:targets] || Datadog::Core::Utils::Base64.strict_encode64('{}')
38
+ targets = payload[:targets] || Datadog::Core::Utils::Base64Codec.strict_encode64('{}')
39
39
  target_files = payload[:target_files] || []
40
40
  client_configs = payload[:client_configs] || []
41
41
 
@@ -45,7 +45,7 @@ module Datadog
45
45
  raise TypeError.new(String, root) unless root.is_a?(String)
46
46
 
47
47
  decoded = begin
48
- Datadog::Core::Utils::Base64.strict_decode64(root) # TODO: unprocessed, don't symbolize_names
48
+ Datadog::Core::Utils::Base64Codec.strict_decode64(root) # TODO: unprocessed, don't symbolize_names
49
49
  rescue ArgumentError
50
50
  raise DecodeError.new(:roots, root)
51
51
  end
@@ -65,7 +65,7 @@ module Datadog
65
65
 
66
66
  @targets = begin
67
67
  decoded = begin
68
- Datadog::Core::Utils::Base64.strict_decode64(targets)
68
+ Datadog::Core::Utils::Base64Codec.strict_decode64(targets)
69
69
  rescue ArgumentError
70
70
  raise DecodeError.new(:targets, targets)
71
71
  end
@@ -93,7 +93,7 @@ module Datadog
93
93
  raise TypeError.new(String, raw) unless raw.is_a?(String)
94
94
 
95
95
  content = begin
96
- Datadog::Core::Utils::Base64.strict_decode64(raw)
96
+ Datadog::Core::Utils::Base64Codec.strict_decode64(raw)
97
97
  rescue ArgumentError
98
98
  raise DecodeError.new(:target_files, raw)
99
99
  end
@@ -10,8 +10,6 @@ module Datadog
10
10
  # Module defining methods for collecting metadata for telemetry
11
11
  module Request
12
12
  class << self
13
- using Core::Utils::Hash::Refinement
14
-
15
13
  def build_payload(event, seq_id, debug: false)
16
14
  hash = {
17
15
  api_version: 'v2',
@@ -70,7 +70,7 @@ module Datadog
70
70
  end
71
71
 
72
72
  def to_s
73
- "#{super}, error_type:#{error.class} error:#{error}"
73
+ "#{self.class}, error_type:#{error.class} error:#{error}"
74
74
  end
75
75
 
76
76
  def inspect
@@ -3,8 +3,9 @@
3
3
  module Datadog
4
4
  module Core
5
5
  module Utils
6
- # Helper methods for encoding and decoding base64
7
- module Base64
6
+ # Base64 encoding/decoding without using the `base64` gem,
7
+ # which is no longer a default gem since Ruby 3.4.
8
+ module Base64Codec
8
9
  def self.encode64(bin)
9
10
  [bin].pack('m')
10
11
  end
@@ -3,30 +3,7 @@
3
3
  module Datadog
4
4
  module Core
5
5
  module Utils
6
- # Refinements for {Hash}.
7
6
  module Hash
8
- # This refinement ensures modern rubies are allowed to use newer,
9
- # simpler, and more performant APIs.
10
- module Refinement
11
- # Introduced in Ruby 2.4
12
- unless ::Hash.method_defined?(:compact)
13
- refine ::Hash do
14
- def compact
15
- reject { |_k, v| v.nil? }
16
- end
17
- end
18
- end
19
-
20
- # Introduced in Ruby 2.4
21
- unless ::Hash.method_defined?(:compact!)
22
- refine ::Hash do
23
- def compact!
24
- reject! { |_k, v| v.nil? }
25
- end
26
- end
27
- end
28
- end
29
-
30
7
  # A minimal {Hash} wrapper that provides case-insensitive access
31
8
  # to hash keys, without the overhead of copying the original hash.
32
9
  #
@@ -3,32 +3,62 @@
3
3
  module Datadog
4
4
  module Core
5
5
  module Utils
6
+ # Applies the Process.spawn wrapper used to merge additional environment variables
7
+ # into child processes.
6
8
  module SpawnMonkeyPatch
7
- # @param lineage_envs_provider [#call] returns a Hash of env vars to merge into the child process
8
- def self.apply!(lineage_envs_provider:)
9
- @lineage_envs_provider = lineage_envs_provider
9
+ # @param env_provider [#call] returns a Hash of env vars to merge into the child process
10
+ def self.apply!(env_provider:)
11
+ @env_provider = env_provider
12
+
13
+ # Idempotent: tests, reloads, or repeated Components init must not stack prepends.
14
+ return if ::Process.singleton_class.ancestors.include?(ProcessSpawnPatch)
15
+
10
16
  ::Process.singleton_class.prepend(ProcessSpawnPatch)
11
- true
12
17
  end
13
18
 
19
+ # Prepends `Process.spawn` to merge `env_provider` output into the child's environment hash.
14
20
  module ProcessSpawnPatch
15
- def spawn(*args, **opts)
16
- args.replace(SpawnMonkeyPatch.inject_lineage_envs(args))
17
- super
21
+ # The One and Only Correct Delegation Pattern
22
+ if RUBY_VERSION >= '3'
23
+ def spawn(*args, **kwargs) # steep:ignore DifferentMethodParameterKind
24
+ super(*SpawnMonkeyPatch.inject_envs(args), **kwargs)
25
+ end
26
+ else
27
+ def spawn(*args)
28
+ super(*SpawnMonkeyPatch.inject_envs(args))
29
+ end
30
+ ruby2_keywords :spawn if respond_to?(:ruby2_keywords, true)
18
31
  end
19
32
  end
20
33
 
21
- # Process.spawn(env?, cmd, ...): env is optional first arg (Hash). When present, merge
22
- # runtime_ids into it; when absent, prepend full ENV + runtime_ids so the child inherits both.
23
- def self.inject_lineage_envs(args)
24
- runtime_ids = @lineage_envs_provider.call
25
- env_provided = Hash === args.first
34
+ # Merge the env vars from `env_provider` with the optional env `Hash` from {Process.spawn}.
35
+ #
36
+ # `env` is the first argument when it is a {Hash}; see MRI `spawn([env, ] *args, options)`:
37
+ # https://docs.ruby-lang.org/en/master/Process.html#method-c-spawn
38
+ #
39
+ # When there is **no** leading env Hash, MRI inherits the parent's `ENV`; we prepend only the
40
+ # `env_provider` hash so spawned children see parent env plus injections.
41
+ #
42
+ # When callers pass `unsetenv_others: true`, MRI only forwards the explicitly passed env Hash;
43
+ # replacing a missing hash with DATADOG_ENV.to_h would wrongly carry over parent variables.
44
+ # Prepending only the provider hash preserves `unsetenv_others` semantics.
45
+ #
46
+ # See https://docs.ruby-lang.org/en/master/Process.html#module-Process-label-Environment+Variables+-28-3Aunsetenv_others-29
47
+ #
48
+ # NOTE: `::Hash` (not bare `Hash`) is required because this module is nested under
49
+ # `Datadog::Core::Utils`, and `Datadog::Core::Utils::Hash` exists.
50
+ # Bare `Hash` resolves to that module via Module.nesting, making `Hash === some_hash`
51
+ # silently return `false`. See https://github.com/DataDog/dd-trace-rb/issues/5621.
52
+ def self.inject_envs(args)
53
+ provided_env = @env_provider.call
26
54
 
27
- base_env = env_provided ? args.first : DATADOG_ENV.to_h
28
- env = base_env.merge(runtime_ids)
29
- rest = env_provided ? args.drop(1) : args
55
+ if ::Hash === args.first
56
+ args[0] = args.first.merge(provided_env)
57
+ else
58
+ args.unshift(provided_env)
59
+ end
30
60
 
31
- [env, *rest]
61
+ args
32
62
  end
33
63
  end
34
64
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'stringio'
4
- require_relative '../core/utils/base64'
4
+ require_relative '../core/utils/base64_codec'
5
5
 
6
6
  module Datadog
7
7
  module DataStreams
@@ -34,7 +34,7 @@ module Datadog
34
34
  end
35
35
 
36
36
  def encode_b64
37
- Core::Utils::Base64.strict_encode64(encode)
37
+ Core::Utils::Base64Codec.strict_encode64(encode)
38
38
  end
39
39
 
40
40
  # Decode pathway context from base64 encoded string
@@ -42,7 +42,7 @@ module Datadog
42
42
  return nil unless encoded_ctx && !encoded_ctx.empty?
43
43
 
44
44
  begin
45
- binary_data = Core::Utils::Base64.strict_decode64(encoded_ctx)
45
+ binary_data = Core::Utils::Base64Codec.strict_decode64(encoded_ctx)
46
46
  decode(binary_data)
47
47
  rescue ArgumentError => e
48
48
  # Invalid base64 encoding - may indicate version mismatch or corruption
@@ -249,23 +249,34 @@ module Datadog
249
249
  exact = registry[suffix]
250
250
  return [suffix, exact] if exact
251
251
 
252
- suffix = suffix.dup
253
- loop do
254
- inexact = []
255
- registry.each do |path, iseq|
256
- if Utils.path_matches_suffix?(path, suffix)
257
- inexact << [path, iseq]
252
+ # Normalize Windows-style backslash separators (DEBUG-5111) upfront
253
+ # so the suffix-shortening loop's "/+" regex can strip leading
254
+ # components on probes whose sourceFile uses backslashes.
255
+ suffix = Utils.normalize_windows_separators(suffix)
256
+
257
+ # Per the design comment in utils.rb, attempt case-sensitive
258
+ # matching first (steps 5-6) and only fall back to case-insensitive
259
+ # matching (steps 7-8) when no case-sensitive match is found.
260
+ [false, true].each do |case_insensitive|
261
+ working_suffix = suffix.dup
262
+ loop do
263
+ inexact = []
264
+ registry.each do |path, iseq|
265
+ if Utils.path_matches_suffix?(path, working_suffix, case_insensitive: case_insensitive)
266
+ inexact << [path, iseq]
267
+ end
258
268
  end
269
+ if inexact.length > 1
270
+ raise Error::MultiplePathsMatch, "Multiple paths matched requested suffix"
271
+ end
272
+ if inexact.any?
273
+ return inexact.first
274
+ end
275
+ break unless working_suffix.include?('/')
276
+ working_suffix.sub!(%r{.*/+}, '')
259
277
  end
260
- if inexact.length > 1
261
- raise Error::MultiplePathsMatch, "Multiple paths matched requested suffix"
262
- end
263
- if inexact.any?
264
- return inexact.first
265
- end
266
- return nil unless suffix.include?('/')
267
- suffix.sub!(%r{.*/+}, '')
268
278
  end
279
+ nil
269
280
  end
270
281
  end
271
282
 
@@ -365,15 +376,25 @@ module Datadog
365
376
  # Exact match.
366
377
  return suffix if paths.include?(suffix)
367
378
 
368
- # Suffix match.
369
- suffix = suffix.dup
370
- loop do
371
- matches = paths.select { |p| Utils.path_matches_suffix?(p, suffix) }
372
- raise Error::MultiplePathsMatch, "Multiple paths matched requested suffix" if matches.length > 1
373
- return matches.first if matches.any?
374
- return nil unless suffix.include?('/')
375
- suffix.sub!(%r{.*/+}, '')
379
+ # Normalize Windows-style backslash separators (DEBUG-5111) upfront
380
+ # so the suffix-shortening loop's "/+" regex can strip leading
381
+ # components on probes whose sourceFile uses backslashes.
382
+ suffix = Utils.normalize_windows_separators(suffix)
383
+
384
+ # Suffix match. Per the design comment in utils.rb, attempt
385
+ # case-sensitive matching first (steps 5-6) and only fall back to
386
+ # case-insensitive (steps 7-8) when no case-sensitive match is found.
387
+ [false, true].each do |case_insensitive|
388
+ working_suffix = suffix.dup
389
+ loop do
390
+ matches = paths.select { |p| Utils.path_matches_suffix?(p, working_suffix, case_insensitive: case_insensitive) }
391
+ raise Error::MultiplePathsMatch, "Multiple paths matched requested suffix" if matches.length > 1
392
+ return matches.first if matches.any?
393
+ break unless working_suffix.include?('/')
394
+ working_suffix.sub!(%r{.*/+}, '')
395
+ end
376
396
  end
397
+ nil
377
398
  end
378
399
  end
379
400
  end
@@ -23,7 +23,11 @@ Datadog::DI::Serializer.register(
23
23
  #
24
24
  # +depth+ is the remaining depth for serializing collections and objects.
25
25
  # It should always be an integer.
26
- # Reduce it by 1 when invoking +serialize_value+ on the contents of +value+.
26
+ # Pass it through to +serialize_value+ when the structure you produce
27
+ # represents +value+ directly (a transparent wrapper); +serialize_value+
28
+ # decrements depth itself when it recurses into Array/Hash/object entries.
29
+ # Decrement +depth+ only if your wrapper introduces real additional
30
+ # nesting levels in the output.
27
31
  # This serializer could also potentially do its own depth limiting.
28
32
  #
29
33
  # Steep: steep thinks all of the arguments are nil here
@@ -33,5 +37,5 @@ Datadog::DI::Serializer.register(
33
37
  attributes: value.attributes,
34
38
  new_record: value.new_record?,
35
39
  }
36
- serializer.serialize_value(value_to_serialize, depth: depth - 1, type: value.class)
40
+ serializer.serialize_value(value_to_serialize, depth: depth, type: value.class)
37
41
  end
@@ -106,7 +106,7 @@ module Datadog
106
106
  serializer = self.serializer
107
107
  method_name = probe.method_name
108
108
  loc = begin
109
- cls.instance_method(method_name).source_location
109
+ cls.instance_method(method_name).source_location # steep:ignore ArgumentTypeMismatch
110
110
  rescue NameError
111
111
  # The target method is not defined.
112
112
  # This could be because it will be explicitly defined later
@@ -618,12 +618,32 @@ module Datadog
618
618
  def raise_if_probe_in_loaded_features(probe, line_no, code_tracker)
619
619
  return unless probe.file
620
620
 
621
- # Find the loaded path matching the probe file.
621
+ # Find the loaded path matching the probe file. Case-sensitive
622
+ # matching is attempted first, with case-insensitive matching as a
623
+ # fallback (see the design comment in utils.rb, steps 5-8). Leading
624
+ # directory components are stripped so probes whose sourceFile carries
625
+ # a source-repo prefix that does not exist on disk still resolve to
626
+ # the loaded file — matching the behavior of
627
+ # CodeTracker#iseqs_for_path_suffix.
622
628
  loaded_path = if $LOADED_FEATURES.include?(probe.file)
623
629
  probe.file
624
630
  else
625
631
  # Expensive suffix check.
626
- $LOADED_FEATURES.find { |path| Utils.path_matches_suffix?(path, probe.file) }
632
+ suffix = Utils.normalize_windows_separators(probe.file)
633
+ found = nil #: ::String?
634
+ [false, true].each do |case_insensitive|
635
+ working_suffix = suffix.dup
636
+ loop do
637
+ found = $LOADED_FEATURES.find do |path|
638
+ Utils.path_matches_suffix?(path, working_suffix, case_insensitive: case_insensitive)
639
+ end
640
+ break if found
641
+ break unless working_suffix.include?('/')
642
+ working_suffix.sub!(%r{.*/+}, '')
643
+ end
644
+ break if found
645
+ end
646
+ found
627
647
  end
628
648
 
629
649
  return unless loaded_path
@@ -648,7 +668,7 @@ module Datadog
648
668
 
649
669
  # TODO test that this resolves qualified names e.g. A::B
650
670
  def symbolize_class_name(cls_name)
651
- Object.const_get(cls_name)
671
+ Object.const_get(cls_name) # steep:ignore ArgumentTypeMismatch
652
672
  rescue NameError => exc
653
673
  raise Error::DITargetNotDefined, "Class not defined: #{cls_name}: #{exc.class}: #{exc.message}"
654
674
  end
@@ -369,7 +369,7 @@ module Datadog
369
369
  rescue => exc
370
370
  evaluation_errors << {
371
371
  message: "#{exc.class}: #{exc.message}",
372
- expr: segment.dsl_expr,
372
+ expr: segment.dsl_expr, # steep:ignore NoMethod
373
373
  }
374
374
  '[evaluation error]'
375
375
  end.join
@@ -42,14 +42,14 @@ module Datadog
42
42
  changes.each do |change|
43
43
  case change.type
44
44
  when :insert
45
- add_probe(change.content, component)
45
+ add_probe(change.content, component) # steep:ignore NoMethod
46
46
  when :update
47
47
  # We do not implement updates at the moment, remove the
48
48
  # probe and reinstall.
49
- remove_probe(change.content, component)
50
- add_probe(change.content, component)
49
+ remove_probe(change.content, component) # steep:ignore NoMethod
50
+ add_probe(change.content, component) # steep:ignore NoMethod
51
51
  when :delete
52
- remove_probe(change.previous, component)
52
+ remove_probe(change.previous, component) # steep:ignore NoMethod
53
53
  else
54
54
  # This really should never happen since we generate the
55
55
  # change types in the library.
@@ -255,7 +255,7 @@ module Datadog
255
255
 
256
256
  serialized.update(value: value)
257
257
  when Array
258
- if depth < 0
258
+ if depth <= 0
259
259
  serialized.update(notCapturedReason: "depth")
260
260
  else
261
261
  max = settings.dynamic_instrumentation.max_capture_collection_size
@@ -271,7 +271,7 @@ module Datadog
271
271
  serialized.update(elements: entries)
272
272
  end
273
273
  when Hash
274
- if depth < 0
274
+ if depth <= 0
275
275
  serialized.update(notCapturedReason: "depth")
276
276
  else
277
277
  max = settings.dynamic_instrumentation.max_capture_collection_size
@@ -288,7 +288,7 @@ module Datadog
288
288
  serialized.update(entries: entries)
289
289
  end
290
290
  else
291
- if depth < 0
291
+ if depth <= 0
292
292
  serialized.update(notCapturedReason: "depth")
293
293
  else
294
294
  fields = {}
@@ -369,7 +369,7 @@ module Datadog
369
369
  when String
370
370
  serialize_string_or_symbol_for_message(value)
371
371
  when Symbol
372
- ':' + serialize_string_or_symbol_for_message(value)
372
+ ':' + serialize_string_or_symbol_for_message(value) # steep:ignore ArgumentTypeMismatch
373
373
  when Array
374
374
  return '...' if depth <= 0
375
375
 
@@ -482,7 +482,7 @@ module Datadog
482
482
  if max % 2 == 0
483
483
  upper += 1
484
484
  end
485
- value[0...max / 2 - 1] + '...' + value[upper...length]
485
+ value[0...max / 2 - 1] + '...' + value[upper...length] # steep:ignore NoMethod
486
486
  end
487
487
  else
488
488
  value