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
@@ -82,15 +82,31 @@ module Datadog
82
82
  # we just strip leading directory components from the "probe path"
83
83
  # until we get a match via a "suffix of the suffix".
84
84
 
85
+ # Returns +path+ with Windows-style backslash separators translated to
86
+ # forward slashes (DEBUG-5111). Used to normalize probe source paths
87
+ # that originate from IDE tooling running on Windows.
88
+ module_function def normalize_windows_separators(path)
89
+ path.tr('\\', '/')
90
+ end
91
+
85
92
  # Returns whether the provided +path+ matches the user-designated
86
93
  # file suffix (of a line probe).
87
94
  #
88
- # If suffix is an absolute path (i.e., it starts with a slash), the path
89
- # must be identical for it to match.
95
+ # Backslash separators in +suffix+ are translated to forward slashes
96
+ # (DEBUG-5111) so paths typed by IDE tooling on Windows can match.
97
+ # Case-insensitive matching (DEBUG-5107) is opt-in via
98
+ # +case_insensitive: true+; callers that orchestrate matching against a
99
+ # set of known paths perform case-sensitive comparisons first and only
100
+ # fall back to case-insensitive when no case-sensitive match is found
101
+ # (see the design comment above, steps 5-8).
102
+ #
103
+ # If suffix is an absolute path (i.e., it starts with a slash, possibly
104
+ # after backslash normalization), the path must be identical for it to
105
+ # match.
90
106
  #
91
107
  # If suffix is not an absolute path, the path matches if its suffix is
92
108
  # the provided suffix, at a path component boundary.
93
- module_function def path_matches_suffix?(path, suffix)
109
+ module_function def path_matches_suffix?(path, suffix, case_insensitive: false)
94
110
  if path.nil?
95
111
  raise ArgumentError, "nil path passed"
96
112
  end
@@ -98,6 +114,12 @@ module Datadog
98
114
  raise ArgumentError, "nil suffix passed"
99
115
  end
100
116
 
117
+ suffix = normalize_windows_separators(suffix)
118
+ if case_insensitive
119
+ path = path.downcase
120
+ suffix = suffix.downcase
121
+ end
122
+
101
123
  if suffix.start_with?('/')
102
124
  path == suffix
103
125
  else
@@ -105,11 +127,6 @@ module Datadog
105
127
  # has to be longer than the suffix. Require full component matches,
106
128
  # meaning either the first character of the suffix is a slash
107
129
  # or the previous character in the path is a slash.
108
- # For now only check for forward slashes for Unix-like OSes;
109
- # backslash is a legitimate character of a file name in Unix
110
- # therefore simply permitting forward or back slash is not
111
- # sufficient, we need to perform an OS check to know which
112
- # path separator to use.
113
130
  !!
114
131
  if path.length > suffix.length && path.end_with?(suffix)
115
132
  previous_char = path[path.length - suffix.length - 1]
@@ -125,15 +142,26 @@ module Datadog
125
142
  # +spec+. Attempts all of the fuzzy matches by stripping directories
126
143
  # from the front of +spec+. Does not consider othr known paths to
127
144
  # identify the case of (potentially) multiple matching paths for +spec+.
145
+ #
146
+ # Matching is attempted case-sensitively first (steps 5-6 in the design
147
+ # comment above) and only falls back to case-insensitive (steps 7-8)
148
+ # when no case-sensitive match is found.
128
149
  module_function def path_can_match_spec?(path, spec)
129
- return true if path_matches_suffix?(path, spec)
150
+ # Normalize Windows-style backslash separators (DEBUG-5111) so the
151
+ # suffix-shortening loop's "/+" regex can strip leading components.
152
+ spec = normalize_windows_separators(spec)
130
153
 
