datadog 2.17.0 → 2.19.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 (108) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +90 -1
  3. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +63 -56
  4. data/ext/datadog_profiling_native_extension/collectors_stack.c +263 -76
  5. data/ext/datadog_profiling_native_extension/collectors_stack.h +20 -3
  6. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +62 -12
  7. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +1 -0
  8. data/ext/datadog_profiling_native_extension/extconf.rb +7 -0
  9. data/ext/datadog_profiling_native_extension/heap_recorder.c +239 -363
  10. data/ext/datadog_profiling_native_extension/heap_recorder.h +4 -6
  11. data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +22 -0
  12. data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +8 -5
  13. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +38 -26
  14. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +6 -4
  15. data/ext/datadog_profiling_native_extension/ruby_helpers.c +1 -13
  16. data/ext/datadog_profiling_native_extension/ruby_helpers.h +3 -11
  17. data/ext/datadog_profiling_native_extension/stack_recorder.c +154 -57
  18. data/ext/libdatadog_api/extconf.rb +2 -2
  19. data/ext/libdatadog_api/library_config.c +54 -12
  20. data/ext/libdatadog_api/library_config.h +6 -0
  21. data/ext/libdatadog_api/process_discovery.c +2 -7
  22. data/ext/libdatadog_extconf_helpers.rb +1 -1
  23. data/lib/datadog/appsec/api_security/lru_cache.rb +9 -2
  24. data/lib/datadog/appsec/api_security/route_extractor.rb +71 -0
  25. data/lib/datadog/appsec/api_security/sampler.rb +59 -0
  26. data/lib/datadog/appsec/api_security.rb +14 -0
  27. data/lib/datadog/appsec/assets/waf_rules/recommended.json +257 -85
  28. data/lib/datadog/appsec/assets/waf_rules/strict.json +10 -78
  29. data/lib/datadog/appsec/component.rb +30 -54
  30. data/lib/datadog/appsec/configuration/settings.rb +60 -2
  31. data/lib/datadog/appsec/context.rb +6 -6
  32. data/lib/datadog/appsec/contrib/devise/tracking_middleware.rb +1 -1
  33. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +27 -16
  34. data/lib/datadog/appsec/instrumentation/gateway/argument.rb +1 -1
  35. data/lib/datadog/appsec/processor/rule_loader.rb +5 -6
  36. data/lib/datadog/appsec/remote.rb +15 -55
  37. data/lib/datadog/appsec/security_engine/engine.rb +194 -0
  38. data/lib/datadog/appsec/security_engine/runner.rb +10 -11
  39. data/lib/datadog/appsec.rb +4 -7
  40. data/lib/datadog/core/configuration/agent_settings.rb +52 -0
  41. data/lib/datadog/core/configuration/agent_settings_resolver.rb +1 -43
  42. data/lib/datadog/core/configuration/components.rb +2 -4
  43. data/lib/datadog/core/configuration/option.rb +9 -9
  44. data/lib/datadog/core/configuration/settings.rb +42 -10
  45. data/lib/datadog/core/configuration/stable_config.rb +1 -2
  46. data/lib/datadog/core/crashtracking/tag_builder.rb +4 -22
  47. data/lib/datadog/core/process_discovery/tracer_memfd.rb +15 -0
  48. data/lib/datadog/core/process_discovery.rb +5 -1
  49. data/lib/datadog/core/remote/configuration/repository.rb +12 -0
  50. data/lib/datadog/core/tag_builder.rb +56 -0
  51. data/lib/datadog/core/telemetry/component.rb +8 -4
  52. data/lib/datadog/core/telemetry/event/app_client_configuration_change.rb +1 -0
  53. data/lib/datadog/core/telemetry/event/app_started.rb +148 -40
  54. data/lib/datadog/core/telemetry/logger.rb +5 -4
  55. data/lib/datadog/core/telemetry/logging.rb +11 -5
  56. data/lib/datadog/core/transport/http/adapters/net.rb +17 -2
  57. data/lib/datadog/core/transport/http/builder.rb +2 -2
  58. data/lib/datadog/core/transport/http/env.rb +8 -0
  59. data/lib/datadog/core/utils.rb +7 -0
  60. data/lib/datadog/di/instrumenter.rb +48 -5
  61. data/lib/datadog/di/probe_notification_builder.rb +37 -42
  62. data/lib/datadog/di/probe_notifier_worker.rb +9 -1
  63. data/lib/datadog/di/serializer.rb +10 -2
  64. data/lib/datadog/di/transport/http/input.rb +10 -0
  65. data/lib/datadog/di/transport/input.rb +10 -2
  66. data/lib/datadog/di.rb +0 -6
  67. data/lib/datadog/kit/appsec/events/v2.rb +195 -0
  68. data/lib/datadog/profiling/collectors/code_provenance.rb +17 -8
  69. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +6 -0
  70. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -0
  71. data/lib/datadog/profiling/collectors/info.rb +41 -0
  72. data/lib/datadog/profiling/collectors/thread_context.rb +16 -1
  73. data/lib/datadog/profiling/component.rb +8 -9
  74. data/lib/datadog/profiling/exporter.rb +9 -3
  75. data/lib/datadog/profiling/ext.rb +0 -12
  76. data/lib/datadog/profiling/http_transport.rb +2 -2
  77. data/lib/datadog/profiling/profiler.rb +2 -0
  78. data/lib/datadog/profiling/scheduler.rb +2 -1
  79. data/lib/datadog/profiling/sequence_tracker.rb +44 -0
  80. data/lib/datadog/profiling/stack_recorder.rb +5 -5
  81. data/lib/datadog/profiling/tag_builder.rb +7 -37
  82. data/lib/datadog/profiling/tasks/setup.rb +2 -0
  83. data/lib/datadog/profiling.rb +1 -0
  84. data/lib/datadog/single_step_instrument.rb +9 -0
  85. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +15 -0
  86. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +19 -12
  87. data/lib/datadog/tracing/contrib/action_pack/ext.rb +2 -0
  88. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +7 -1
  89. data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +13 -0
  90. data/lib/datadog/tracing/contrib/lograge/patcher.rb +4 -2
  91. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +16 -6
  92. data/lib/datadog/tracing/contrib/rails/patcher.rb +4 -1
  93. data/lib/datadog/tracing/contrib/rails/runner.rb +61 -40
  94. data/lib/datadog/tracing/contrib/sidekiq/ext.rb +1 -0
  95. data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +5 -2
  96. data/lib/datadog/tracing/diagnostics/environment_logger.rb +3 -1
  97. data/lib/datadog/tracing/span_event.rb +1 -1
  98. data/lib/datadog/tracing/span_operation.rb +22 -0
  99. data/lib/datadog/tracing/sync_writer.rb +1 -1
  100. data/lib/datadog/tracing/trace_operation.rb +12 -4
  101. data/lib/datadog/tracing/tracer.rb +6 -2
  102. data/lib/datadog/version.rb +1 -1
  103. data/lib/datadog.rb +7 -0
  104. metadata +14 -10
  105. data/lib/datadog/appsec/assets/waf_rules/processors.json +0 -321
  106. data/lib/datadog/appsec/assets/waf_rules/scanners.json +0 -1023
  107. data/lib/datadog/appsec/processor/rule_merger.rb +0 -171
  108. data/lib/datadog/appsec/processor.rb +0 -107
