datadog 2.26.0 → 2.28.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 +49 -1
- data/ext/datadog_profiling_native_extension/clock_id_from_pthread.c +2 -1
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +7 -6
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +2 -2
- data/ext/datadog_profiling_native_extension/collectors_gc_profiling_helper.c +3 -2
- data/ext/datadog_profiling_native_extension/collectors_stack.c +6 -5
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +16 -12
- data/ext/datadog_profiling_native_extension/crashtracking_runtime_stacks.c +2 -2
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +48 -1
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +41 -0
- data/ext/datadog_profiling_native_extension/encoded_profile.c +2 -1
- data/ext/datadog_profiling_native_extension/heap_recorder.c +24 -24
- data/ext/datadog_profiling_native_extension/http_transport.c +10 -5
- data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +3 -22
- data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +0 -5
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +9 -8
- data/ext/datadog_profiling_native_extension/profiling.c +20 -15
- data/ext/datadog_profiling_native_extension/ruby_helpers.c +55 -44
- data/ext/datadog_profiling_native_extension/ruby_helpers.h +17 -5
- data/ext/datadog_profiling_native_extension/setup_signal_handler.c +8 -2
- data/ext/datadog_profiling_native_extension/setup_signal_handler.h +3 -0
- data/ext/datadog_profiling_native_extension/stack_recorder.c +16 -16
- data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.c +2 -1
- data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.h +5 -2
- data/ext/libdatadog_api/crashtracker.c +5 -8
- data/ext/libdatadog_api/datadog_ruby_common.c +48 -1
- data/ext/libdatadog_api/datadog_ruby_common.h +41 -0
- data/ext/libdatadog_api/ddsketch.c +4 -8
- data/ext/libdatadog_api/feature_flags.c +5 -5
- data/ext/libdatadog_api/helpers.h +27 -0
- data/ext/libdatadog_api/init.c +4 -0
- data/lib/datadog/ai_guard/configuration/settings.rb +13 -1
- data/lib/datadog/ai_guard/contrib/integration.rb +37 -0
- data/lib/datadog/ai_guard/contrib/ruby_llm/chat_instrumentation.rb +42 -0
- data/lib/datadog/ai_guard/contrib/ruby_llm/integration.rb +41 -0
- data/lib/datadog/ai_guard/contrib/ruby_llm/patcher.rb +30 -0
- data/lib/datadog/ai_guard.rb +2 -0
- data/lib/datadog/appsec/api_security/endpoint_collection/rails_collector.rb +8 -1
- data/lib/datadog/appsec/api_security/endpoint_collection/rails_route_serializer.rb +9 -2
- data/lib/datadog/appsec/component.rb +1 -1
- data/lib/datadog/appsec/context.rb +3 -3
- data/lib/datadog/appsec/contrib/active_record/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/active_record/patcher.rb +1 -1
- data/lib/datadog/appsec/contrib/excon/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/excon/patcher.rb +1 -1
- data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +47 -12
- data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +32 -15
- data/lib/datadog/appsec/contrib/rails/patcher.rb +10 -2
- data/lib/datadog/appsec/contrib/rest_client/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/rest_client/patcher.rb +1 -1
- data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +50 -14
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +5 -4
- data/lib/datadog/appsec/contrib/sinatra/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/sinatra/patcher.rb +4 -4
- data/lib/datadog/appsec/contrib/sinatra/patches/json_patch.rb +1 -1
- data/lib/datadog/appsec/ext.rb +2 -0
- data/lib/datadog/appsec/metrics/collector.rb +8 -3
- data/lib/datadog/appsec/metrics/exporter.rb +7 -0
- data/lib/datadog/appsec/metrics/telemetry.rb +7 -2
- data/lib/datadog/appsec/metrics.rb +5 -5
- data/lib/datadog/appsec/remote.rb +4 -4
- data/lib/datadog/appsec/utils/http/media_type.rb +37 -23
- data/lib/datadog/appsec.rb +7 -1
- data/lib/datadog/core/configuration/settings.rb +17 -0
- data/lib/datadog/core/configuration/supported_configurations.rb +2 -0
- data/lib/datadog/core/crashtracking/tag_builder.rb +6 -0
- data/lib/datadog/core/knuth_sampler.rb +57 -0
- data/lib/datadog/core/telemetry/logger.rb +2 -0
- data/lib/datadog/core/telemetry/logging.rb +20 -2
- data/lib/datadog/di/configuration/settings.rb +22 -0
- data/lib/datadog/di/redactor.rb +8 -1
- data/lib/datadog/profiling/component.rb +13 -0
- data/lib/datadog/profiling/exporter.rb +4 -0
- data/lib/datadog/profiling/ext/exec_monkey_patch.rb +32 -0
- data/lib/datadog/profiling/flush.rb +3 -0
- data/lib/datadog/profiling/profiler.rb +3 -5
- data/lib/datadog/profiling/scheduler.rb +8 -7
- data/lib/datadog/profiling/tag_builder.rb +1 -0
- data/lib/datadog/tracing/sampling/rate_sampler.rb +8 -19
- data/lib/datadog/version.rb +1 -1
- metadata +13 -6
|
@@ -4,14 +4,13 @@ module Datadog
|
|
|
4
4
|
module AppSec
|
|
5
5
|
module Utils
|
|
6
6
|
module HTTP
|
|
7
|
-
# Implementation of media type for
|
|
7
|
+
# Implementation of media type for HTTP headers
|
|
8
8
|
#
|
|
9
9
|
# See:
|
|
10
10
|
# - https://www.rfc-editor.org/rfc/rfc7231#section-5.3.1
|
|
11
11
|
# - https://www.rfc-editor.org/rfc/rfc7231#section-5.3.2
|
|
12
12
|
class MediaType
|
|
13
|
-
|
|
14
|
-
end
|
|
13
|
+
ParseError = Class.new(StandardError) # steep:ignore IncompatibleAssignment
|
|
15
14
|
|
|
16
15
|
WILDCARD = '*'
|
|
17
16
|
|
|
@@ -19,7 +18,7 @@ module Datadog
|
|
|
19
18
|
TOKEN_RE = /[-#$%&'*+.^_`|~A-Za-z0-9]+/.freeze
|
|
20
19
|
|
|
21
20
|
# See: https://www.rfc-editor.org/rfc/rfc7231#section-3.1.1.1
|
|
22
|
-
PARAMETER_RE = %r{
|
|
21
|
+
PARAMETER_RE = %r{
|
|
23
22
|
(?:
|
|
24
23
|
(?<parameter_name>#{TOKEN_RE})
|
|
25
24
|
=
|
|
@@ -46,39 +45,54 @@ module Datadog
|
|
|
46
45
|
|
|
47
46
|
attr_reader :type, :subtype, :parameters
|
|
48
47
|
|
|
49
|
-
def
|
|
50
|
-
|
|
48
|
+
def self.json?(media_type)
|
|
49
|
+
return false if media_type.nil? || media_type.empty?
|
|
51
50
|
|
|
52
|
-
|
|
51
|
+
match = MEDIA_TYPE_RE.match(media_type)
|
|
52
|
+
return false if match.nil?
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
@parameters = {}
|
|
54
|
+
subtype = match['subtype']
|
|
55
|
+
return false if subtype.nil? || subtype.empty?
|
|
57
56
|
|
|
58
|
-
|
|
57
|
+
subtype.downcase!
|
|
58
|
+
subtype == 'json' || subtype.end_with?('+json')
|
|
59
|
+
end
|
|
59
60
|
|
|
60
|
-
|
|
61
|
+
def initialize(media_type)
|
|
62
|
+
match = MEDIA_TYPE_RE.match(media_type)
|
|
63
|
+
raise ParseError, media_type.inspect if match.nil?
|
|
61
64
|
|
|
62
|
-
|
|
63
|
-
|
|
65
|
+
@type = match['type'] || WILDCARD
|
|
66
|
+
@type.downcase!
|
|
64
67
|
|
|
65
|
-
|
|
68
|
+
@subtype = match['subtype'] || WILDCARD
|
|
69
|
+
@subtype.downcase!
|
|
66
70
|
|
|
67
|
-
|
|
68
|
-
|
|
71
|
+
@parameters = {}
|
|
72
|
+
|
|
73
|
+
parameters = match['parameters']
|
|
74
|
+
return if parameters.nil? || parameters.empty?
|
|
69
75
|
|
|
70
|
-
|
|
76
|
+
parameters.scan(PARAMETER_RE) do |name, unquoted_value, quoted_value|
|
|
77
|
+
# NOTE: Order of unquoted_value and quoted_value does not matter,
|
|
78
|
+
# as they are mutually exclusive by the regex.
|
|
79
|
+
# @type var value: ::String?
|
|
80
|
+
value = unquoted_value || quoted_value
|
|
81
|
+
next if name.nil? || value.nil?
|
|
71
82
|
|
|
72
|
-
|
|
83
|
+
# See https://github.com/soutaro/steep/issues/2051
|
|
84
|
+
name.downcase! # steep:ignore NoMethod
|
|
85
|
+
value.downcase!
|
|
86
|
+
|
|
87
|
+
# See https://github.com/soutaro/steep/issues/2051
|
|
88
|
+
@parameters[name] = value # steep:ignore ArgumentTypeMismatch
|
|
73
89
|
end
|
|
74
90
|
end
|
|
75
91
|
|
|
76
92
|
def to_s
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
s << ';' << @parameters.map { |k, v| "#{k}=#{v}" }.join(';') if @parameters.count > 0
|
|
93
|
+
return "#{@type}/#{@subtype}" if @parameters.empty?
|
|
80
94
|
|
|
81
|
-
|
|
95
|
+
"#{@type}/#{@subtype};#{@parameters.map { |k, v| "#{k}=#{v}" }.join(";")}"
|
|
82
96
|
end
|
|
83
97
|
end
|
|
84
98
|
end
|
data/lib/datadog/appsec.rb
CHANGED
|
@@ -22,8 +22,14 @@ module Datadog
|
|
|
22
22
|
Datadog::AppSec::Context.active
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
+
# NOTE: This is a temporary workaround for type checking.
|
|
26
|
+
#
|
|
27
|
+
# We want to move from possible nil-component to the disabled-component
|
|
28
|
+
# on an initialization error. Technically, telemetry will be never
|
|
29
|
+
# used if AppSec was not able to initialize, so it's safe to assume
|
|
30
|
+
# that telemetry will never be used and will be nil at the same time.
|
|
25
31
|
def telemetry
|
|
26
|
-
components.appsec&.telemetry
|
|
32
|
+
components.appsec&.telemetry || components.telemetry
|
|
27
33
|
end
|
|
28
34
|
|
|
29
35
|
def security_engine
|
|
@@ -436,6 +436,23 @@ module Datadog
|
|
|
436
436
|
o.default true
|
|
437
437
|
end
|
|
438
438
|
|
|
439
|
+
# The profiler gathers data by sending `SIGPROF` unix signals to Ruby application threads.
|
|
440
|
+
#
|
|
441
|
+
# When using `Kernel#exec` on Linux, it can happen that a signal sent before calling `exec` arrives after
|
|
442
|
+
# the new process is running, causing it to fail with the `Profiling timer expired` error message.
|
|
443
|
+
# To avoid this, the profiler installs a monkey patch on `Kernel#exec` to stop profiling before actually
|
|
444
|
+
# calling `exec`.
|
|
445
|
+
# This monkey patch is available for Ruby 2.7+; let us know if you need it on earlier Rubies.
|
|
446
|
+
# For more details see https://github.com/DataDog/dd-trace-rb/issues/5101 .
|
|
447
|
+
#
|
|
448
|
+
# @default `DD_PROFILING_SHUTDOWN_ON_EXEC_ENABLED` environment variable as a boolean,
|
|
449
|
+
# otherwise `true`
|
|
450
|
+
option :shutdown_on_exec_enabled do |o|
|
|
451
|
+
o.env 'DD_PROFILING_SHUTDOWN_ON_EXEC_ENABLED'
|
|
452
|
+
o.type :bool
|
|
453
|
+
o.default true
|
|
454
|
+
end
|
|
455
|
+
|
|
439
456
|
# Configures how much wall-time overhead the profiler targets. The profiler will dynamically adjust the
|
|
440
457
|
# interval between samples it takes so as to try and maintain the property that it spends no longer than
|
|
441
458
|
# this amount of wall-clock time profiling. For example, with the default value of 2%, the profiler will
|
|
@@ -48,6 +48,7 @@ module Datadog
|
|
|
48
48
|
"DD_DYNAMIC_INSTRUMENTATION_PROBE_FILE",
|
|
49
49
|
"DD_DYNAMIC_INSTRUMENTATION_REDACTED_IDENTIFIERS",
|
|
50
50
|
"DD_DYNAMIC_INSTRUMENTATION_REDACTED_TYPES",
|
|
51
|
+
"DD_DYNAMIC_INSTRUMENTATION_REDACTION_EXCLUDED_IDENTIFIERS",
|
|
51
52
|
"DD_ENV",
|
|
52
53
|
"DD_ERROR_TRACKING_HANDLED_ERRORS",
|
|
53
54
|
"DD_ERROR_TRACKING_HANDLED_ERRORS_INCLUDE",
|
|
@@ -81,6 +82,7 @@ module Datadog
|
|
|
81
82
|
"DD_PROFILING_NO_SIGNALS_WORKAROUND_ENABLED",
|
|
82
83
|
"DD_PROFILING_OVERHEAD_TARGET_PERCENTAGE",
|
|
83
84
|
"DD_PROFILING_PREVIEW_OTEL_CONTEXT_ENABLED",
|
|
85
|
+
"DD_PROFILING_SHUTDOWN_ON_EXEC_ENABLED",
|
|
84
86
|
"DD_PROFILING_SIGHANDLER_SAMPLING_ENABLED",
|
|
85
87
|
"DD_PROFILING_SKIP_MYSQL2_CHECK",
|
|
86
88
|
"DD_PROFILING_TIMELINE_ENABLED",
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative '../tag_builder'
|
|
4
4
|
require_relative '../utils'
|
|
5
|
+
require_relative '../environment/process'
|
|
5
6
|
|
|
6
7
|
module Datadog
|
|
7
8
|
module Core
|
|
@@ -13,6 +14,11 @@ module Datadog
|
|
|
13
14
|
'is_crash' => 'true',
|
|
14
15
|
)
|
|
15
16
|
|
|
17
|
+
if settings.experimental_propagate_process_tags_enabled
|
|
18
|
+
process_tags = Environment::Process.serialized
|
|
19
|
+
hash['process_tags'] = process_tags unless process_tags.empty?
|
|
20
|
+
end
|
|
21
|
+
|
|
16
22
|
Utils.encode_tags(hash)
|
|
17
23
|
end
|
|
18
24
|
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Datadog
|
|
4
|
+
module Core
|
|
5
|
+
# Deterministic sampler using Knuth multiplicative hash algorithm.
|
|
6
|
+
#
|
|
7
|
+
# This sampler provides consistent sampling decisions based on an input value,
|
|
8
|
+
# ensuring the same input always produces the same sampling decision for a given rate.
|
|
9
|
+
#
|
|
10
|
+
# The algorithm multiplies the input by a large prime (Knuth factor), takes modulo
|
|
11
|
+
# to constrain to a fixed range, and compares against a threshold derived from the sample rate.
|
|
12
|
+
#
|
|
13
|
+
# @api private
|
|
14
|
+
# @see https://en.wikipedia.org/wiki/Hash_function#Multiplicative_hashing
|
|
15
|
+
class KnuthSampler
|
|
16
|
+
# Maximum unsigned 64-bit integer for uniform distribution across 64-bit input space.
|
|
17
|
+
UINT64_MAX = (1 << 64) - 1
|
|
18
|
+
UINT64_MODULO = 1 << 64
|
|
19
|
+
|
|
20
|
+
# Golden ratio constant for optimal distribution.
|
|
21
|
+
# @see https://en.wikipedia.org/wiki/Hash_function#Fibonacci_hashing
|
|
22
|
+
DEFAULT_KNUTH_FACTOR = 11400714819323198485
|
|
23
|
+
|
|
24
|
+
attr_reader :rate
|
|
25
|
+
|
|
26
|
+
# @param rate [Float] Sampling rate between +0.0+ and +1.0+ (inclusive).
|
|
27
|
+
# +0.0+ means no samples are kept; +1.0+ means all samples are kept.
|
|
28
|
+
# Invalid values fall back to +1.0+ (sample everything).
|
|
29
|
+
# @param knuth_factor [Integer] Multiplicative constant for hashing.
|
|
30
|
+
# Different factors produce different sampling distributions.
|
|
31
|
+
def initialize(rate = 1.0, knuth_factor: DEFAULT_KNUTH_FACTOR)
|
|
32
|
+
@knuth_factor = knuth_factor
|
|
33
|
+
|
|
34
|
+
rate = rate.to_f
|
|
35
|
+
unless rate >= 0.0 && rate <= 1.0
|
|
36
|
+
Datadog.logger.warn("Sample rate #{rate} is not between 0.0 and 1.0, falling back to 1.0")
|
|
37
|
+
rate = 1.0
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
@rate = rate
|
|
41
|
+
@threshold = @rate * UINT64_MAX
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Determines if the given input should be sampled.
|
|
45
|
+
#
|
|
46
|
+
# This method is deterministic: the same input value always produces
|
|
47
|
+
# the same result for a given sample rate and configuration.
|
|
48
|
+
#
|
|
49
|
+
# @param input [Integer] Value to determine sampling decision.
|
|
50
|
+
# Typically a trace ID or incrementing counter.
|
|
51
|
+
# @return [Boolean] +true+ if input should be sampled, +false+ otherwise
|
|
52
|
+
def sample?(input)
|
|
53
|
+
((input * @knuth_factor) % UINT64_MODULO) <= @threshold
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -14,10 +14,12 @@ module Datadog
|
|
|
14
14
|
# read: lib/datadog/core/telemetry/logging.rb
|
|
15
15
|
module Logger
|
|
16
16
|
class << self
|
|
17
|
+
# (see Datadog::Core::Telemetry::Logging#report)
|
|
17
18
|
def report(exception, level: :error, description: nil)
|
|
18
19
|
instance&.report(exception, level: level, description: description)
|
|
19
20
|
end
|
|
20
21
|
|
|
22
|
+
# (see Datadog::Core::Telemetry::Logging#error)
|
|
21
23
|
def error(description)
|
|
22
24
|
instance&.error(description)
|
|
23
25
|
end
|
|
@@ -45,11 +45,20 @@ module Datadog
|
|
|
45
45
|
end
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
+
# @param exception [Exception] The exception to report
|
|
49
|
+
# @param level [Symbol] The log level (:error, :warn, :info, :debug)
|
|
50
|
+
# @param description [String, nil] A low cardinality description, without dynamic data
|
|
48
51
|
def report(exception, level: :error, description: nil)
|
|
49
52
|
# Anonymous exceptions to be logged as <Class:0x00007f8b1c0b3b40>
|
|
50
|
-
message = +
|
|
53
|
+
message = +(exception.class.name || exception.class.inspect)
|
|
51
54
|
|
|
52
|
-
|
|
55
|
+
telemetry_message = message_for_telemetry(exception)
|
|
56
|
+
|
|
57
|
+
if description || telemetry_message
|
|
58
|
+
message << ':'
|
|
59
|
+
message << " #{description}" if description
|
|
60
|
+
message << " (#{telemetry_message})" if telemetry_message
|
|
61
|
+
end
|
|
53
62
|
|
|
54
63
|
event = Event::Log.new(
|
|
55
64
|
message: message,
|
|
@@ -65,6 +74,15 @@ module Datadog
|
|
|
65
74
|
|
|
66
75
|
log!(event)
|
|
67
76
|
end
|
|
77
|
+
|
|
78
|
+
private
|
|
79
|
+
|
|
80
|
+
# A constant string representing the exception
|
|
81
|
+
def message_for_telemetry(exception)
|
|
82
|
+
return unless exception.instance_variable_defined?(:@telemetry_message)
|
|
83
|
+
|
|
84
|
+
exception.instance_variable_get(:@telemetry_message)
|
|
85
|
+
end
|
|
68
86
|
end
|
|
69
87
|
end
|
|
70
88
|
end
|
|
@@ -45,6 +45,28 @@ module Datadog
|
|
|
45
45
|
o.default []
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
+
# An array of variable and key names to exclude from the
|
|
49
|
+
# built-in redaction list.
|
|
50
|
+
#
|
|
51
|
+
# This allows users to capture values of variables that would
|
|
52
|
+
# otherwise be redacted by the default identifier list.
|
|
53
|
+
# For example, if an application has a "session" variable
|
|
54
|
+
# that does not contain sensitive data, "session" can be added
|
|
55
|
+
# to this list to exclude it from redaction.
|
|
56
|
+
#
|
|
57
|
+
# The names will be normalized the same way as redacted_identifiers,
|
|
58
|
+
# by removing the following symbols: _, -, @, $, and then matched
|
|
59
|
+
# against the complete variable or key name while ignoring the case.
|
|
60
|
+
option :redaction_excluded_identifiers do |o|
|
|
61
|
+
o.env "DD_DYNAMIC_INSTRUMENTATION_REDACTION_EXCLUDED_IDENTIFIERS"
|
|
62
|
+
o.env_parser do |value|
|
|
63
|
+
value&.split(",")&.map(&:strip)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
o.type :array
|
|
67
|
+
o.default []
|
|
68
|
+
end
|
|
69
|
+
|
|
48
70
|
# An array of class names, values of which will be redacted from
|
|
49
71
|
# dynamic instrumentation snapshots. Example: FooClass.
|
|
50
72
|
# If a name is suffixed by '*', it becomes a wildcard and
|
data/lib/datadog/di/redactor.rb
CHANGED
|
@@ -13,6 +13,10 @@ module Datadog
|
|
|
13
13
|
# redaction. Additional names can be provided by the user via the
|
|
14
14
|
# settings.dynamic_instrumentation.redacted_identifiers setting or
|
|
15
15
|
# the DD_DYNAMIC_INSTRUMENTATION_REDACTED_IDENTIFIERS environment
|
|
16
|
+
# variable. Users can also exclude specific identifiers from the default
|
|
17
|
+
# redaction list via the
|
|
18
|
+
# settings.dynamic_instrumentation.redaction_excluded_identifiers setting or
|
|
19
|
+
# the DD_DYNAMIC_INSTRUMENTATION_REDACTION_EXCLUDED_IDENTIFIERS environment
|
|
16
20
|
# variable. Currently no class names are subject to redaction by default;
|
|
17
21
|
# class names can be provided via the
|
|
18
22
|
# settings.dynamic_instrumentation.redacted_type_names setting or
|
|
@@ -61,7 +65,10 @@ module Datadog
|
|
|
61
65
|
names.map! do |name|
|
|
62
66
|
normalize(name)
|
|
63
67
|
end
|
|
64
|
-
|
|
68
|
+
excluded = settings.dynamic_instrumentation.redaction_excluded_identifiers.map do |name|
|
|
69
|
+
normalize(name)
|
|
70
|
+
end
|
|
71
|
+
Set.new(names) - Set.new(excluded)
|
|
65
72
|
end
|
|
66
73
|
end
|
|
67
74
|
|
|
@@ -82,6 +82,10 @@ module Datadog
|
|
|
82
82
|
Datadog::Profiling::Ext::DirMonkeyPatches.apply!
|
|
83
83
|
end
|
|
84
84
|
|
|
85
|
+
if can_apply_exec_monkey_patch?(settings)
|
|
86
|
+
Datadog::Profiling::Ext::ExecMonkeyPatch.apply!
|
|
87
|
+
end
|
|
88
|
+
|
|
85
89
|
[profiler, {profiling_enabled: true}]
|
|
86
90
|
end
|
|
87
91
|
|
|
@@ -434,6 +438,15 @@ module Datadog
|
|
|
434
438
|
settings.profiling.advanced.dir_interruption_workaround_enabled
|
|
435
439
|
end
|
|
436
440
|
|
|
441
|
+
private_class_method def self.can_apply_exec_monkey_patch?(settings)
|
|
442
|
+
return false if RUBY_VERSION < "2.7"
|
|
443
|
+
|
|
444
|
+
# This file is 2.7+ only so we only require it here once we've checked the Ruby version
|
|
445
|
+
require "datadog/profiling/ext/exec_monkey_patch"
|
|
446
|
+
|
|
447
|
+
settings.profiling.advanced.shutdown_on_exec_enabled
|
|
448
|
+
end
|
|
449
|
+
|
|
437
450
|
private_class_method def self.enable_gvl_profiling?(settings, logger)
|
|
438
451
|
return false if RUBY_VERSION < "3.2"
|
|
439
452
|
|
|
@@ -70,6 +70,9 @@ module Datadog
|
|
|
70
70
|
|
|
71
71
|
uncompressed_code_provenance = code_provenance_collector.refresh.generate_json if code_provenance_collector
|
|
72
72
|
|
|
73
|
+
process_tags = Datadog.configuration.experimental_propagate_process_tags_enabled ?
|
|
74
|
+
Core::Environment::Process.serialized : ''
|
|
75
|
+
|
|
73
76
|
Flush.new(
|
|
74
77
|
start: start,
|
|
75
78
|
finish: finish,
|
|
@@ -80,6 +83,7 @@ module Datadog
|
|
|
80
83
|
settings: Datadog.configuration,
|
|
81
84
|
profile_seq: sequence_tracker.get_next,
|
|
82
85
|
).to_a,
|
|
86
|
+
process_tags: process_tags,
|
|
83
87
|
internal_metadata: internal_metadata.merge(
|
|
84
88
|
{
|
|
85
89
|
worker_stats: worker_stats,
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Datadog
|
|
4
|
+
module Profiling
|
|
5
|
+
module Ext
|
|
6
|
+
# The profiler gathers data by sending `SIGPROF` unix signals to Ruby application threads.
|
|
7
|
+
#
|
|
8
|
+
# When using `Kernel#exec` on Linux, it can happen that a signal sent before calling `exec` arrives after
|
|
9
|
+
# the new process is running, causing it to fail with the `Profiling timer expired` error message.
|
|
10
|
+
# To avoid this, the profiler installs a monkey patch on `Kernel#exec` to stop profiling before actually
|
|
11
|
+
# calling `exec`.
|
|
12
|
+
# This monkey patch is available for Ruby 2.7+; let us know if you need it on earlier Rubies.
|
|
13
|
+
# For more details see https://github.com/DataDog/dd-trace-rb/issues/5101 .
|
|
14
|
+
module ExecMonkeyPatch
|
|
15
|
+
def self.apply!
|
|
16
|
+
::Object.prepend(ObjectMonkeyPatch)
|
|
17
|
+
|
|
18
|
+
true
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
module ObjectMonkeyPatch
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def exec(...)
|
|
25
|
+
Datadog.send(:components, allow_initialization: false)&.profiler&.shutdown!(report_last_profile: false)
|
|
26
|
+
super
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -13,6 +13,7 @@ module Datadog
|
|
|
13
13
|
:code_provenance_file_name,
|
|
14
14
|
:code_provenance_data,
|
|
15
15
|
:tags_as_array,
|
|
16
|
+
:process_tags,
|
|
16
17
|
:internal_metadata_json,
|
|
17
18
|
:info_json
|
|
18
19
|
|
|
@@ -23,6 +24,7 @@ module Datadog
|
|
|
23
24
|
code_provenance_file_name:,
|
|
24
25
|
code_provenance_data:,
|
|
25
26
|
tags_as_array:,
|
|
27
|
+
process_tags:,
|
|
26
28
|
internal_metadata:,
|
|
27
29
|
info_json:
|
|
28
30
|
)
|
|
@@ -32,6 +34,7 @@ module Datadog
|
|
|
32
34
|
@code_provenance_file_name = code_provenance_file_name
|
|
33
35
|
@code_provenance_data = code_provenance_data
|
|
34
36
|
@tags_as_array = tags_as_array
|
|
37
|
+
@process_tags = process_tags
|
|
35
38
|
@internal_metadata_json = JSON.generate(internal_metadata)
|
|
36
39
|
@info_json = info_json
|
|
37
40
|
end
|
|
@@ -31,10 +31,11 @@ module Datadog
|
|
|
31
31
|
scheduler.start(on_failure_proc: proc { component_failed(:scheduler) })
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
def shutdown!
|
|
34
|
+
def shutdown!(report_last_profile: true)
|
|
35
35
|
Datadog.logger.debug("Shutting down profiler")
|
|
36
36
|
|
|
37
37
|
stop_worker
|
|
38
|
+
scheduler.disable_reporting unless report_last_profile
|
|
38
39
|
stop_scheduler
|
|
39
40
|
end
|
|
40
41
|
|
|
@@ -57,11 +58,8 @@ module Datadog
|
|
|
57
58
|
Datadog::Core::Telemetry::Logger
|
|
58
59
|
.error("Detected issue with profiler (#{failed_component} component), stopping profiling")
|
|
59
60
|
|
|
60
|
-
# We explicitly not stop the crash tracker in this situation, under the assumption that, if a component failed,
|
|
61
|
-
# we're operating in a degraded state and crash tracking may still be helpful.
|
|
62
|
-
|
|
63
61
|
if failed_component == :worker
|
|
64
|
-
scheduler.
|
|
62
|
+
scheduler.disable_reporting
|
|
65
63
|
stop_scheduler
|
|
66
64
|
elsif failed_component == :scheduler
|
|
67
65
|
stop_worker
|
|
@@ -24,7 +24,7 @@ module Datadog
|
|
|
24
24
|
attr_reader \
|
|
25
25
|
:exporter,
|
|
26
26
|
:transport,
|
|
27
|
-
:
|
|
27
|
+
:reporting_disabled
|
|
28
28
|
|
|
29
29
|
public
|
|
30
30
|
|
|
@@ -36,7 +36,7 @@ module Datadog
|
|
|
36
36
|
)
|
|
37
37
|
@exporter = exporter
|
|
38
38
|
@transport = transport
|
|
39
|
-
@
|
|
39
|
+
@reporting_disabled = false
|
|
40
40
|
@stop_requested = false
|
|
41
41
|
|
|
42
42
|
# Workers::Async::Thread settings
|
|
@@ -83,14 +83,15 @@ module Datadog
|
|
|
83
83
|
true
|
|
84
84
|
end
|
|
85
85
|
|
|
86
|
-
#
|
|
87
|
-
# if there is data to be flushed
|
|
88
|
-
|
|
89
|
-
|
|
86
|
+
# Called by the Profiler when it wants to prevent any further reporting,
|
|
87
|
+
# even if there is data to be flushed.
|
|
88
|
+
# Used when the profiler failed or we're otherwise in a hurry to shut down.
|
|
89
|
+
def disable_reporting
|
|
90
|
+
@reporting_disabled = true
|
|
90
91
|
end
|
|
91
92
|
|
|
92
93
|
def work_pending?
|
|
93
|
-
!
|
|
94
|
+
!reporting_disabled && exporter.can_flush? && (run_loop? || !stop_requested?)
|
|
94
95
|
end
|
|
95
96
|
|
|
96
97
|
def reset_after_fork
|
|
@@ -50,6 +50,7 @@ module Datadog
|
|
|
50
50
|
FORM_FIELD_TAG_PROFILER_VERSION => profiler_version,
|
|
51
51
|
'profile_seq' => profile_seq.to_s,
|
|
52
52
|
)
|
|
53
|
+
|
|
53
54
|
user_tag_keys = settings.tags.keys
|
|
54
55
|
hash.keep_if { |tag| user_tag_keys.include?(tag) || ALLOWED_TAGS.include?(tag) }
|
|
55
56
|
Core::Utils.encode_tags(hash)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'sampler'
|
|
4
|
-
require_relative '
|
|
4
|
+
require_relative '../../core/knuth_sampler'
|
|
5
5
|
|
|
6
6
|
module Datadog
|
|
7
7
|
module Tracing
|
|
@@ -9,46 +9,35 @@ module Datadog
|
|
|
9
9
|
# {Datadog::Tracing::Sampling::RateSampler} is based on a sample rate.
|
|
10
10
|
class RateSampler < Sampler
|
|
11
11
|
KNUTH_FACTOR = 1111111111111111111
|
|
12
|
-
UINT64_MODULO = (1 << 64)
|
|
13
12
|
|
|
14
13
|
# Initialize a {Datadog::Tracing::Sampling::RateSampler}.
|
|
15
14
|
# This sampler keeps a random subset of the traces. Its main purpose is to
|
|
16
15
|
# reduce the instrumentation footprint.
|
|
17
16
|
#
|
|
18
17
|
# @param sample_rate [Numeric] the sample rate between 0.0 and 1.0, inclusive.
|
|
19
|
-
# 0.0 means that no trace will be sampled; 1.0 means that all traces will be
|
|
18
|
+
# 0.0 means that no trace will be sampled; 1.0 means that all traces will be sampled.
|
|
20
19
|
def initialize(sample_rate = 1.0, decision: nil)
|
|
21
20
|
super()
|
|
22
|
-
|
|
23
|
-
unless sample_rate >= 0.0 && sample_rate <= 1.0
|
|
24
|
-
Datadog.logger.warn('sample rate is not between 0 and 1, falling back to 1')
|
|
25
|
-
sample_rate = 1.0
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
self.sample_rate = sample_rate
|
|
29
|
-
|
|
21
|
+
@sampler = Core::KnuthSampler.new(sample_rate, knuth_factor: KNUTH_FACTOR)
|
|
30
22
|
@decision = decision
|
|
31
23
|
end
|
|
32
24
|
|
|
33
25
|
def sample_rate(*_)
|
|
34
|
-
@
|
|
26
|
+
@sampler.rate
|
|
35
27
|
end
|
|
36
28
|
|
|
37
29
|
def sample_rate=(sample_rate)
|
|
38
|
-
@
|
|
39
|
-
@sampling_id_threshold = sample_rate * Tracing::Utils::EXTERNAL_MAX_ID
|
|
30
|
+
@sampler = Core::KnuthSampler.new(sample_rate, knuth_factor: KNUTH_FACTOR)
|
|
40
31
|
end
|
|
41
32
|
|
|
42
33
|
def sample?(trace)
|
|
43
|
-
(
|
|
34
|
+
@sampler.sample?(trace.id)
|
|
44
35
|
end
|
|
45
36
|
|
|
46
37
|
def sample!(trace)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return false unless sampled
|
|
38
|
+
return false unless sample?(trace)
|
|
50
39
|
|
|
51
|
-
trace.sample_rate =
|
|
40
|
+
trace.sample_rate = sample_rate
|
|
52
41
|
trace.set_tag(Tracing::Metadata::Ext::Distributed::TAG_DECISION_MAKER, @decision) if @decision
|
|
53
42
|
|
|
54
43
|
true
|