131
- spec = spec.dup
132
- loop do
133
- return false unless spec.include?('/')
134
- spec.sub!(%r{.*/+}, '')
135
- return true if path_matches_suffix?(path, spec)
154
+ [false, true].each do |case_insensitive|
155
+ working_spec = spec.dup
156
+ return true if path_matches_suffix?(path, working_spec, case_insensitive: case_insensitive)
157
+
158
+ loop do
159
+ break unless working_spec.include?('/')
160
+ working_spec.sub!(%r{.*/+}, '')
161
+ return true if path_matches_suffix?(path, working_spec, case_insensitive: case_insensitive)
162
+ end
136
163
  end
164
+ false
137
165
  end
138
166
  end
139
167
  end
@@ -152,6 +152,71 @@ module Datadog
152
152
  o.setter(&Settings.normalize_protocol('OTEL_EXPORTER_OTLP_METRICS_PROTOCOL'))
153
153
  end
154
154
  end
155
+
156
+ settings :logs do
157
+ option :enabled do |o|
158
+ o.type :bool
159
+ o.env 'DD_LOGS_OTEL_ENABLED'
160
+ o.default false
161
+ end
162
+
163
+ option :exporter do |o|
164
+ o.type :string
165
+ o.env 'OTEL_LOGS_EXPORTER'
166
+ o.default 'otlp'
167
+ end
168
+
169
+ option :endpoint do |o|
170
+ o.type :string, nilable: true
171
+ o.env 'OTEL_EXPORTER_OTLP_LOGS_ENDPOINT'
172
+ o.default nil
173
+ end
174
+
175
+ option :headers do |o|
176
+ o.skip_telemetry true
177
+ o.type :hash, nilable: true
178
+ o.env 'OTEL_EXPORTER_OTLP_LOGS_HEADERS'
179
+ o.default nil
180
+ o.env_parser(&Settings.headers_parser('OTEL_EXPORTER_OTLP_LOGS_HEADERS'))
181
+ end
182
+
183
+ option :timeout_millis do |o|
184
+ o.type :int, nilable: true
185
+ o.env 'OTEL_EXPORTER_OTLP_LOGS_TIMEOUT'
186
+ o.default 10_000
187
+ end
188
+
189
+ option :protocol do |o|
190
+ o.type :string, nilable: true
191
+ o.env 'OTEL_EXPORTER_OTLP_LOGS_PROTOCOL'
192
+ o.default "http/protobuf"
193
+ o.setter(&Settings.normalize_protocol('OTEL_EXPORTER_OTLP_LOGS_PROTOCOL'))
194
+ end
195
+
196
+ option :max_queue_size do |o|
197
+ o.type :int
198
+ o.env 'OTEL_BLRP_MAX_QUEUE_SIZE'
199
+ o.default 2048
200
+ end
201
+
202
+ option :schedule_delay_millis do |o|
203
+ o.type :int
204
+ o.env 'OTEL_BLRP_SCHEDULE_DELAY'
205
+ o.default 1000
206
+ end
207
+
208
+ option :export_timeout_millis do |o|
209
+ o.type :int
210
+ o.env 'OTEL_BLRP_EXPORT_TIMEOUT'
211
+ o.default 30_000
212
+ end
213
+
214
+ option :max_export_batch_size do |o|
215
+ o.type :int
216
+ o.env 'OTEL_BLRP_MAX_EXPORT_BATCH_SIZE'
217
+ o.default 512
218
+ end
219
+ end
155
220
  end
156
221
  end
157
222
  end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module OpenTelemetry