@@ -237,6 +237,10 @@ module Datadog
237
237
  @path = path
238
238
  @previous = previous
239
239
  end
240
+
241
+ def type
242
+ :delete
243
+ end
240
244
  end
241
245
 
242
246
  # Insert change
@@ -247,6 +251,10 @@ module Datadog
247
251
  @path = path
248
252
  @content = content
249
253
  end
254
+
255
+ def type
256
+ :insert
257
+ end
250
258
  end
251
259
 
252
260
  # Update change
@@ -258,6 +266,10 @@ module Datadog
258
266
  @content = content
259
267
  @previous = previous
260
268
  end
269
+
270
+ def type
271
+ :update
272
+ end
261
273
  end
262
274
  end
263
275
 
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'environment/socket'
4
+ require_relative 'environment/identity'
5
+ require_relative 'environment/git'
6
+
7
+ module Datadog
8
+ module Core
9
+ # This module builds a hash of tags.
10
+ #
11
+ # When changing or adding the tags, make sure they are kept in sync with
12
+ # https://docs.google.com/spreadsheets/d/1LOGMf4c4Avbtn36uZ2SWvhIGKRPLM1BoWkUP4JYj7hA/
13
+ # (Datadog internal link).
14
+ #
15
+ # @api private
16
+ module TagBuilder
17
+ def self.fixed_environment_tags
18
+ @fixed_environment_tags ||= {
19
+ 'language' => Environment::Identity.lang,
20
+ 'runtime' => Environment::Identity.lang, # Known to be duplicated from language above
21
+ 'runtime_engine' => Environment::Identity.lang_engine,
22
+ 'runtime_platform' => Environment::Identity.lang_platform,
23
+ 'runtime_version' => Environment::Identity.lang_version,
24
+ 'library_version' => Environment::Identity.gem_datadog_version,
25
+ 'git.repository_url' => Environment::Git.git_repository_url,
26
+ 'git.commit.sha' => Environment::Git.git_commit_sha,
27
+ }.compact.freeze
28
+ end
29
+
30
+ def self.tags(settings)
31
+ # Note that user tags get overwritten by our tags, and also
32
+ # that user tags do not get compacted (nil values are sent as
33
+ # empty strings).
34
+ settings.tags.merge(fixed_environment_tags).merge({
35
+ # Hostname can possibly change during application runtime.
36
+ 'host' => Environment::Socket.hostname,
37
+ # Runtime ID changes upon a fork.
38
+ 'runtime-id' => Environment::Identity.id,
39
+ # Process ID changes upon a fork.
40
+ 'process_id' => Process.pid.to_s,
41
+ # Unified service tagging.
42
+ 'env' => settings.env,
43
+ 'service' => settings.service,
44
+ 'version' => settings.version,
45
+ }.compact)
46
+ end
47
+
48
+ def self.serialize_tags(tags)
49
+ # DEV: Should there be some sort of escaping done here?
50
+ tags.map do |key, value|
51
+ "#{key}:#{value}"
52
+ end.join(',')
53
+ end
54
+ end
55
+ end
56
+ end
@@ -62,7 +62,9 @@ module Datadog
62
62
  return unless @enabled
