datadog 2.23.0 → 2.25.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +67 -1
- data/ext/datadog_profiling_native_extension/collectors_stack.c +17 -5
- data/ext/datadog_profiling_native_extension/crashtracking_runtime_stacks.c +239 -0
- data/ext/datadog_profiling_native_extension/extconf.rb +4 -1
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +12 -0
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +4 -0
- data/ext/datadog_profiling_native_extension/profiling.c +2 -0
- data/lib/datadog/ai_guard/api_client.rb +82 -0
- data/lib/datadog/ai_guard/component.rb +42 -0
- data/lib/datadog/ai_guard/configuration/ext.rb +17 -0
- data/lib/datadog/ai_guard/configuration/settings.rb +98 -0
- data/lib/datadog/ai_guard/configuration.rb +11 -0
- data/lib/datadog/ai_guard/evaluation/message.rb +25 -0
- data/lib/datadog/ai_guard/evaluation/no_op_result.rb +34 -0
- data/lib/datadog/ai_guard/evaluation/request.rb +81 -0
- data/lib/datadog/ai_guard/evaluation/result.rb +43 -0
- data/lib/datadog/ai_guard/evaluation/tool_call.rb +18 -0
- data/lib/datadog/ai_guard/evaluation.rb +72 -0
- data/lib/datadog/ai_guard/ext.rb +16 -0
- data/lib/datadog/ai_guard.rb +153 -0
- data/lib/datadog/appsec/context.rb +2 -1
- data/lib/datadog/appsec/remote.rb +5 -12
- data/lib/datadog/appsec/security_engine/engine.rb +3 -3
- data/lib/datadog/appsec/security_engine/result.rb +2 -1
- data/lib/datadog/appsec/security_engine/runner.rb +2 -2
- data/lib/datadog/core/configuration/components.rb +6 -0
- data/lib/datadog/core/configuration/config_helper.rb +1 -1
- data/lib/datadog/core/configuration/deprecations.rb +2 -2
- data/lib/datadog/core/configuration/option_definition.rb +4 -2
- data/lib/datadog/core/configuration/options.rb +8 -5
- data/lib/datadog/core/configuration/settings.rb +14 -3
- data/lib/datadog/core/configuration/supported_configurations.rb +8 -1
- data/lib/datadog/core/environment/cgroup.rb +52 -25
- data/lib/datadog/core/environment/container.rb +140 -46
- data/lib/datadog/core/environment/ext.rb +1 -0
- data/lib/datadog/core/environment/process.rb +9 -1
- data/lib/datadog/core/error.rb +6 -6
- data/lib/datadog/core/pin.rb +4 -0
- data/lib/datadog/core/rate_limiter.rb +9 -1
- data/lib/datadog/core/remote/client.rb +14 -6
- data/lib/datadog/core/remote/component.rb +6 -4
- data/lib/datadog/core/remote/configuration/content.rb +15 -2
- data/lib/datadog/core/remote/configuration/digest.rb +14 -7
- data/lib/datadog/core/remote/configuration/repository.rb +1 -1
- data/lib/datadog/core/remote/configuration/target.rb +13 -6
- data/lib/datadog/core/remote/transport/config.rb +3 -16
- data/lib/datadog/core/remote/transport/http/config.rb +4 -44
- data/lib/datadog/core/remote/transport/http/negotiation.rb +0 -39
- data/lib/datadog/core/remote/transport/http.rb +13 -24
- data/lib/datadog/core/remote/transport/negotiation.rb +7 -16
- data/lib/datadog/core/semaphore.rb +1 -4
- data/lib/datadog/core/telemetry/component.rb +52 -13
- data/lib/datadog/core/telemetry/event/app_started.rb +36 -1
- data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +27 -4
- data/lib/datadog/core/telemetry/metrics_manager.rb +9 -0
- data/lib/datadog/core/telemetry/request.rb +17 -3
- data/lib/datadog/core/telemetry/transport/http/telemetry.rb +2 -32
- data/lib/datadog/core/telemetry/transport/http.rb +21 -16
- data/lib/datadog/core/telemetry/transport/telemetry.rb +3 -10
- data/lib/datadog/core/telemetry/worker.rb +88 -32
- data/lib/datadog/core/transport/ext.rb +2 -0
- data/lib/datadog/core/transport/http/api/endpoint.rb +9 -4
- data/lib/datadog/core/transport/http/api/instance.rb +4 -21
- data/lib/datadog/core/transport/http/builder.rb +9 -5
- data/lib/datadog/core/transport/http/client.rb +19 -8
- data/lib/datadog/core/transport/http.rb +22 -19
- data/lib/datadog/core/transport/response.rb +12 -1
- data/lib/datadog/core/transport/transport.rb +90 -0
- data/lib/datadog/core/utils/only_once_successful.rb +2 -0
- data/lib/datadog/core/utils/safe_dup.rb +2 -2
- data/lib/datadog/core/utils/sequence.rb +2 -0
- data/lib/datadog/core/utils/time.rb +1 -1
- data/lib/datadog/core/workers/async.rb +10 -1
- data/lib/datadog/core/workers/interval_loop.rb +44 -3
- data/lib/datadog/core/workers/polling.rb +2 -0
- data/lib/datadog/core/workers/queue.rb +100 -1
- data/lib/datadog/data_streams/processor.rb +1 -1
- data/lib/datadog/data_streams/transport/http/stats.rb +1 -36
- data/lib/datadog/data_streams/transport/http.rb +5 -6
- data/lib/datadog/data_streams/transport/stats.rb +3 -17
- data/lib/datadog/di/boot.rb +4 -2
- data/lib/datadog/di/contrib/active_record.rb +30 -5
- data/lib/datadog/di/el/compiler.rb +8 -4
- data/lib/datadog/di/error.rb +5 -0
- data/lib/datadog/di/instrumenter.rb +26 -7
- data/lib/datadog/di/logger.rb +2 -2
- data/lib/datadog/di/probe_builder.rb +2 -1
- data/lib/datadog/di/probe_file_loader/railtie.rb +1 -1
- data/lib/datadog/di/probe_manager.rb +37 -31
- data/lib/datadog/di/probe_notification_builder.rb +15 -2
- data/lib/datadog/di/probe_notifier_worker.rb +5 -5
- data/lib/datadog/di/remote.rb +89 -84
- data/lib/datadog/di/transport/diagnostics.rb +7 -35
- data/lib/datadog/di/transport/http/diagnostics.rb +1 -31
- data/lib/datadog/di/transport/http/input.rb +1 -31
- data/lib/datadog/di/transport/http.rb +28 -17
- data/lib/datadog/di/transport/input.rb +7 -34
- data/lib/datadog/di.rb +61 -5
- data/lib/datadog/error_tracking/filters.rb +2 -2
- data/lib/datadog/kit/appsec/events/v2.rb +2 -3
- data/lib/datadog/open_feature/evaluation_engine.rb +2 -1
- data/lib/datadog/open_feature/remote.rb +3 -10
- data/lib/datadog/open_feature/transport.rb +9 -11
- data/lib/datadog/opentelemetry/api/baggage.rb +1 -1
- data/lib/datadog/opentelemetry/configuration/settings.rb +2 -2
- data/lib/datadog/opentelemetry/metrics.rb +21 -14
- data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +5 -8
- data/lib/datadog/profiling/collectors/code_provenance.rb +27 -2
- data/lib/datadog/profiling/collectors/info.rb +5 -4
- data/lib/datadog/profiling/component.rb +12 -11
- data/lib/datadog/profiling/ext/dir_monkey_patches.rb +18 -0
- data/lib/datadog/profiling/http_transport.rb +4 -1
- data/lib/datadog/tracing/contrib/extensions.rb +10 -2
- data/lib/datadog/tracing/contrib/karafka/patcher.rb +31 -32
- data/lib/datadog/tracing/contrib/status_range_matcher.rb +2 -1
- data/lib/datadog/tracing/contrib/utils/quantization/hash.rb +3 -1
- data/lib/datadog/tracing/contrib/waterdrop/patcher.rb +6 -3
- data/lib/datadog/tracing/contrib/waterdrop.rb +4 -0
- data/lib/datadog/tracing/diagnostics/environment_logger.rb +1 -1
- data/lib/datadog/tracing/distributed/baggage.rb +3 -2
- data/lib/datadog/tracing/remote.rb +1 -9
- data/lib/datadog/tracing/sampling/priority_sampler.rb +3 -1
- data/lib/datadog/tracing/span.rb +1 -1
- data/lib/datadog/tracing/span_event.rb +2 -2
- data/lib/datadog/tracing/span_operation.rb +20 -9
- data/lib/datadog/tracing/trace_operation.rb +44 -6
- data/lib/datadog/tracing/tracer.rb +42 -16
- data/lib/datadog/tracing/transport/http/traces.rb +2 -50
- data/lib/datadog/tracing/transport/http.rb +15 -9
- data/lib/datadog/tracing/transport/io/client.rb +1 -1
- data/lib/datadog/tracing/transport/traces.rb +6 -66
- data/lib/datadog/tracing/workers/trace_writer.rb +5 -0
- data/lib/datadog/tracing/writer.rb +1 -0
- data/lib/datadog/version.rb +2 -2
- data/lib/datadog.rb +1 -0
- metadata +24 -17
- data/lib/datadog/core/remote/transport/http/api.rb +0 -53
- data/lib/datadog/core/telemetry/transport/http/api.rb +0 -43
- data/lib/datadog/core/transport/http/api/spec.rb +0 -36
- data/lib/datadog/data_streams/transport/http/api.rb +0 -33
- data/lib/datadog/data_streams/transport/http/client.rb +0 -21
- data/lib/datadog/di/transport/http/api.rb +0 -42
- data/lib/datadog/opentelemetry/api/baggage.rbs +0 -26
- data/lib/datadog/tracing/transport/http/api.rb +0 -44
|
@@ -14,6 +14,7 @@ require_relative '../remote/component'
|
|
|
14
14
|
require_relative '../../tracing/component'
|
|
15
15
|
require_relative '../../profiling/component'
|
|
16
16
|
require_relative '../../appsec/component'
|
|
17
|
+
require_relative '../../ai_guard/component'
|
|
17
18
|
require_relative '../../di/component'
|
|
18
19
|
require_relative '../../open_feature/component'
|
|
19
20
|
require_relative '../../error_tracking/component'
|
|
@@ -107,6 +108,7 @@ module Datadog
|
|
|
107
108
|
:error_tracking,
|
|
108
109
|
:dynamic_instrumentation,
|
|
109
110
|
:appsec,
|
|
111
|
+
:ai_guard,
|
|
110
112
|
:agent_info,
|
|
111
113
|
:data_streams,
|
|
112
114
|
:open_feature
|
|
@@ -143,6 +145,7 @@ module Datadog
|
|
|
143
145
|
@runtime_metrics = self.class.build_runtime_metrics_worker(settings, @logger, telemetry)
|
|
144
146
|
@health_metrics = self.class.build_health_metrics(settings, @logger, telemetry)
|
|
145
147
|
@appsec = Datadog::AppSec::Component.build_appsec_component(settings, telemetry: telemetry)
|
|
148
|
+
@ai_guard = Datadog::AIGuard::Component.build(settings, logger: @logger, telemetry: telemetry)
|
|
146
149
|
@open_feature = OpenFeature::Component.build(settings, agent_settings, logger: @logger, telemetry: telemetry)
|
|
147
150
|
@dynamic_instrumentation = Datadog::DI::Component.build(settings, agent_settings, @logger, telemetry: telemetry)
|
|
148
151
|
@error_tracking = Datadog::ErrorTracking::Component.build(settings, @tracer, @logger)
|
|
@@ -209,6 +212,9 @@ module Datadog
|
|
|
209
212
|
# Decommission AppSec
|
|
210
213
|
appsec&.shutdown!
|
|
211
214
|
|
|
215
|
+
# Shutdown AIGuard component
|
|
216
|
+
ai_guard&.shutdown!
|
|
217
|
+
|
|
212
218
|
# Shutdown the old tracer, unless it's still being used.
|
|
213
219
|
# (e.g. a custom tracer instance passed in.)
|
|
214
220
|
tracer.shutdown! unless replacement && tracer.equal?(replacement.tracer)
|
|
@@ -9,7 +9,7 @@ module Datadog
|
|
|
9
9
|
class ConfigHelper
|
|
10
10
|
def initialize(
|
|
11
11
|
source_env: ENV,
|
|
12
|
-
supported_configurations:
|
|
12
|
+
supported_configurations: SUPPORTED_CONFIGURATION_NAMES,
|
|
13
13
|
aliases: ALIASES,
|
|
14
14
|
alias_to_canonical: ALIAS_TO_CANONICAL,
|
|
15
15
|
raise_on_unknown_env_var: false
|
|
@@ -21,12 +21,12 @@ module Datadog
|
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
private_class_method def self.log_deprecated_environment_variables(logger, source_env, source_name, deprecations, alias_to_canonical)
|
|
24
|
-
deprecations.each do |deprecated_env_var
|
|
24
|
+
deprecations.each do |deprecated_env_var|
|
|
25
25
|
next unless source_env.key?(deprecated_env_var)
|
|
26
26
|
|
|
27
27
|
Datadog::Core.log_deprecation(disallowed_next_major: false, logger: logger) do
|
|
28
28
|
"#{deprecated_env_var} #{source_name} variable is deprecated" +
|
|
29
|
-
(alias_to_canonical[deprecated_env_var] ? ", use #{alias_to_canonical[deprecated_env_var]} instead." : ".
|
|
29
|
+
(alias_to_canonical[deprecated_env_var] ? ", use #{alias_to_canonical[deprecated_env_var]} instead." : ".")
|
|
30
30
|
end
|
|
31
31
|
end
|
|
32
32
|
end
|
|
@@ -42,7 +42,8 @@ module Datadog
|
|
|
42
42
|
# Acts as DSL for building OptionDefinitions
|
|
43
43
|
# @public_api
|
|
44
44
|
class Builder
|
|
45
|
-
|
|
45
|
+
# Steep: https://github.com/soutaro/steep/issues/1880
|
|
46
|
+
InvalidOptionError = Class.new(StandardError) # steep:ignore IncompatibleAssignment
|
|
46
47
|
|
|
47
48
|
attr_reader \
|
|
48
49
|
:helpers
|
|
@@ -119,7 +120,8 @@ module Datadog
|
|
|
119
120
|
env_parser(&options[:env_parser]) if options.key?(:env_parser)
|
|
120
121
|
after_set(&options[:after_set]) if options.key?(:after_set)
|
|
121
122
|
resetter(&options[:resetter]) if options.key?(:resetter)
|
|
122
|
-
|
|
123
|
+
# Steep: https://github.com/soutaro/steep/issues/1979
|
|
124
|
+
setter(&options[:setter]) if options.key?(:setter) # steep:ignore BlockTypeMismatch
|
|
123
125
|
type(options[:type], **(options[:type_options] || {})) if options.key?(:type)
|
|
124
126
|
end
|
|
125
127
|
|
|
@@ -40,14 +40,16 @@ module Datadog
|
|
|
40
40
|
|
|
41
41
|
def default_helpers(name)
|
|
42
42
|
option_name = name.to_sym
|
|
43
|
-
#
|
|
44
|
-
|
|
43
|
+
# Steep: https://github.com/soutaro/steep/issues/335
|
|
44
|
+
# @type var opt_getter: Configuration::OptionDefinition::generic_proc
|
|
45
|
+
opt_getter = proc do # steep:ignore IncompatibleAssignment
|
|
45
46
|
# These Procs uses `get/set_option`, but we only add them to the OptionDefinition helpers here.
|
|
46
47
|
# Steep is right that these methods are not defined, but we only run these Procs in instance context.
|
|
47
48
|
get_option(option_name) # steep:ignore NoMethod
|
|
48
49
|
end
|
|
49
|
-
#
|
|
50
|
-
|
|
50
|
+
# Steep: https://github.com/soutaro/steep/issues/335
|
|
51
|
+
# @type var opt_setter: Configuration::OptionDefinition::generic_proc
|
|
52
|
+
opt_setter = proc do |value| # steep:ignore IncompatibleAssignment
|
|
51
53
|
set_option(option_name, value) # steep:ignore NoMethod
|
|
52
54
|
end
|
|
53
55
|
{
|
|
@@ -127,7 +129,8 @@ module Datadog
|
|
|
127
129
|
end
|
|
128
130
|
end
|
|
129
131
|
|
|
130
|
-
|
|
132
|
+
# Steep: https://github.com/soutaro/steep/issues/1880
|
|
133
|
+
InvalidOptionError = Class.new(StandardError) # steep:ignore IncompatibleAssignment
|
|
131
134
|
end
|
|
132
135
|
end
|
|
133
136
|
end
|
|
@@ -170,6 +170,20 @@ module Datadog
|
|
|
170
170
|
o.env Core::Environment::Ext::ENV_ENVIRONMENT
|
|
171
171
|
end
|
|
172
172
|
|
|
173
|
+
# Configuration for container environments. For internal use only.
|
|
174
|
+
# @!visibility private
|
|
175
|
+
settings :container do
|
|
176
|
+
# Data supplied by the container runner to assist in uniquely identifying this process.
|
|
177
|
+
# Used in [Origin Detection](https://docs.datadoghq.com/developers/dogstatsd/unix_socket/?tab=host#origin-detection)
|
|
178
|
+
#
|
|
179
|
+
# @default `DD_EXTERNAL_ENV` environment variable, otherwise `nil`
|
|
180
|
+
# @return [String,nil]
|
|
181
|
+
option :external_env do |o|
|
|
182
|
+
o.type :string, nilable: true
|
|
183
|
+
o.env Core::Environment::Ext::ENV_EXTERNAL_ENV
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
173
187
|
# Internal {Datadog::Statsd} metrics collection.
|
|
174
188
|
#
|
|
175
189
|
# @public_api
|
|
@@ -293,9 +307,6 @@ module Datadog
|
|
|
293
307
|
# for Ruby versions 2.x, 3.1.4+, 3.2.3+ and 3.3.0+
|
|
294
308
|
# (more details in {Datadog::Profiling::Component.enable_gc_profiling?})
|
|
295
309
|
#
|
|
296
|
-
# @warn Due to a VM bug in the Ractor implementation (https://bugs.ruby-lang.org/issues/19112) this feature
|
|
297
|
-
# stops working when Ractors get garbage collected.
|
|
298
|
-
#
|
|
299
310
|
# @default `DD_PROFILING_GC_ENABLED` environment variable, otherwise `true`
|
|
300
311
|
option :gc_enabled do |o|
|
|
301
312
|
o.type :bool
|
|
@@ -8,8 +8,13 @@ require 'set'
|
|
|
8
8
|
module Datadog
|
|
9
9
|
module Core
|
|
10
10
|
module Configuration
|
|
11
|
-
|
|
11
|
+
SUPPORTED_CONFIGURATION_NAMES =
|
|
12
12
|
Set["DD_AGENT_HOST",
|
|
13
|
+
"DD_AI_GUARD_ENABLED",
|
|
14
|
+
"DD_AI_GUARD_ENDPOINT",
|
|
15
|
+
"DD_AI_GUARD_MAX_CONTENT_SIZE",
|
|
16
|
+
"DD_AI_GUARD_MAX_MESSAGES_LENGTH",
|
|
17
|
+
"DD_AI_GUARD_TIMEOUT",
|
|
13
18
|
"DD_API_KEY",
|
|
14
19
|
"DD_API_SECURITY_ENABLED",
|
|
15
20
|
"DD_API_SECURITY_ENDPOINT_COLLECTION_ENABLED",
|
|
@@ -34,6 +39,7 @@ module Datadog
|
|
|
34
39
|
"DD_APPSEC_TRACE_RATE_LIMIT",
|
|
35
40
|
"DD_APPSEC_WAF_DEBUG",
|
|
36
41
|
"DD_APPSEC_WAF_TIMEOUT",
|
|
42
|
+
"DD_APP_KEY",
|
|
37
43
|
"DD_CRASHTRACKING_ENABLED",
|
|
38
44
|
"DD_DATA_STREAMS_ENABLED",
|
|
39
45
|
"DD_DBM_PROPAGATION_MODE",
|
|
@@ -47,6 +53,7 @@ module Datadog
|
|
|
47
53
|
"DD_ERROR_TRACKING_HANDLED_ERRORS_INCLUDE",
|
|
48
54
|
"DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED",
|
|
49
55
|
"DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED",
|
|
56
|
+
"DD_EXTERNAL_ENV",
|
|
50
57
|
"DD_GIT_COMMIT_SHA",
|
|
51
58
|
"DD_GIT_REPOSITORY_URL",
|
|
52
59
|
"DD_HEALTH_METRICS_ENABLED",
|
|
@@ -10,40 +10,67 @@ module Datadog
|
|
|
10
10
|
# about the current Linux container identity.
|
|
11
11
|
# @see https://man7.org/linux/man-pages/man7/cgroups.7.html
|
|
12
12
|
module Cgroup
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
:
|
|
17
|
-
:groups,
|
|
13
|
+
# A parsed cgroup entry from /proc/<pid>/cgroup
|
|
14
|
+
Entry = Struct.new(
|
|
15
|
+
:hierarchy,
|
|
16
|
+
:controllers,
|
|
18
17
|
:path,
|
|
19
|
-
:
|
|
18
|
+
:inode
|
|
20
19
|
)
|
|
21
20
|
|
|
22
21
|
module_function
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
end
|
|
34
|
-
rescue => e
|
|
35
|
-
Datadog.logger.error(
|
|
36
|
-
"Error while parsing cgroup. Cause: #{e.class.name} #{e.message} Location: #{Array(e.backtrace).first}"
|
|
37
|
-
)
|
|
23
|
+
# Parses the /proc/self/cgroup file,
|
|
24
|
+
# @return [Array<Entry>] one entry for each valid cgroup line
|
|
25
|
+
def entries
|
|
26
|
+
filepath = '/proc/self/cgroup'
|
|
27
|
+
return [] unless File.exist?(filepath)
|
|
28
|
+
|
|
29
|
+
ret = []
|
|
30
|
+
File.foreach(filepath) do |entry_line|
|
|
31
|
+
ret << parse(entry_line) unless entry_line.empty?
|
|
38
32
|
end
|
|
33
|
+
ret
|
|
39
34
|
end
|
|
40
35
|
|
|
41
|
-
|
|
42
|
-
|
|
36
|
+
# Parses a single cgroup entry from /proc/<pid>/cgroup.
|
|
37
|
+
#
|
|
38
|
+
# Files can have cgroup v1 and v2 entries mixed. Their format is the same.
|
|
39
|
+
#
|
|
40
|
+
# Each entry has 3 colon-separated fields:
|
|
41
|
+
# hierarchy-ID:controllers:path
|
|
42
|
+
# Examples:
|
|
43
|
+
# 10:memory:/docker/1234567890abcdef (cgroup v1)
|
|
44
|
+
# 0::/docker/1234567890abcdef (cgroup v2)
|
|
45
|
+
#
|
|
46
|
+
# @see https://man7.org/linux/man-pages/man7/cgroups.7.html#:~:text=%2Fproc%2Fpid%2Fcgroup
|
|
47
|
+
# @return [Entry]
|
|
48
|
+
def parse(entry_line)
|
|
49
|
+
hierarchy, controllers, path = entry_line.split(':', 3)
|
|
43
50
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
51
|
+
Entry.new(
|
|
52
|
+
hierarchy,
|
|
53
|
+
controllers,
|
|
54
|
+
path,
|
|
55
|
+
inode_for(controllers, path)
|
|
56
|
+
)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# We can find the container inode by running a file stat on the cgroup filesystem path.
|
|
60
|
+
# Example:
|
|
61
|
+
# For the entry `0:cpu:/docker/abc123`,
|
|
62
|
+
# we read `stat -c '%i' /sys/fs/cgroup/cpu/docker/abc123`
|
|
63
|
+
def inode_for(controllers, path)
|
|
64
|
+
return if controllers.nil? || path.nil?
|
|
65
|
+
|
|
66
|
+
# In cgroup v1, when multiple controllers are co-mounted, the controllers
|
|
67
|
+
# becomes part of the directory name (with commas preserved).
|
|
68
|
+
# Example entry:
|
|
69
|
+
# For the line "10:cpu,cpuacct:/docker/abc123", the path is
|
|
70
|
+
# "/sys/fs/cgroup/cpu,cpuacct/docker/abc123"
|
|
71
|
+
inode_path = File.join('/sys/fs/cgroup', controllers, path)
|
|
72
|
+
|
|
73
|
+
File.stat(inode_path).ino if File.exist?(inode_path)
|
|
47
74
|
end
|
|
48
75
|
end
|
|
49
76
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'cgroup'
|
|
4
|
+
require_relative 'ext'
|
|
4
5
|
|
|
5
6
|
module Datadog
|
|
6
7
|
module Core
|
|
@@ -15,73 +16,166 @@ module Datadog
|
|
|
15
16
|
CONTAINER_REGEX = /(?<container>#{UUID_PATTERN}|#{CONTAINER_PATTERN})(?:.scope)?$/.freeze
|
|
16
17
|
FARGATE_14_CONTAINER_REGEX = /(?<container>[0-9a-f]{32}-[0-9]{1,10})/.freeze
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
# From https://github.com/torvalds/linux/blob/5859a2b1991101d6b978f3feb5325dad39421f29/include/linux/proc_ns.h#L41-L49
|
|
20
|
+
# Currently, the host namespace inode number is hardcoded.
|
|
21
|
+
# We use it to determine if we're running in the host namespace.
|
|
22
|
+
# This detection approach does not work when running in
|
|
23
|
+
# ["Docker-in-Docker"](https://www.docker.com/resources/docker-in-docker-containerized-ci-workflows-dockercon-2023/).
|
|
24
|
+
HOST_CGROUP_NAMESPACE_INODE = 0xEFFFFFFB
|
|
25
|
+
|
|
26
|
+
Entry = Struct.new(
|
|
19
27
|
:platform,
|
|
28
|
+
:task_uid,
|
|
20
29
|
:container_id,
|
|
21
|
-
:
|
|
30
|
+
:inode
|
|
22
31
|
)
|
|
23
32
|
|
|
24
33
|
module_function
|
|
25
34
|
|
|
35
|
+
# Returns HTTP headers representing container information.
|
|
36
|
+
# These can used in any Datadog request that requires origin detection.
|
|
37
|
+
# This is the recommended method to call to get container information.
|
|
38
|
+
def to_headers
|
|
39
|
+
headers = {}
|
|
40
|
+
headers["Datadog-Container-ID"] = container_id if container_id
|
|
41
|
+
headers["Datadog-Entity-ID"] = entity_id if entity_id
|
|
42
|
+
headers["Datadog-External-Env"] = external_env if external_env
|
|
43
|
+
headers
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Container ID, prefixed with "ci-" or Inode, prefixed with "in-".
|
|
47
|
+
def entity_id
|
|
48
|
+
if container_id
|
|
49
|
+
"ci-#{container_id}"
|
|
50
|
+
elsif inode
|
|
51
|
+
"in-#{inode}"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# External data supplied by the Datadog Cluster Agent Admission Controller.
|
|
56
|
+
# @see {Ext::ENV_EXTERNAL_ENV} for more details.
|
|
57
|
+
def external_env
|
|
58
|
+
Datadog.configuration.container.external_env
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# The container orchestration platform or runtime environment.
|
|
62
|
+
#
|
|
63
|
+
# Examples: Docker, Kubernetes, AWS Fargate, LXC, etc.
|
|
64
|
+
#
|
|
65
|
+
# @return [String, nil] The platform name (e.g., "docker", "kubepods", "fargate"), or nil if not containerized
|
|
26
66
|
def platform
|
|
27
|
-
|
|
67
|
+
entry.platform
|
|
28
68
|
end
|
|
29
69
|
|
|
70
|
+
# The unique identifier of the current container in the container environment.
|
|
71
|
+
#
|
|
72
|
+
# @return [String, nil] The container ID, or nil if not running in a containerized environment
|
|
30
73
|
def container_id
|
|
31
|
-
|
|
74
|
+
entry.container_id
|
|
32
75
|
end
|
|
33
76
|
|
|
77
|
+
# The unique identifier of the task or pod containing this container.
|
|
78
|
+
#
|
|
79
|
+
# In Kubernetes, this is the Pod UID; in AWS ECS/Fargate, the task ID.
|
|
80
|
+
# Used to identify higher-level workloads beyond this container,
|
|
81
|
+
# enabling correlation across container restarts and multi-container applications.
|
|
82
|
+
#
|
|
83
|
+
# @return [String, nil] The task/pod UID, or nil if not available in the current environment
|
|
34
84
|
def task_uid
|
|
35
|
-
|
|
85
|
+
entry.task_uid
|
|
36
86
|
end
|
|
37
87
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
88
|
+
# A unique identifier for the execution context (container or host namespace).
|
|
89
|
+
#
|
|
90
|
+
# Used as a fallback identifier when {#container_id} is unavailable.
|
|
91
|
+
#
|
|
92
|
+
# @return [Integer, nil] The namespace inode, or nil if unavailable
|
|
93
|
+
def inode
|
|
94
|
+
entry.inode
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Checks if the current process is running on the host cgroup namespace.
|
|
98
|
+
# This indicates that the process is not running inside a container.
|
|
99
|
+
# When unsure, we return `false` (not running on host).
|
|
100
|
+
def running_on_host?
|
|
101
|
+
return @running_on_host if defined?(@running_on_host)
|
|
102
|
+
|
|
103
|
+
@running_on_host = begin
|
|
104
|
+
if File.exist?('/proc/self/ns/cgroup')
|
|
105
|
+
File.stat('/proc/self/ns/cgroup').ino == HOST_CGROUP_NAMESPACE_INODE
|
|
106
|
+
else
|
|
107
|
+
false
|
|
108
|
+
end
|
|
109
|
+
rescue => e
|
|
110
|
+
Datadog.logger.debug(
|
|
111
|
+
"Error while checking cgroup namespace. Cause: #{e.class.name} #{e.message} Location: #{Array(e.backtrace).first}"
|
|
112
|
+
)
|
|
113
|
+
false
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# All cgroup entries have the same container identity.
|
|
118
|
+
# The first valid one is sufficient.
|
|
119
|
+
# v2 entries are preferred over v1.
|
|
120
|
+
def entry
|
|
121
|
+
return @entry if defined?(@entry)
|
|
122
|
+
|
|
123
|
+
# Scan all v2 entries first, only then falling back to v1 entries.
|
|
124
|
+
#
|
|
125
|
+
# To do this, we {Enumerable#partition} the list between v1 and v2,
|
|
126
|
+
# with a `true` predicate for v2 entries, making v2 first
|
|
127
|
+
# partition returned.
|
|
128
|
+
#
|
|
129
|
+
# All v2 entries have the `hierarchy` set to zero.
|
|
130
|
+
# v1 entries have a non-zero `hierarchy`.
|
|
131
|
+
entries = Cgroup.entries.partition { |d| d.hierarchy == '0' }.flatten(1)
|
|
132
|
+
entries.each do |entry_obj|
|
|
133
|
+
path = entry_obj.path
|
|
134
|
+
next unless path
|
|
135
|
+
|
|
136
|
+
# To ease handling, remove the emtpy leading "",
|
|
137
|
+
# as `path` starts with a "/".
|
|
138
|
+
path.delete_prefix!('/')
|
|
139
|
+
parts = path.split('/')
|
|
140
|
+
|
|
141
|
+
# With not path information, we can still use the inode
|
|
142
|
+
if parts.empty? && entry_obj.inode && !running_on_host?
|
|
143
|
+
return @entry = Entry.new(nil, nil, nil, entry_obj.inode)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
platform = parts[0][PLATFORM_REGEX, :platform]
|
|
147
|
+
|
|
148
|
+
# Extract container_id and task_uid based on path structure
|
|
149
|
+
container_id = task_uid = nil
|
|
150
|
+
if parts.length >= 2
|
|
151
|
+
# Try standard container regex first
|
|
152
|
+
if (container_id = parts[-1][CONTAINER_REGEX, :container])
|
|
153
|
+
# For 3+ parts, also extract task_uid
|
|
154
|
+
if parts.length > 2
|
|
63
155
|
task_uid = parts[-2][POD_REGEX, :pod] || parts[1][POD_REGEX, :pod]
|
|
64
|
-
else
|
|
65
|
-
container_id = parts[-1][FARGATE_14_CONTAINER_REGEX, :container]
|
|
66
156
|
end
|
|
157
|
+
else
|
|
158
|
+
# Fall back to Fargate regex
|
|
159
|
+
container_id = parts[-1][FARGATE_14_CONTAINER_REGEX, :container]
|
|
67
160
|
end
|
|
161
|
+
end
|
|
68
162
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
descriptor.task_uid = task_uid
|
|
76
|
-
|
|
77
|
-
break
|
|
163
|
+
# container_id is a better container identifier than inode.
|
|
164
|
+
# We MUST only populate one of them, to avoid container identification ambiguity.
|
|
165
|
+
if container_id
|
|
166
|
+
return @entry = Entry.new(platform, task_uid, container_id)
|
|
167
|
+
elsif entry_obj.inode && !running_on_host?
|
|
168
|
+
return @entry = Entry.new(platform, task_uid, nil, entry_obj.inode)
|
|
78
169
|
end
|
|
79
|
-
rescue => e
|
|
80
|
-
Datadog.logger.error(
|
|
81
|
-
"Error while parsing container info. Cause: #{e.class.name} #{e.message} " \
|
|
82
|
-
"Location: #{Array(e.backtrace).first}"
|
|
83
|
-
)
|
|
84
170
|
end
|
|
171
|
+
|
|
172
|
+
@entry = Entry.new # Empty entry if no valid cgroup entry is found
|
|
173
|
+
rescue => e
|
|
174
|
+
Datadog.logger.debug(
|
|
175
|
+
"Error while reading container entry. Cause: #{e.class.name} #{e.message} Location: #{Array(e.backtrace).first}"
|
|
176
|
+
)
|
|
177
|
+
@entry = Entry.new unless defined?(@entry)
|
|
178
|
+
@entry
|
|
85
179
|
end
|
|
86
180
|
end
|
|
87
181
|
end
|
|
@@ -14,6 +14,14 @@ module Datadog
|
|
|
14
14
|
# @return [String] comma-separated normalized key:value pairs
|
|
15
15
|
def self.serialized
|
|
16
16
|
return @serialized if defined?(@serialized)
|
|
17
|
+
|
|
18
|
+
@serialized = tags.join(',').freeze
|
|
19
|
+
end
|
|
20
|
+
|
|
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
|
|
23
|
+
def self.tags
|
|
24
|
+
return @tags if defined?(@tags)
|
|
17
25
|
tags = []
|
|
18
26
|
|
|
19
27
|
workdir = TagNormalizer.normalize_process_value(entrypoint_workdir.to_s)
|
|
@@ -27,7 +35,7 @@ module Datadog
|
|
|
27
35
|
|
|
28
36
|
tags << "#{Environment::Ext::TAG_ENTRYPOINT_TYPE}:#{TagNormalizer.normalize(entrypoint_type, remove_digit_start_char: false)}"
|
|
29
37
|
|
|
30
|
-
@
|
|
38
|
+
@tags = tags.freeze
|
|
31
39
|
end
|
|
32
40
|
|
|
33
41
|
# Returns the last segment of the working directory of the process
|
data/lib/datadog/core/error.rb
CHANGED
|
@@ -13,12 +13,12 @@ module Datadog
|
|
|
13
13
|
case value
|
|
14
14
|
# A Ruby {Exception} is the most common parameter type.
|
|
15
15
|
when Exception then new(value.class, value.message, full_backtrace(value))
|
|
16
|
-
#
|
|
17
|
-
|
|
18
|
-
when
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
# Steep: Steep doesn't like an array with up to 3 elements to be passed here: it thinks the array is unbounded.
|
|
17
|
+
when Array then new(*value) # steep:ignore UnexpectedPositionalArgument
|
|
18
|
+
when ->(v) { v.respond_to?(:message) }
|
|
19
|
+
# Steep: Steep can not follow the logic inside the lambda, thus it doesn't know `value` responds to `:message`.
|
|
20
|
+
# @type var value: _ContainsMessage
|
|
21
|
+
new(value.class, value.message)
|
|
22
22
|
when String then new(nil, value)
|
|
23
23
|
when Error then value
|
|
24
24
|
else Error.new # Blank error
|
data/lib/datadog/core/pin.rb
CHANGED
|
@@ -44,12 +44,16 @@ module Datadog
|
|
|
44
44
|
def onto(obj)
|
|
45
45
|
unless obj.respond_to? :datadog_pin=
|
|
46
46
|
obj.define_singleton_method(:datadog_pin=) do |pin|
|
|
47
|
+
# Steep: https://github.com/soutaro/steep/issues/380
|
|
48
|
+
# @type self: PinnedObject
|
|
47
49
|
@datadog_pin = pin
|
|
48
50
|
end
|
|
49
51
|
end
|
|
50
52
|
|
|
51
53
|
unless obj.respond_to? :datadog_pin
|
|
52
54
|
obj.define_singleton_method(:datadog_pin) do
|
|
55
|
+
# Steep: https://github.com/soutaro/steep/issues/380
|
|
56
|
+
# @type self: PinnedObject
|
|
53
57
|
@datadog_pin
|
|
54
58
|
end
|
|
55
59
|
end
|
|
@@ -81,6 +81,10 @@ module Datadog
|
|
|
81
81
|
|
|
82
82
|
return current_window_rate if @prev_conforming_messages.nil? || @prev_total_messages.nil?
|
|
83
83
|
|
|
84
|
+
# Steep: Due to https://github.com/soutaro/steep/issues/477,
|
|
85
|
+
# the previous nil check does not narrow type to Integer
|
|
86
|
+
# The annotation fixes the typing error, but it takes effect in the method
|
|
87
|
+
# @type ivar @prev_total_messages: Integer
|
|
84
88
|
(@conforming_messages.to_f + @prev_conforming_messages.to_f) / (@total_messages + @prev_total_messages)
|
|
85
89
|
end
|
|
86
90
|
|
|
@@ -154,7 +158,11 @@ module Datadog
|
|
|
154
158
|
if @current_window.nil?
|
|
155
159
|
@current_window = now
|
|
156
160
|
# If more than 1 second has past since last window, reset
|
|
157
|
-
|
|
161
|
+
#
|
|
162
|
+
# Steep: @current_window is a Float, but for some reason annotations does not work here
|
|
163
|
+
# Once a fix will be out for nil checks on instance variables, we can remove the ignore comment
|
|
164
|
+
# https://github.com/soutaro/steep/issues/477
|
|
165
|
+
elsif now - @current_window >= 1 # steep:ignore UnresolvedOverloading
|
|
158
166
|
@prev_conforming_messages = @conforming_messages
|
|
159
167
|
@prev_total_messages = @total_messages
|
|
160
168
|
@conforming_messages = 0
|
|
@@ -14,10 +14,11 @@ module Datadog
|
|
|
14
14
|
|
|
15
15
|
class SyncError < StandardError; end
|
|
16
16
|
|
|
17
|
-
attr_reader :transport, :repository, :id, :dispatcher, :logger
|
|
17
|
+
attr_reader :transport, :repository, :id, :dispatcher, :settings, :logger
|
|
18
18
|
|
|
19
|
-
def initialize(transport, capabilities,
|
|
19
|
+
def initialize(transport, capabilities, settings:, logger:, repository: Configuration::Repository.new)
|
|
20
20
|
@transport = transport
|
|
21
|
+
@settings = settings
|
|
21
22
|
@logger = logger
|
|
22
23
|
|
|
23
24
|
@repository = repository
|
|
@@ -97,7 +98,9 @@ module Datadog
|
|
|
97
98
|
content = contents.find_content(path, target)
|
|
98
99
|
|
|
99
100
|
# abort entirely if matching content not found
|
|
100
|
-
|
|
101
|
+
if content.nil?
|
|
102
|
+
raise SyncError, "no valid content for target at path '#{path}'"
|
|
103
|
+
end
|
|
101
104
|
|
|
102
105
|
# to be added or updated << config
|
|
103
106
|
# TODO: metadata (hash, version, etc...)
|
|
@@ -153,14 +156,19 @@ module Datadog
|
|
|
153
156
|
language: Core::Environment::Identity.lang,
|
|
154
157
|
tracer_version: tracer_version,
|
|
155
158
|
service: service_name,
|
|
156
|
-
env:
|
|
159
|
+
env: settings.env,
|
|
157
160
|
tags: client_tracer_tags,
|
|
158
161
|
}
|
|
159
162
|
|
|
160
|
-
app_version =
|
|
163
|
+
app_version = settings.version
|
|
161
164
|
|
|
162
165
|
client_tracer[:app_version] = app_version if app_version
|
|
163
166
|
|
|
167
|
+
if settings.experimental_propagate_process_tags_enabled
|
|
168
|
+
process_tags = Core::Environment::Process.tags
|
|
169
|
+
client_tracer[:process_tags] = process_tags if process_tags.any?
|
|
170
|
+
end
|
|
171
|
+
|
|
164
172
|
{
|
|
165
173
|
client: {
|
|
166
174
|
state: {
|
|
@@ -184,7 +192,7 @@ module Datadog
|
|
|
184
192
|
end
|
|
185
193
|
|
|
186
194
|
def service_name
|
|
187
|
-
|
|
195
|
+
settings.remote.service || settings.service
|
|
188
196
|
end
|
|
189
197
|
|
|
190
198
|
def tracer_version
|