5
+ module Ext
6
+ EXPORTER_NONE = 'none'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'ext'
4
+ require_relative 'signal_configuration'
5
+
6
+ module Datadog
7
+ module OpenTelemetry
8
+ class Logs
9
+ include SignalConfiguration
10
+
11
+ def self.initialize!(components)
12
+ new(components).configure_logs_sdk
13
+ true
14
+ rescue => exc
15
+ components.logger.warn(
16
+ "Failed to initialize OpenTelemetry logs: #{exc.class}: #{exc.message}\n#{(exc.backtrace || []).join("\n")}"
17
+ )
18
+ false
19
+ end
20
+
21
+ def initialize(components)
22
+ @logger = components.logger
23
+ @settings = components.settings
24
+ @agent_host = components.agent_settings.hostname
25
+ @agent_ssl = components.agent_settings.ssl
26
+ end
27
+
28
+ def configure_logs_sdk
29
+ provider = ::OpenTelemetry.logger_provider
30
+ provider.shutdown if provider.is_a?(::OpenTelemetry::SDK::Logs::LoggerProvider)
31
+
32
+ resource = create_resource
33
+ provider = ::OpenTelemetry::SDK::Logs::LoggerProvider.new(resource: resource)
34
+ processor_configured = configure_log_record_processor(provider)
35
+ ::OpenTelemetry.logger_provider = provider
36
+
37
+ disable_log_injection if processor_configured
38
+ end
39
+
40
+ private
41
+
42
+ def disable_log_injection
43
+ @logger.warn('OTel logs enabled: disabling Datadog log injection to prevent duplicate trace correlation fields')
44
+ Datadog.configure do |c|
45
+ c.tracing.log_injection = false # steep:ignore
46
+ end
47
+ end
48
+
49
+ def configure_log_record_processor(provider)
50
+ exporter_name = @settings.opentelemetry.logs.exporter
51
+ return false if exporter_name == Ext::EXPORTER_NONE
52
+
53
+ configure_otlp_exporter(provider)
54
+ rescue => e
55
+ @logger.warn("Failed to configure OTLP logs exporter: #{e.class}: #{e.message}")
56
+ false
57
+ end
58
+
59
+ def default_logs_endpoint
60
+ "#{@agent_ssl ? 'https' : 'http'}://#{@agent_host}:4318/v1/logs"
61
+ end
62
+
63
+ def configure_otlp_exporter(provider)
64
+ require_relative 'sdk/logs_exporter'
65
+
66
+ logs_config = @settings.opentelemetry.logs
67
+ endpoint = config_or_exporter_fallback(
68
+ signal: :logs,
69
+ option_name: :endpoint,
70
+ computed_default: default_logs_endpoint
71
+ )
72
+ timeout = config_or_exporter_fallback(signal: :logs, option_name: :timeout_millis)
73
+ headers = config_or_exporter_fallback(signal: :logs, option_name: :headers)
74
+ # opentelemetry-logs-sdk only supports http/protobuf protocol as of 0.5.0.
75
+ config_or_exporter_fallback(signal: :logs, option_name: :protocol)
76
+
77
+ exporter = Datadog::OpenTelemetry::SDK::LogsExporter.new(
78
+ endpoint: endpoint,
79
+ timeout: timeout / 1000.0,
80
+ headers: headers
81
+ )
82
+
83
+ processor = ::OpenTelemetry::SDK::Logs::Export::BatchLogRecordProcessor.new(
84
+ exporter,
85
+ max_queue_size: logs_config.max_queue_size,
86
+ schedule_delay: logs_config.schedule_delay_millis,
87
+ exporter_timeout: logs_config.export_timeout_millis,
88
+ max_export_batch_size: logs_config.max_export_batch_size
89
+ )
90
+ provider.add_log_record_processor(processor)
91
+ true
92
+ rescue LoadError => e
93
+ @logger.warn("Could not load OTLP logs exporter: #{e.class}: #{e.message}")
94
+ false
95
+ end
96
+ end
97
+ end
98
+ end
@@ -1,17 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../core/configuration/ext'
4
+ require_relative 'ext'
5
+ require_relative 'signal_configuration'
4
6
 
5
7
  module Datadog
6
8
  module OpenTelemetry
7
9
  class Metrics
8
- EXPORTER_NONE = 'none'
10
+ include SignalConfiguration
9
11
 
10
12
  def self.initialize!(components)
11
13
  new(components).configure_metrics_sdk