63
63
 
64
64
  @transport = if settings.telemetry.agentless_enabled
65
- agent_settings = Core::Configuration::AgentlessSettingsResolver.call(
65
+ # We don't touch the `agent_settings` since we still want the telemetry payloads to refer to the original
66
+ # settings, even though the telemetry itself may be using a different path.
67
+ telemetry_specific_agent_settings = Core::Configuration::AgentlessSettingsResolver.call(
66
68
  settings,
67
69
  host_prefix: 'instrumentation-telemetry-intake',
68
70
  url_override: settings.telemetry.agentless_url_override,
@@ -70,7 +72,7 @@ module Datadog
70
72
  logger: logger,
71
73
  )
72
74
  Telemetry::Transport::HTTP.agentless_telemetry(
73
- agent_settings: agent_settings,
75
+ agent_settings: telemetry_specific_agent_settings,
74
76
  logger: logger,
75
77
  # api_key should have already validated to be
76
78
  # not nil by +build+ method above.
@@ -96,6 +98,8 @@ module Datadog
96
98
  logger: logger,
97
99
  shutdown_timeout: settings.telemetry.shutdown_timeout_seconds,
98
100
  )
101
+
102
+ @agent_settings = agent_settings
99
103
  end
100
104
 
101
105
  def disable!
@@ -107,9 +111,9 @@ module Datadog
107
111
  return if !@enabled
108
112
 
109
113
  initial_event = if initial_event_is_change
110
- Event::SynthAppClientConfigurationChange.new
114
+ Event::SynthAppClientConfigurationChange.new(agent_settings: @agent_settings)
111
115
  else
112
- Event::AppStarted.new
116
+ Event::AppStarted.new(agent_settings: @agent_settings)
113
117
  end
114
118
 
115
119
  @worker.start(initial_event)
@@ -37,6 +37,7 @@ module Datadog
37
37
  }
38
38
  end
39
39
 
40
+ # DEV: This seems unnecessary (we send the state of sca_enabled for each remote config change)
40
41
  unless config.dig('appsec', 'sca_enabled').nil?
