datadog 2.33.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +30 -1
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +20 -0
- data/ext/datadog_profiling_native_extension/macos_sampler_thread.h +55 -0
- data/lib/datadog/appsec/component.rb +4 -1
- data/lib/datadog/appsec/compressed_json.rb +2 -2
- data/lib/datadog/appsec/contrib/aws_lambda/waf_addresses.rb +3 -3
- data/lib/datadog/appsec/contrib/rack/ext.rb +1 -1
- data/lib/datadog/core/configuration/components.rb +8 -1
- data/lib/datadog/core/configuration/settings.rb +6 -1
- data/lib/datadog/core/configuration/supported_configurations.rb +10 -0
- data/lib/datadog/core/environment/ext.rb +4 -0
- data/lib/datadog/core/environment/identity.rb +15 -1
- data/lib/datadog/core/environment/process.rb +48 -27
- data/lib/datadog/core/remote/client/capabilities.rb +11 -2
- data/lib/datadog/core/remote/transport/http/config.rb +5 -5
- data/lib/datadog/core/telemetry/request.rb +0 -2
- data/lib/datadog/core/transport/response.rb +1 -1
- data/lib/datadog/core/utils/{base64.rb → base64_codec.rb} +3 -2
- data/lib/datadog/core/utils/hash.rb +0 -23
- data/lib/datadog/core/utils/spawn_monkey_patch.rb +46 -16
- data/lib/datadog/data_streams/pathway_context.rb +3 -3
- data/lib/datadog/di/code_tracker.rb +43 -22
- data/lib/datadog/di/contrib/active_record.rb +6 -2
- data/lib/datadog/di/instrumenter.rb +24 -4
- data/lib/datadog/di/probe_notification_builder.rb +1 -1
- data/lib/datadog/di/remote.rb +4 -4
- data/lib/datadog/di/serializer.rb +5 -5
- data/lib/datadog/di/utils.rb +42 -14
- data/lib/datadog/opentelemetry/configuration/settings.rb +65 -0
- data/lib/datadog/opentelemetry/ext.rb +9 -0
- data/lib/datadog/opentelemetry/logs.rb +98 -0
- data/lib/datadog/opentelemetry/metrics.rb +10 -46
- data/lib/datadog/opentelemetry/sdk/configurator.rb +40 -0
- data/lib/datadog/opentelemetry/sdk/logs_exporter.rb +37 -0
- data/lib/datadog/opentelemetry/signal_configuration.rb +53 -0
- data/lib/datadog/opentelemetry.rb +1 -0
- data/lib/datadog/symbol_database/component.rb +409 -0
- data/lib/datadog/symbol_database/configuration.rb +2 -2
- data/lib/datadog/symbol_database/extractor.rb +29 -1
- data/lib/datadog/symbol_database/remote.rb +175 -0
- data/lib/datadog/symbol_database/scope_batcher.rb +8 -0
- data/lib/datadog/symbol_database/service_version.rb +11 -2
- data/lib/datadog/symbol_database/symbol.rb +6 -3
- data/lib/datadog/symbol_database/uploader.rb +62 -8
- data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +8 -0
- data/lib/datadog/tracing/contrib/active_record/events/sql.rb +0 -4
- data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +0 -4
- data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +0 -4
- data/lib/datadog/tracing/contrib/aws/instrumentation.rb +0 -5
- data/lib/datadog/tracing/contrib/dalli/instrumentation.rb +0 -5
- data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +0 -5
- data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +0 -5
- data/lib/datadog/tracing/contrib/ethon/multi_patch.rb +0 -8
- data/lib/datadog/tracing/contrib/excon/middleware.rb +0 -5
- data/lib/datadog/tracing/contrib/ext.rb +2 -3
- data/lib/datadog/tracing/contrib/faraday/middleware.rb +0 -5
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +0 -5
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +0 -5
- data/lib/datadog/tracing/contrib/http/instrumentation.rb +0 -5
- data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +0 -5
- data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +0 -5
- data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +0 -5
- data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +0 -5
- data/lib/datadog/tracing/contrib/opensearch/patcher.rb +0 -5
- data/lib/datadog/tracing/contrib/pg/instrumentation.rb +0 -5
- data/lib/datadog/tracing/contrib/presto/instrumentation.rb +0 -5
- data/lib/datadog/tracing/contrib/racecar/event.rb +0 -5
- data/lib/datadog/tracing/contrib/redis/tags.rb +0 -5
- data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +0 -5
- data/lib/datadog/tracing/contrib/sequel/utils.rb +0 -5
- data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +0 -5
- data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +0 -13
- data/lib/datadog/tracing/distributed/trace_context.rb +0 -28
- data/lib/datadog/tracing/metadata/ext.rb +3 -0
- data/lib/datadog/tracing/span_operation.rb +13 -0
- data/lib/datadog/tracing/trace_operation.rb +22 -0
- data/lib/datadog/tracing/tracer.rb +6 -0
- data/lib/datadog/version.rb +1 -1
- metadata +12 -5
|
@@ -3,32 +3,62 @@
|
|
|
3
3
|
module Datadog
|
|
4
4
|
module Core
|
|
5
5
|
module Utils
|
|
6
|
+
# Applies the Process.spawn wrapper used to merge additional environment variables
|
|
7
|
+
# into child processes.
|
|
6
8
|
module SpawnMonkeyPatch
|
|
7
|
-
# @param
|
|
8
|
-
def self.apply!(
|
|
9
|
-
@
|
|
9
|
+
# @param env_provider [#call] returns a Hash of env vars to merge into the child process
|
|
10
|
+
def self.apply!(env_provider:)
|
|
11
|
+
@env_provider = env_provider
|
|
12
|
+
|
|
13
|
+
# Idempotent: tests, reloads, or repeated Components init must not stack prepends.
|
|
14
|
+
return if ::Process.singleton_class.ancestors.include?(ProcessSpawnPatch)
|
|
15
|
+
|
|
10
16
|
::Process.singleton_class.prepend(ProcessSpawnPatch)
|
|
11
|
-
true
|
|
12
17
|
end
|
|
13
18
|
|
|
19
|
+
# Prepends `Process.spawn` to merge `env_provider` output into the child's environment hash.
|
|
14
20
|
module ProcessSpawnPatch
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
21
|
+
# The One and Only Correct Delegation Pattern
|
|
22
|
+
if RUBY_VERSION >= '3'
|
|
23
|
+
def spawn(*args, **kwargs) # steep:ignore DifferentMethodParameterKind
|
|
24
|
+
super(*SpawnMonkeyPatch.inject_envs(args), **kwargs)
|
|
25
|
+
end
|
|
26
|
+
else
|
|
27
|
+
def spawn(*args)
|
|
28
|
+
super(*SpawnMonkeyPatch.inject_envs(args))
|
|
29
|
+
end
|
|
30
|
+
ruby2_keywords :spawn if respond_to?(:ruby2_keywords, true)
|
|
18
31
|
end
|
|
19
32
|
end
|
|
20
33
|
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
34
|
+
# Merge the env vars from `env_provider` with the optional env `Hash` from {Process.spawn}.
|
|
35
|
+
#
|
|
36
|
+
# `env` is the first argument when it is a {Hash}; see MRI `spawn([env, ] *args, options)`:
|
|
37
|
+
# https://docs.ruby-lang.org/en/master/Process.html#method-c-spawn
|
|
38
|
+
#
|
|
39
|
+
# When there is **no** leading env Hash, MRI inherits the parent's `ENV`; we prepend only the
|
|
40
|
+
# `env_provider` hash so spawned children see parent env plus injections.
|
|
41
|
+
#
|
|
42
|
+
# When callers pass `unsetenv_others: true`, MRI only forwards the explicitly passed env Hash;
|
|
43
|
+
# replacing a missing hash with DATADOG_ENV.to_h would wrongly carry over parent variables.
|
|
44
|
+
# Prepending only the provider hash preserves `unsetenv_others` semantics.
|
|
45
|
+
#
|
|
46
|
+
# See https://docs.ruby-lang.org/en/master/Process.html#module-Process-label-Environment+Variables+-28-3Aunsetenv_others-29
|
|
47
|
+
#
|
|
48
|
+
# NOTE: `::Hash` (not bare `Hash`) is required because this module is nested under
|
|
49
|
+
# `Datadog::Core::Utils`, and `Datadog::Core::Utils::Hash` exists.
|
|
50
|
+
# Bare `Hash` resolves to that module via Module.nesting, making `Hash === some_hash`
|
|
51
|
+
# silently return `false`. See https://github.com/DataDog/dd-trace-rb/issues/5621.
|
|
52
|
+
def self.inject_envs(args)
|
|
53
|
+
provided_env = @env_provider.call
|
|
26
54
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
55
|
+
if ::Hash === args.first
|
|
56
|
+
args[0] = args.first.merge(provided_env)
|
|
57
|
+
else
|
|
58
|
+
args.unshift(provided_env)
|
|
59
|
+
end
|
|
30
60
|
|
|
31
|
-
|
|
61
|
+
args
|
|
32
62
|
end
|
|
33
63
|
end
|
|
34
64
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'stringio'
|
|
4
|
-
require_relative '../core/utils/
|
|
4
|
+
require_relative '../core/utils/base64_codec'
|
|
5
5
|
|
|
6
6
|
module Datadog
|
|
7
7
|
module DataStreams
|
|
@@ -34,7 +34,7 @@ module Datadog
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def encode_b64
|
|
37
|
-
Core::Utils::
|
|
37
|
+
Core::Utils::Base64Codec.strict_encode64(encode)
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
# Decode pathway context from base64 encoded string
|
|
@@ -42,7 +42,7 @@ module Datadog
|
|
|
42
42
|
return nil unless encoded_ctx && !encoded_ctx.empty?
|
|
43
43
|
|
|
44
44
|
begin
|
|
45
|
-
binary_data = Core::Utils::
|
|
45
|
+
binary_data = Core::Utils::Base64Codec.strict_decode64(encoded_ctx)
|
|
46
46
|
decode(binary_data)
|
|
47
47
|
rescue ArgumentError => e
|
|
48
48
|
# Invalid base64 encoding - may indicate version mismatch or corruption
|
|
@@ -249,23 +249,34 @@ module Datadog
|
|
|
249
249
|
exact = registry[suffix]
|
|
250
250
|
return [suffix, exact] if exact
|
|
251
251
|
|
|
252
|
-
|
|
253
|
-
loop
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
252
|
+
# Normalize Windows-style backslash separators (DEBUG-5111) upfront
|
|
253
|
+
# so the suffix-shortening loop's "/+" regex can strip leading
|
|
254
|
+
# components on probes whose sourceFile uses backslashes.
|
|
255
|
+
suffix = Utils.normalize_windows_separators(suffix)
|
|
256
|
+
|
|
257
|
+
# Per the design comment in utils.rb, attempt case-sensitive
|
|
258
|
+
# matching first (steps 5-6) and only fall back to case-insensitive
|
|
259
|
+
# matching (steps 7-8) when no case-sensitive match is found.
|
|
260
|
+
[false, true].each do |case_insensitive|
|
|
261
|
+
working_suffix = suffix.dup
|
|
262
|
+
loop do
|
|
263
|
+
inexact = []
|
|
264
|
+
registry.each do |path, iseq|
|
|
265
|
+
if Utils.path_matches_suffix?(path, working_suffix, case_insensitive: case_insensitive)
|
|
266
|
+
inexact << [path, iseq]
|
|
267
|
+
end
|
|
258
268
|
end
|
|
269
|
+
if inexact.length > 1
|
|
270
|
+
raise Error::MultiplePathsMatch, "Multiple paths matched requested suffix"
|
|
271
|
+
end
|
|
272
|
+
if inexact.any?
|
|
273
|
+
return inexact.first
|
|
274
|
+
end
|
|
275
|
+
break unless working_suffix.include?('/')
|
|
276
|
+
working_suffix.sub!(%r{.*/+}, '')
|
|
259
277
|
end
|
|
260
|
-
if inexact.length > 1
|
|
261
|
-
raise Error::MultiplePathsMatch, "Multiple paths matched requested suffix"
|
|
262
|
-
end
|
|
263
|
-
if inexact.any?
|
|
264
|
-
return inexact.first
|
|
265
|
-
end
|
|
266
|
-
return nil unless suffix.include?('/')
|
|
267
|
-
suffix.sub!(%r{.*/+}, '')
|
|
268
278
|
end
|
|
279
|
+
nil
|
|
269
280
|
end
|
|
270
281
|
end
|
|
271
282
|
|
|
@@ -365,15 +376,25 @@ module Datadog
|
|
|
365
376
|
# Exact match.
|
|
366
377
|
return suffix if paths.include?(suffix)
|
|
367
378
|
|
|
368
|
-
#
|
|
369
|
-
suffix
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
379
|
+
# Normalize Windows-style backslash separators (DEBUG-5111) upfront
|
|
380
|
+
# so the suffix-shortening loop's "/+" regex can strip leading
|
|
381
|
+
# components on probes whose sourceFile uses backslashes.
|
|
382
|
+
suffix = Utils.normalize_windows_separators(suffix)
|
|
383
|
+
|
|
384
|
+
# Suffix match. Per the design comment in utils.rb, attempt
|
|
385
|
+
# case-sensitive matching first (steps 5-6) and only fall back to
|
|
386
|
+
# case-insensitive (steps 7-8) when no case-sensitive match is found.
|
|
387
|
+
[false, true].each do |case_insensitive|
|
|
388
|
+
working_suffix = suffix.dup
|
|
389
|
+
loop do
|
|
390
|
+
matches = paths.select { |p| Utils.path_matches_suffix?(p, working_suffix, case_insensitive: case_insensitive) }
|
|
391
|
+
raise Error::MultiplePathsMatch, "Multiple paths matched requested suffix" if matches.length > 1
|
|
392
|
+
return matches.first if matches.any?
|
|
393
|
+
break unless working_suffix.include?('/')
|
|
394
|
+
working_suffix.sub!(%r{.*/+}, '')
|
|
395
|
+
end
|
|
376
396
|
end
|
|
397
|
+
nil
|
|
377
398
|
end
|
|
378
399
|
end
|
|
379
400
|
end
|
|
@@ -23,7 +23,11 @@ Datadog::DI::Serializer.register(
|
|
|
23
23
|
#
|
|
24
24
|
# +depth+ is the remaining depth for serializing collections and objects.
|
|
25
25
|
# It should always be an integer.
|
|
26
|
-
#
|
|
26
|
+
# Pass it through to +serialize_value+ when the structure you produce
|
|
27
|
+
# represents +value+ directly (a transparent wrapper); +serialize_value+
|
|
28
|
+
# decrements depth itself when it recurses into Array/Hash/object entries.
|
|
29
|
+
# Decrement +depth+ only if your wrapper introduces real additional
|
|
30
|
+
# nesting levels in the output.
|
|
27
31
|
# This serializer could also potentially do its own depth limiting.
|
|
28
32
|
#
|
|
29
33
|
# Steep: steep thinks all of the arguments are nil here
|
|
@@ -33,5 +37,5 @@ Datadog::DI::Serializer.register(
|
|
|
33
37
|
attributes: value.attributes,
|
|
34
38
|
new_record: value.new_record?,
|
|
35
39
|
}
|
|
36
|
-
serializer.serialize_value(value_to_serialize, depth: depth
|
|
40
|
+
serializer.serialize_value(value_to_serialize, depth: depth, type: value.class)
|
|
37
41
|
end
|
|
@@ -106,7 +106,7 @@ module Datadog
|
|
|
106
106
|
serializer = self.serializer
|
|
107
107
|
method_name = probe.method_name
|
|
108
108
|
loc = begin
|
|
109
|
-
cls.instance_method(method_name).source_location
|
|
109
|
+
cls.instance_method(method_name).source_location # steep:ignore ArgumentTypeMismatch
|
|
110
110
|
rescue NameError
|
|
111
111
|
# The target method is not defined.
|
|
112
112
|
# This could be because it will be explicitly defined later
|
|
@@ -618,12 +618,32 @@ module Datadog
|
|
|
618
618
|
def raise_if_probe_in_loaded_features(probe, line_no, code_tracker)
|
|
619
619
|
return unless probe.file
|
|
620
620
|
|
|
621
|
-
# Find the loaded path matching the probe file.
|
|
621
|
+
# Find the loaded path matching the probe file. Case-sensitive
|
|
622
|
+
# matching is attempted first, with case-insensitive matching as a
|
|
623
|
+
# fallback (see the design comment in utils.rb, steps 5-8). Leading
|
|
624
|
+
# directory components are stripped so probes whose sourceFile carries
|
|
625
|
+
# a source-repo prefix that does not exist on disk still resolve to
|
|
626
|
+
# the loaded file — matching the behavior of
|
|
627
|
+
# CodeTracker#iseqs_for_path_suffix.
|
|
622
628
|
loaded_path = if $LOADED_FEATURES.include?(probe.file)
|
|
623
629
|
probe.file
|
|
624
630
|
else
|
|
625
631
|
# Expensive suffix check.
|
|
626
|
-
|
|
632
|
+
suffix = Utils.normalize_windows_separators(probe.file)
|
|
633
|
+
found = nil #: ::String?
|
|
634
|
+
[false, true].each do |case_insensitive|
|
|
635
|
+
working_suffix = suffix.dup
|
|
636
|
+
loop do
|
|
637
|
+
found = $LOADED_FEATURES.find do |path|
|
|
638
|
+
Utils.path_matches_suffix?(path, working_suffix, case_insensitive: case_insensitive)
|
|
639
|
+
end
|
|
640
|
+
break if found
|
|
641
|
+
break unless working_suffix.include?('/')
|
|
642
|
+
working_suffix.sub!(%r{.*/+}, '')
|
|
643
|
+
end
|
|
644
|
+
break if found
|
|
645
|
+
end
|
|
646
|
+
found
|
|
627
647
|
end
|
|
628
648
|
|
|
629
649
|
return unless loaded_path
|
|
@@ -648,7 +668,7 @@ module Datadog
|
|
|
648
668
|
|
|
649
669
|
# TODO test that this resolves qualified names e.g. A::B
|
|
650
670
|
def symbolize_class_name(cls_name)
|
|
651
|
-
Object.const_get(cls_name)
|
|
671
|
+
Object.const_get(cls_name) # steep:ignore ArgumentTypeMismatch
|
|
652
672
|
rescue NameError => exc
|
|
653
673
|
raise Error::DITargetNotDefined, "Class not defined: #{cls_name}: #{exc.class}: #{exc.message}"
|
|
654
674
|
end
|
data/lib/datadog/di/remote.rb
CHANGED
|
@@ -42,14 +42,14 @@ module Datadog
|
|
|
42
42
|
changes.each do |change|
|
|
43
43
|
case change.type
|
|
44
44
|
when :insert
|
|
45
|
-
add_probe(change.content, component)
|
|
45
|
+
add_probe(change.content, component) # steep:ignore NoMethod
|
|
46
46
|
when :update
|
|
47
47
|
# We do not implement updates at the moment, remove the
|
|
48
48
|
# probe and reinstall.
|
|
49
|
-
remove_probe(change.content, component)
|
|
50
|
-
add_probe(change.content, component)
|
|
49
|
+
remove_probe(change.content, component) # steep:ignore NoMethod
|
|
50
|
+
add_probe(change.content, component) # steep:ignore NoMethod
|
|
51
51
|
when :delete
|
|
52
|
-
remove_probe(change.previous, component)
|
|
52
|
+
remove_probe(change.previous, component) # steep:ignore NoMethod
|
|
53
53
|
else
|
|
54
54
|
# This really should never happen since we generate the
|
|
55
55
|
# change types in the library.
|
|
@@ -255,7 +255,7 @@ module Datadog
|
|
|
255
255
|
|
|
256
256
|
serialized.update(value: value)
|
|
257
257
|
when Array
|
|
258
|
-
if depth
|
|
258
|
+
if depth <= 0
|
|
259
259
|
serialized.update(notCapturedReason: "depth")
|
|
260
260
|
else
|
|
261
261
|
max = settings.dynamic_instrumentation.max_capture_collection_size
|
|
@@ -271,7 +271,7 @@ module Datadog
|
|
|
271
271
|
serialized.update(elements: entries)
|
|
272
272
|
end
|
|
273
273
|
when Hash
|
|
274
|
-
if depth
|
|
274
|
+
if depth <= 0
|
|
275
275
|
serialized.update(notCapturedReason: "depth")
|
|
276
276
|
else
|
|
277
277
|
max = settings.dynamic_instrumentation.max_capture_collection_size
|
|
@@ -288,7 +288,7 @@ module Datadog
|
|
|
288
288
|
serialized.update(entries: entries)
|
|
289
289
|
end
|
|
290
290
|
else
|
|
291
|
-
if depth
|
|
291
|
+
if depth <= 0
|
|
292
292
|
serialized.update(notCapturedReason: "depth")
|
|
293
293
|
else
|
|
294
294
|
fields = {}
|
|
@@ -369,7 +369,7 @@ module Datadog
|
|
|
369
369
|
when String
|
|
370
370
|
serialize_string_or_symbol_for_message(value)
|
|
371
371
|
when Symbol
|
|
372
|
-
':' + serialize_string_or_symbol_for_message(value)
|
|
372
|
+
':' + serialize_string_or_symbol_for_message(value) # steep:ignore ArgumentTypeMismatch
|
|
373
373
|
when Array
|
|
374
374
|
return '...' if depth <= 0
|
|
375
375
|
|
|
@@ -482,7 +482,7 @@ module Datadog
|
|
|
482
482
|
if max % 2 == 0
|
|
483
483
|
upper += 1
|
|
484
484
|
end
|
|
485
|
-
value[0...max / 2 - 1] + '...' + value[upper...length]
|
|
485
|
+
value[0...max / 2 - 1] + '...' + value[upper...length] # steep:ignore NoMethod
|
|
486
486
|
end
|
|
487
487
|
else
|
|
488
488
|
value
|
data/lib/datadog/di/utils.rb
CHANGED
|
@@ -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
|
-
#
|
|
89
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
132
|
-
|
|
133
|
-
return
|
|
134
|
-
|
|
135
|
-
|
|
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,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
|