12
14
  true
13
15
  rescue => exc
14
- components.logger.error("Failed to initialize OpenTelemetry metrics: #{exc.class}: #{exc.message}: #{exc.backtrace.join("\n")}")
16
+ components.logger.warn("Failed to initialize OpenTelemetry metrics: #{exc.class}: #{exc.message}: #{exc.backtrace.join("\n")}")
15
17
  false
16
18
  end
17
19
 
@@ -40,30 +42,9 @@ module Datadog
40
42
 
41
43
  private
42
44
 
43
- def create_resource
44
- resource_attributes = {}
45
- resource_attributes['host.name'] = Datadog::Core::Environment::Socket.hostname if @settings.tracing.report_hostname
46
-
47
- @settings.tags&.each do |key, value|
48
- otel_key = case key
49
- when 'service' then 'service.name'
50
- when 'env' then 'deployment.environment'
51
- when 'version' then 'service.version'
52
- else key
53
- end
54
- resource_attributes[otel_key] = value
55
- end
56
-
57
- resource_attributes['service.name'] = @settings.service_without_fallback || resource_attributes['service.name'] || Datadog::Core::Environment::Ext::FALLBACK_SERVICE_NAME
58
- resource_attributes['deployment.environment'] = @settings.env if @settings.env
59
- resource_attributes['service.version'] = @settings.version if @settings.version
60
-
61
- ::OpenTelemetry::SDK::Resources::Resource.create(resource_attributes)
62
- end
63
-
64
45
  def configure_metric_reader(provider)
65
46
  exporter_name = @settings.opentelemetry.metrics.exporter
66
- return if exporter_name == EXPORTER_NONE
47
+ return if exporter_name == Ext::EXPORTER_NONE
67
48
 
68
49
  configure_otlp_exporter(provider)
69
50
  rescue => e
@@ -79,15 +60,16 @@ module Datadog
79
60
  require_relative 'sdk/metrics_exporter'
80
61
 
81
62
  metrics_config = @settings.opentelemetry.metrics