41
42
  res << {
42
43
  name: 'appsec.sca_enabled',
@@ -8,6 +8,10 @@ module Datadog
8
8
  module Event
9
9
  # Telemetry class for the 'app-started' event
10
10
  class AppStarted < Base
11
+ def initialize(agent_settings:)
12
+ @agent_settings = agent_settings
13
+ end
14
+
11
15
  def type
12
16
  'app-started'
13
17
  end
@@ -25,7 +29,7 @@ module Datadog
25
29
  private
26
30
 
27
31
  def products
28
- # @type var products: Hash[Symbol, Hash[Symbol, Object]]
32
+ # @type var products: Hash[Symbol, Hash[Symbol, Hash[Symbol, String | Integer] | bool | nil]]
29
33
  products = {
30
34
  appsec: {
31
35
  enabled: Datadog::AppSec.enabled?,
@@ -64,6 +68,7 @@ module Datadog
64
68
  tracing.partial_flush.min_spans_threshold
65
69
  tracing.report_hostname
66
70
  tracing.sampling.rate_limit
71
+ apm.tracing.enabled
67
72
  ].freeze
68
73
 
69
74
  # standard:disable Metrics/AbcSize
@@ -72,23 +77,70 @@ module Datadog
72
77
  config = Datadog.configuration
73
78
  seq_id = Event.configuration_sequence.next
74
79
 
80
+ # tracing.writer_options.buffer_size and tracing.writer_options.flush_interval have the same origin.
81
+ writer_option_origin = get_telemetry_origin(config, 'tracing.writer_options')
82
+
75
83
  list = [
84
+ # Only set using env var as of June 2025
76
85
  conf_value('DD_GIT_REPOSITORY_URL', Core::Environment::Git.git_repository_url, seq_id, 'env_var'),
77
86
  conf_value('DD_GIT_COMMIT_SHA', Core::Environment::Git.git_commit_sha, seq_id, 'env_var'),
78
87
 
79
- conf_value('DD_AGENT_HOST', config.agent.host, seq_id),
80
- conf_value('DD_AGENT_TRANSPORT', agent_transport(config), seq_id),
81
- conf_value('DD_TRACE_SAMPLE_RATE', to_value(config.tracing.sampling.default_rate), seq_id),
88
+ # Set by the customer application (eg. `require 'datadog/auto_instrument'`)
89
+ conf_value(
90
+ 'tracing.auto_instrument.enabled',
91
+ !defined?(Datadog::AutoInstrument::LOADED).nil?,
92
+ seq_id,
93
+ 'code'
94
+ ),
95
+ conf_value(
96
+ 'tracing.opentelemetry.enabled',
97
+ !defined?(Datadog::OpenTelemetry::LOADED).nil?,
98
+ seq_id,
99
+ 'code'
100
+ ),
101
+
102
+ # Mix of env var, programmatic and default config, so we use unknown
103
+ conf_value('DD_AGENT_TRANSPORT', agent_transport, seq_id, 'unknown'),
104
+
105
+ # writer_options is defined as an option that has a Hash value.
106
+ conf_value(
107
+ 'tracing.writer_options.buffer_size',
108
+ to_value(config.tracing.writer_options[:buffer_size]),
109
+ seq_id,
110
+ writer_option_origin
111
+ ),
112
+ conf_value(
113
+ 'tracing.writer_options.flush_interval',
114
+ to_value(config.tracing.writer_options[:flush_interval]),
115
+ seq_id,
116
+ writer_option_origin
117
+ ),
118
+
119
+ conf_value('DD_AGENT_HOST', config.agent.host, seq_id, get_telemetry_origin(config, 'agent.host')),
120
+ conf_value(
121
+ 'DD_TRACE_SAMPLE_RATE',
122
+ to_value(config.tracing.sampling.default_rate),
123
+ seq_id,
124
+ get_telemetry_origin(config, 'tracing.sampling.default_rate')
125
+ ),
82
126
  conf_value(
83
127
  'DD_TRACE_REMOVE_INTEGRATION_SERVICE_NAMES_ENABLED',
84
128
  config.tracing.contrib.global_default_service_name.enabled,
85
- seq_id
129
+ seq_id,
130
+ get_telemetry_origin(config, 'tracing.contrib.global_default_service_name.enabled')
86
131
  ),
87
132
  conf_value(
88
133
  'DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED',
89
134
  config.tracing.contrib.peer_service_defaults,
90
- seq_id
135
+ seq_id,
136
+ get_telemetry_origin(config, 'tracing.contrib.peer_service_defaults')
91
137
  ),
138
+ conf_value(
139
+ 'DD_TRACE_DEBUG',
140
+ config.diagnostics.debug,
141
+ seq_id,
142
+ get_telemetry_origin(config, 'diagnostics.debug')
143
+ )
92
144
  ]
93
145
 
94
146
  peer_service_mapping_str = ''
@@ -96,39 +148,69 @@ module Datadog
96
148
  peer_service_mapping = config.tracing.contrib.peer_service_mapping
97
149
  peer_service_mapping_str = peer_service_mapping.map { |key, value| "#{key}:#{value}" }.join(',')
98
150
  end
99
- list << conf_value('DD_TRACE_PEER_SERVICE_MAPPING', peer_service_mapping_str, seq_id)
151
+ list << conf_value(
152
+ 'DD_TRACE_PEER_SERVICE_MAPPING',
153
+ peer_service_mapping_str,
154
+ seq_id,
155
+ get_telemetry_origin(config, 'tracing.contrib.peer_service_mapping')
156
+ )
100
157
 
101
158
  # Whitelist of configuration options to send in additional payload object
102
- TARGET_OPTIONS.each do |option|
103
- split_option = option.split('.')
104
- list << conf_value(option, to_value(config.dig(*split_option)), seq_id)
159
+ TARGET_OPTIONS.each do |option_path|
160
+ split_option = option_path.split('.')
161
+ list << conf_value(
162
+ option_path,
163
+ to_value(config.dig(*split_option)),
164
+ seq_id,
165
+ get_telemetry_origin(config, option_path)
166
+ )
105
167
  end
106
168
 
107
- # Add some more custom additional payload values here
169
+ instrumentation_source = if Datadog.const_defined?(:SingleStepInstrument, false) &&
170
+ Datadog::SingleStepInstrument.const_defined?(:LOADED, false) &&
171
+ Datadog::SingleStepInstrument::LOADED
172
+ 'ssi'
173
+ else
174
+ 'manual'
175
+ end
176
+ # Track ssi configurations
108
177
  list.push(
109
- conf_value('tracing.auto_instrument.enabled', !defined?(Datadog::AutoInstrument::LOADED).nil?, seq_id),
110
- conf_value(
111
- 'tracing.writer_options.buffer_size',
112
- to_value(config.tracing.writer_options[:buffer_size]),
113
- seq_id
114
- ),
115
- conf_value(
116
- 'tracing.writer_options.flush_interval',
117
- to_value(config.tracing.writer_options[:flush_interval]),
118
- seq_id
119
- ),
120
- conf_value(
121
- 'tracing.opentelemetry.enabled',
122
- !defined?(Datadog::OpenTelemetry::LOADED).nil?,
123
- seq_id
124
- ),
178
+ conf_value('instrumentation_source', instrumentation_source, seq_id, 'code'),
179
+ conf_value('DD_INJECT_FORCE', Core::Environment::VariableHelpers.env_to_bool('DD_INJECT_FORCE', false), seq_id, 'env_var'),
180
+ conf_value('DD_INJECTION_ENABLED', ENV['DD_INJECTION_ENABLED'] || '', seq_id, 'env_var'),
125
181
  )
126
- list << conf_value('logger.instance', config.logger.instance.class.to_s, seq_id) if config.logger.instance
182
+
183
+ # Add some more custom additional payload values here
184
+ if config.logger.instance
185
+ list << conf_value(
186
+ 'logger.instance',
187
+ config.logger.instance.class.to_s,
188
+ seq_id,
189
+ get_telemetry_origin(config, 'logger.instance')
190
+ )
191
+ end
127
192
  if config.respond_to?('appsec')
128
- list << conf_value('appsec.enabled', config.dig('appsec', 'enabled'), seq_id)
129
- list << conf_value('appsec.sca_enabled', config.dig('appsec', 'sca_enabled'), seq_id)
193
+ list << conf_value(
194
+ 'appsec.enabled',
195
+ config.dig('appsec', 'enabled'),
196
+ seq_id,
197
+ get_telemetry_origin(config, 'appsec.enabled')
198
+ )
199
+ list << conf_value(
200
+ 'appsec.sca_enabled',
201
+ config.dig('appsec', 'sca_enabled'),
202
+ seq_id,
203
+ get_telemetry_origin(config, 'appsec.sca_enabled')
204
+ )
205
+ end
206
+ if config.respond_to?('ci')
207
+ list << conf_value(
208
+ 'ci.enabled',
209
+ config.dig('ci', 'enabled'),
210
+ seq_id,
211
+ get_telemetry_origin(config, 'ci.enabled')
212
+ )
130
213
  end
131
- list << conf_value('ci.enabled', config.dig('ci', 'enabled'), seq_id) if config.respond_to?('ci')
132
214
 
133
215
  list.reject! { |entry| entry[:value].nil? }
134
216
  list
@@ -136,8 +218,8 @@ module Datadog
136
218
  # standard:enable Metrics/AbcSize
137
219
  # standard:enable Metrics/MethodLength
138
220
 
139
- def agent_transport(config)
140
- adapter = Core::Configuration::AgentSettingsResolver.call(config).adapter
221
+ def agent_transport
222
+ adapter = @agent_settings.adapter
141
223
  if adapter == Datadog::Core::Transport::Ext::UnixSocket::ADAPTER
142
224
  'UDS'
143
225
  else
@@ -145,13 +227,27 @@ module Datadog
145
227
  end
146
228
  end
147
229
 
148
- def conf_value(name, value, seq_id, origin = 'code')
149
- {
150
- name: name,
151
- value: value,
152
- origin: origin,
153
- seq_id: seq_id,
154
- }
230
+ # `origin`: Source of the configuration. One of :
231
+ # - `fleet_stable_config`: configuration is set via the fleet automation Datadog UI
232
+ # - `local_stable_config`: configuration set via a user-managed file
233
+ # - `env_var`: configurations that are set through environment variables
234
+ # - `jvm_prop`: JVM system properties passed on the command line
235
+ # - `code`: configurations that are set through the customer application
236
+ # - `dd_config`: set by the dd.yaml file or json
237
+ # - `remote_config`: values that are set using remote config
238
+ # - `app.config`: only applies to .NET
239
+ # - `default`: set when the user has not set any configuration for the key (defaults to a value)
240
+ # - `unknown`: set for cases where it is difficult/not possible to determine the source of a config.
241
+ def conf_value(name, value, seq_id, origin)
242
+ result = {name: name, value: value, origin: origin, seq_id: seq_id}
243
+ if origin == 'fleet_stable_config'
244
+ fleet_id = Core::Configuration::StableConfig.configuration.dig(:fleet, :id)
245
+ result[:config_id] = fleet_id if fleet_id
246
+ elsif origin == 'local_stable_config'
247
+ local_id = Core::Configuration::StableConfig.configuration.dig(:local, :id)
248
+ result[:config_id] = local_id if local_id
249
+ end
250
+ result
155
251
  end
156
252
 
157
253
  def to_value(value)
@@ -172,6 +268,18 @@ module Datadog
172
268
  install_time: config.dig('telemetry', 'install_time'),
173
269
  }
174
270
  end
271
+
272
+ def get_telemetry_origin(config, config_path)
273
+ split_option = config_path.split('.')
274
+ option_name = split_option.pop
275
+ return 'unknown' if option_name.nil?
276
+
277
+ # @type var parent_setting: Core::Configuration::Options
278
+ # @type var option: Core::Configuration::Option
279
+ parent_setting = config.dig(*split_option)
280
+ option = parent_setting.send(:resolve_option, option_name.to_sym)
281
+ option.precedence_set&.origin || 'unknown'
282
+ end
175
283
  end
176
284
  end
177
285
  end
@@ -14,8 +14,8 @@ module Datadog
14
14
  # read: lib/datadog/core/telemetry/logging.rb
15
15
  module Logger
16
16
  class << self
17
- def report(exception, level: :error, description: nil)
18
- instance&.report(exception, level: level, description: description)
17
+ def report(exception, level: :error, description: nil, pii_safe: false)
18
+ instance&.report(exception, level: level, description: description, pii_safe: pii_safe)
19
19
  end
20
20
 
21
21
  def error(description)
@@ -34,9 +34,10 @@ module Datadog
34
34
  #
35
35
  # The downside is: this leaves us unable to report telemetry during component initialization.
36
36
  components = Datadog.send(:components, allow_initialization: false)
37
+ telemetry = components&.telemetry
37
38
 
38
- if components&.telemetry
39
- components.telemetry
39
+ if telemetry
40
+ telemetry
40
41
  else
41
42
  Datadog.logger.warn(
42
43
  'Failed to send telemetry before components initialization or within components lifecycle'
@@ -45,11 +45,17 @@ module Datadog
45
45
  end
46
46
  end
47
47
 
48
- def report(exception, level: :error, description: nil)
49
- # Annoymous exceptions to be logged as <Class:0x00007f8b1c0b3b40>
50
- message = +''
51
- message << (exception.class.name || exception.class.inspect)
52
- message << ': ' << description if description
48
+ def report(exception, level: :error, description: nil, pii_safe: false)
49
+ # Anonymous exceptions to be logged as <Class:0x00007f8b1c0b3b40>
50
+ message = +"#{exception.class.name || exception.class.inspect}" # standard:disable Style/RedundantInterpolation
51
+
52
+ exception_message = pii_safe ? exception.message : nil
53
+
54
+ if description || exception_message
55
+ message << ':'
56
+ message << " #{description}" if description
57
+ message << " (#{exception.message})" if exception_message
58
+ end
53
59
 
54
60
  event = Event::Log.new(
55
61
  message: message,
@@ -48,7 +48,7 @@ module Datadog
48
48
  end
49
49
 
50
50
  def get(env)
51
- get = ::Net::HTTP::Get.new(env.path, env.headers)
51
+ get = ::Net::HTTP::Get.new(net_http_path_from_env(env), env.headers)
52
52
 
53
53
  # Connect and send the request
54
54
  http_response = open do |http|
@@ -63,7 +63,7 @@ module Datadog
63
63
  post = nil
64
64
 
65
65
  if env.form.nil? || env.form.empty?
66
- post = ::Net::HTTP::Post.new(env.path, env.headers)
66
+ post = ::Net::HTTP::Post.new(net_http_path_from_env(env), env.headers)
67
67
  post.body = env.body
68
68
  else
69
69
  post = ::Datadog::Core::Vendor::Net::HTTP::Post::Multipart.new(
@@ -86,6 +86,21 @@ module Datadog
86
86
  "http://#{hostname}:#{port}?timeout=#{timeout}"
87
87
  end
88
88
 
89
+ def net_http_path_from_env(env)
90
+ path = env.path
91
+ case query = env.query
92
+ when Hash
93
+ path = path + '?' + URI.encode_www_form(query)
94
+ when String
95
+ path = path + '?' + query
96
+ when nil
97
+ # Nothing
98
+ else
99
+ raise ArgumentError, "Invalid type for query: #{query}"
100
+ end
101
+ path
102
+ end
103
+
89
104
  # Raised when called with an unknown HTTP method
90
105
  class UnknownHTTPMethod < StandardError
91
106
  attr_reader :verb
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../../configuration/agent_settings_resolver'
3
+ require_relative '../../configuration/agent_settings'
4
4
  require_relative 'adapters/registry'
5
5
  require_relative 'api/map'
6
6
 
@@ -41,7 +41,7 @@ module Datadog
41
41
 
42
42
  def adapter(config, *args, **kwargs)
43
43
  @default_adapter = case config
44
- when Core::Configuration::AgentSettingsResolver::AgentSettings
44
+ when Core::Configuration::AgentSettings
45
45
  registry_klass = REGISTRY.get(config.adapter)
46
46
  raise UnknownAdapterError, config.adapter if registry_klass.nil?
47
47
 
@@ -32,6 +32,14 @@ module Datadog
32
32
  self[:path] = value
33
33
  end
34
34
 
35
+ def query
36
+ self[:query]
37
+ end
38
+
39
+ def query=(value)
40
+ self[:query] = value
41
+ end
42
+
35
43
  def body
36
44
  self[:body]
37
45
  end
@@ -66,6 +66,13 @@ module Datadog
66
66
  placeholder
67
67
  end
68
68
 
69
+ def self.encode_tags(hash)
70
+ # Make sure everything is an utf-8 string, to avoid encoding issues in downstream
71
+ hash.each_with_object({}) do |(key, value), h|
72
+ h[utf8_encode(key)] = utf8_encode(value)
73
+ end
74
+ end
75
+
69
76
  # @!visibility private
70
77
  def self.without_warnings
71
78
  # This is typically used when monkey patching functions such as
@@ -52,6 +52,17 @@ module Datadog
52
52
  # (however, Probe instances can be replaced by OpenStruct instances
53
53
  # providing the same interface with not much effort).
54
54
  #
55
+ # Instrumenter (this class) is responsible for building snapshots.
56
+ # This is because to capture values on method entry, those values need to
57
+ # be duplicated or serialized into immutable values to prevent their
58
+ # modification by the instrumented method. Therefore this class must
59
+ # do at least some serialization/snapshot building and to keep the code
60
+ # well-encapsulated, all serialization/snapshot building should thus be
61
+ # initiated from this class rather than downstream code.
62
+ #
63
+ # As a consequence of Instrumenter building snapshots, it should not
64
+ # expose TracePoint objects to any downstream code.
65
+ #
55
66
  # @api private
56
67
  class Instrumenter
57
68
  def initialize(settings, serializer, logger, code_tracker: nil, telemetry: nil)
@@ -110,8 +121,8 @@ module Datadog
110
121
  if rate_limiter.nil? || rate_limiter.allow?
111
122
  # Arguments may be mutated by the method, therefore
112
123
  # they need to be serialized prior to method invocation.
113
- entry_args = if probe.capture_snapshot?
114
- serializer.serialize_args(args, kwargs,
124
+ serialized_entry_args = if probe.capture_snapshot?
125
+ serializer.serialize_args(args, kwargs, self,
115
126
  depth: probe.max_capture_depth || settings.dynamic_instrumentation.max_capture_depth,
116
127
  attribute_count: probe.max_capture_attribute_count || settings.dynamic_instrumentation.max_capture_attribute_count)
117
128
  end
@@ -148,8 +159,10 @@ module Datadog
148
159
  caller_locs = method_frame + caller_locations # steep:ignore
149
160
  # TODO capture arguments at exit
150
161
  # & is to stop steep complaints, block is always present here.
151
- block&.call(probe: probe, rv: rv, duration: duration, caller_locations: caller_locs,
152
- serialized_entry_args: entry_args)
162
+ block&.call(probe: probe, rv: rv,
163
+ duration: duration, caller_locations: caller_locs,
164
+ target_self: self,
165
+ serialized_entry_args: serialized_entry_args)
153
166
  rv
154
167
  else
155
168
  # stop standard from trying to mess up my code
@@ -298,8 +311,21 @@ module Datadog
298
311
  probe.file == tp.path || probe.file_matches?(tp.path)
299
312
  )
300
313
  if rate_limiter.nil? || rate_limiter.allow?
314
+ serialized_locals = if probe.capture_snapshot?
315
+ serializer.serialize_vars(Instrumenter.get_local_variables(tp),
316
+ depth: probe.max_capture_depth || settings.dynamic_instrumentation.max_capture_depth,
317
+ attribute_count: probe.max_capture_attribute_count || settings.dynamic_instrumentation.max_capture_attribute_count,)
318
+ end
319
+ if probe.capture_snapshot?
320
+ serializer.serialize_value(tp.self,
321
+ depth: probe.max_capture_depth || settings.dynamic_instrumentation.max_capture_depth,
322
+ attribute_count: probe.max_capture_attribute_count || settings.dynamic_instrumentation.max_capture_attribute_count,)
323
+ end
301
324
  # & is to stop steep complaints, block is always present here.
302
- block&.call(probe: probe, trace_point: tp, caller_locations: caller_locations)
325
+ block&.call(probe: probe,
326
+ serialized_locals: serialized_locals,
327
+ target_self: tp.self,
328
+ path: tp.path, caller_locations: caller_locations)
303
329
  end
304
330
  end
305
331
  rescue => exc
@@ -371,6 +397,23 @@ module Datadog
371
397
  end
372
398
  end
373
399
 
400
+ class << self
401
+ def get_local_variables(trace_point)
402
+ # binding appears to be constructed on access, therefore
403
+ # 1) we should attempt to cache it and
404
+ # 2) we should not call +binding+ until we actually need variable values.
405
+ binding = trace_point.binding
406
+
407
+ # steep hack - should never happen
408
+ return {} unless binding
409
+
410
+ binding.local_variables.each_with_object({}) do |name, map|
411
+ value = binding.local_variable_get(name)
412
+ map[name] = value
413
+ end
414
+ end
415
+ end
416
+
374
417
  private
375
418
 
376
419
  attr_reader :lock