82
- endpoint = get_metrics_config_with_fallback(
63
+ endpoint = config_or_exporter_fallback(
64
+ signal: :metrics,
83
65
  option_name: :endpoint,
84
66
  computed_default: default_metrics_endpoint
85
67
  )
86
- timeout = get_metrics_config_with_fallback(option_name: :timeout_millis)
87
- headers = get_metrics_config_with_fallback(option_name: :headers)
68
+ timeout = config_or_exporter_fallback(signal: :metrics, option_name: :timeout_millis)
69
+ headers = config_or_exporter_fallback(signal: :metrics, option_name: :headers)
88
70
  # OpenTelemetry SDK only supports http/protobuf protocol.
89
71
  # TODO: Add support for http/json and grpc.
90
- # protocol = get_metrics_config_with_fallback(option_name: :protocol)
72
+ # protocol = config_or_exporter_fallback(signal: :metrics, option_name: :protocol)
91
73
  exporter = Datadog::OpenTelemetry::SDK::MetricsExporter.new(
92
74
  endpoint: endpoint,
93
75
  timeout: timeout / 1000.0,
@@ -103,15 +85,6 @@ module Datadog
103
85
  rescue LoadError => e
104
86
  @logger.warn("Could not load OTLP metrics exporter: #{e.class}: #{e.message}")
105
87
  end
106
-
107
- # Returns metrics config value if explicitly set, otherwise falls back to exporter config or computed default value.
108
- def get_metrics_config_with_fallback(option_name:, computed_default: nil)
109
- if @settings.opentelemetry.metrics.using_default?(option_name)
110
- @settings.opentelemetry.exporter.public_send(option_name) || computed_default
111
- else
112
- @settings.opentelemetry.metrics.public_send(option_name)
113
- end
114
- end
115
88
  end
116
89
  end
117
90
  end
@@ -30,6 +30,16 @@ module Datadog
30
30
  [SpanProcessor.new]
31
31
  end
32
32
 
33
+ # SDK 1.6.0+ calls logs_configuration_hook from configure.
34
+ # https://github.com/open-telemetry/opentelemetry-ruby/blob/opentelemetry-sdk/v1.6.0/sdk/lib/opentelemetry/sdk/configurator.rb#L152
35
+ # Older supported SDK versions do not, so we call it explicitly as a fallback.
36
+ # The flag prevents double-calling on newer SDK versions.
37
+ def configure
38
+ @datadog_logs_hook_called = false
39
+ super
40
+ logs_configuration_hook unless @datadog_logs_hook_called
41
+ end
42
+
33
43
  def metrics_configuration_hook
34
44
  components = Datadog.send(:components)
35
45
  return super unless components.settings.opentelemetry.metrics.enabled
@@ -45,15 +55,45 @@ module Datadog
45
55
  super unless success
46
56
  end
47
57
 
58
+ def logs_configuration_hook
59
+ @datadog_logs_hook_called = true
60
+ components = Datadog.send(:components)
61
+ unless components.settings.opentelemetry.logs.enabled
62
+ super if defined?(super)
63
+ return
64
+ end
65
+
66
+ begin
67
+ require 'opentelemetry-logs-sdk'
68
+ rescue LoadError => exc
69
+ components.logger.warn("Failed to load OpenTelemetry logs gems: #{exc.class}: #{exc.message}")
70
+ return
71
+ end
72
+
73
+ success = Datadog::OpenTelemetry::Logs.initialize!(components)
74
+ unless success
75
+ components.logger.warn('Falling back to OpenTelemetry default logs configuration')
76
+ super if defined?(super)
77
+ end
78
+ end
79
+
48
80
  # Prepend to ConfiguratorPatch (not Configurator) so our hook runs first.
49
81
  begin
50
82
  require 'opentelemetry-metrics-sdk' if defined?(OpenTelemetry::SDK) && !defined?(OpenTelemetry::SDK::Metrics::ConfiguratorPatch)
51
83
  rescue LoadError
52
84
  end
53
85
 
86
+ begin
87
+ require 'opentelemetry-logs-sdk' if defined?(OpenTelemetry::SDK) && !defined?(OpenTelemetry::SDK::Logs::ConfiguratorPatch)
88
+ rescue LoadError
89
+ end
90
+
54
91
  if defined?(::OpenTelemetry::SDK::Metrics::ConfiguratorPatch)
55
92
  ::OpenTelemetry::SDK::Metrics::ConfiguratorPatch.prepend(self) unless ::OpenTelemetry::SDK::Metrics::ConfiguratorPatch.ancestors.include?(self)
56
93
  end
94
+ if defined?(::OpenTelemetry::SDK::Logs::ConfiguratorPatch)
95
+ ::OpenTelemetry::SDK::Logs::ConfiguratorPatch.prepend(self) unless ::OpenTelemetry::SDK::Logs::ConfiguratorPatch.ancestors.include?(self)
96
+ end
57
97
  ::OpenTelemetry::SDK::Configurator.prepend(self) unless ::OpenTelemetry::SDK::Configurator.ancestors.include?(self)
58
98
  end
59
99
  end
@@ -1,23 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../../tracing/utils'
4
+
3
5
  module Datadog
4
6
  module OpenTelemetry
5
7
  module SDK
6
- # Generates Datadog-compatible IDs for OpenTelemetry traces.
7
- # OpenTelemetry traces already produce Datadog-compatible IDs.
8
+ # Generates Datadog-compatible trace IDs for OpenTelemetry spans.
9
+ #
10
+ # Reuses the same 128-bit ID format as non-OTel Datadog tracing:
11
+ # [32-bit seconds since Epoch | 32 zero bits | 64 random bits]
12
+ #
13
+ # When DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED is false the high 64
14
+ # bits are zero, preserving the OTel 16-byte wire format while keeping
15
+ # backward compatibility with 64-bit Datadog trace IDs.
8
16
  class IdGenerator
9
17
  class << self
10
18
  include ::OpenTelemetry::Trace
11
19
 
12
- # Generates a valid trace identifier, a 16-byte string with at least one
13
- # non-zero byte.
14
- #
15
- # @return [String] a valid trace ID.
20
+ # @return [String] a valid 16-byte trace ID.
16
21
  def generate_trace_id
17
- loop do
18
- id = Random.bytes(8) # DEV: Change to 16 (16*8-byte) when 128-bit trace_id is supported.
19
- return id unless id == ::OpenTelemetry::Trace::INVALID_SPAN_ID
20
- end
22
+ trace_id = Tracing::Utils::TraceId.next_id
23
+ [
24
+ Tracing::Utils::TraceId.to_high_order(trace_id),
25
+ Tracing::Utils::TraceId.to_low_order(trace_id),
26
+ ].pack('Q>Q>')
21
27
  end
22
28
  end
23
29
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'opentelemetry/exporter/otlp_logs'
4
+
5
+ module Datadog
6
+ module OpenTelemetry
7
+ module SDK
8
+ class LogsExporter < ::OpenTelemetry::Exporter::OTLP::Logs::LogsExporter
9
+ METRIC_EXPORT_ATTEMPTS = 'otel.logs_export_attempts'
10
+ METRIC_EXPORT_SUCCESSES = 'otel.logs_export_successes'
11
+ METRIC_EXPORT_FAILURES = 'otel.logs_export_failures'
12
+ METRIC_LOG_RECORDS = 'otel.log_records'
13
+ TELEMETRY_NAMESPACE = 'tracers'
14
+ TELEMETRY_TAGS = {'protocol' => 'http', 'encoding' => 'protobuf'}.freeze
15
+
16
+ def export(log_records, timeout: nil)
17
+ telemetry&.inc(TELEMETRY_NAMESPACE, METRIC_EXPORT_ATTEMPTS, 1, tags: TELEMETRY_TAGS)
18
+ telemetry&.inc(TELEMETRY_NAMESPACE, METRIC_LOG_RECORDS, log_records.size, tags: TELEMETRY_TAGS)
19
+ result = super
20
+ metric_name = (result == 0) ? METRIC_EXPORT_SUCCESSES : METRIC_EXPORT_FAILURES
21
+ telemetry&.inc(TELEMETRY_NAMESPACE, metric_name, 1, tags: TELEMETRY_TAGS)
22
+ result
23
+ rescue => e
24
+ Datadog.logger.warn("Failed to export OpenTelemetry Logs: #{e.class}: #{e.message}")
25
+ telemetry&.inc(TELEMETRY_NAMESPACE, METRIC_EXPORT_FAILURES, 1, tags: TELEMETRY_TAGS)
26
+ raise
27
+ end
28
+
29
+ private
30
+
31
+ def telemetry
32
+ Datadog.send(:components).telemetry
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../core/configuration/ext'
4
+ require_relative '../core/environment/socket'
5
+
6
+ module Datadog
7
+ module OpenTelemetry
8
+ # Shared resource building and signal-specific config fallback logic for Logs and Metrics.
9
+ module SignalConfiguration
10
+ private
11
+
12
+ def create_resource
13
+ resource_attributes = {}
14
+
15
+ @settings.tags&.each do |key, value| # steep:ignore
16
+ otel_key = case key
17
+ when 'service' then 'service.name'
18
+ when 'env' then 'deployment.environment'
19
+ when 'version' then 'service.version'
20
+ else key
21
+ end
22
+ resource_attributes[otel_key] = value
23
+ end
24
+
25
+ resource_attributes['service.name'] = @settings.service_without_fallback || resource_attributes['service.name'] || Datadog::Core::Environment::Ext::FALLBACK_SERVICE_NAME # steep:ignore
26
+ resource_attributes['deployment.environment'] = @settings.env if @settings.env # steep:ignore
27
+ resource_attributes['service.version'] = @settings.version if @settings.version # steep:ignore
28
+
29
+ hostname = Datadog::Core::Environment::Socket.resolved_hostname(@settings) # steep:ignore
30
+ if hostname
31
+ if hostname == @settings.hostname # steep:ignore
32
+ resource_attributes['host.name'] = hostname
33
+ elsif !resource_attributes.key?('host.name')
34
+ resource_attributes['host.name'] = hostname
35
+ end
36
+ end
37
+
38
+ ::OpenTelemetry::SDK::Resources::Resource.create(resource_attributes)
39
+ end
40
+
41
+ # Returns the signal-specific option value when explicitly set,
42
+ # otherwise falls back to the general OTLP exporter config or computed_default.
43
+ def config_or_exporter_fallback(signal:, option_name:, computed_default: nil)
44
+ signal_settings = @settings.opentelemetry.public_send(signal) # steep:ignore
45
+ if signal_settings.using_default?(option_name)
46
+ @settings.opentelemetry.exporter.public_send(option_name) || computed_default # steep:ignore
47
+ else
48
+ signal_settings.public_send(option_name)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -23,6 +23,7 @@ require_relative 'opentelemetry/sdk/configurator' if defined?(OpenTelemetry::SDK
23
23
  require_relative 'opentelemetry/sdk/trace/span' if defined?(OpenTelemetry::SDK)
24
24
 
25
25
  require_relative 'opentelemetry/metrics' if defined?(OpenTelemetry::SDK::Metrics)
26
+ require_relative 'opentelemetry/logs' if defined?(OpenTelemetry::SDK::Logs)
26
27
 
27
28
  module Datadog
28
29
  # Datadog OpenTelemetry integration.
@@ -49,7 +49,6 @@ module Datadog
49
49
  valid_cpu_sampling_interval(settings.profiling.advanced.experimental_cpu_sampling_interval_ms, logger)
50
50
 
51
51
  recorder = Datadog::Profiling::StackRecorder.new(
52
- cpu_time_enabled: RUBY_PLATFORM.include?("linux"), # Only supported on Linux currently
53
52
  alloc_samples_enabled: allocation_profiling_enabled,
54
53
  heap_samples_enabled: heap_profiling_enabled,
55
54
  heap_size_enabled: heap_size_profiling_enabled,
@@ -9,7 +9,6 @@ module Datadog
9
9
  # Methods prefixed with _native_ are implemented in `stack_recorder.c`
10
10
  class StackRecorder
11
11
  def initialize(
12
- cpu_time_enabled:,
13
12
  alloc_samples_enabled:,
14
13
  heap_samples_enabled:,
15
14
  heap_size_enabled:,
@@ -27,7 +26,6 @@ module Datadog
27
26
 
28
27
  self.class._native_initialize(
29
28
  self_instance: self,
30
- cpu_time_enabled: cpu_time_enabled,
31
29
  alloc_samples_enabled: alloc_samples_enabled,
32
30
  heap_samples_enabled: heap_samples_enabled,
33
31
  heap_size_enabled: heap_size_enabled,
@@ -38,7 +36,6 @@ module Datadog
38
36
  end
39
37
 
40
38
  def self.for_testing(
41
- cpu_time_enabled: true,
42
39
  alloc_samples_enabled: false,
43
40
  heap_samples_enabled: false,
44
41
  heap_size_enabled: false,
@@ -48,7 +45,6 @@ module Datadog
48
45
  **options
49
46
  )
50
47
  new(
51
- cpu_time_enabled: cpu_time_enabled,
52
48
  alloc_samples_enabled: alloc_samples_enabled,
53
49
  heap_samples_enabled: heap_samples_enabled,
54
50
  heap_size_enabled: heap_size_enabled,