datadog 2.35.0 → 2.36.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 +40 -1
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +68 -31
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +1 -1
- data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +1 -1
- data/ext/datadog_profiling_native_extension/collectors_stack.c +37 -18
- data/ext/datadog_profiling_native_extension/collectors_stack.h +8 -2
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +434 -300
- data/ext/datadog_profiling_native_extension/collectors_thread_context.h +9 -7
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +7 -8
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +0 -12
- data/ext/datadog_profiling_native_extension/extconf.rb +2 -2
- data/ext/datadog_profiling_native_extension/gvl_profiling_helper.c +4 -43
- data/ext/datadog_profiling_native_extension/gvl_profiling_helper.h +15 -47
- data/ext/datadog_profiling_native_extension/heap_recorder.c +44 -26
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +14 -35
- data/ext/datadog_profiling_native_extension/profiling.c +41 -4
- data/ext/datadog_profiling_native_extension/ruby_helpers.c +33 -34
- data/ext/datadog_profiling_native_extension/stack_recorder.c +24 -3
- data/ext/datadog_profiling_native_extension/stack_recorder.h +1 -0
- data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.h +4 -2
- data/ext/libdatadog_api/datadog_ruby_common.c +7 -8
- data/ext/libdatadog_api/datadog_ruby_common.h +0 -12
- data/ext/libdatadog_extconf_helpers.rb +1 -1
- data/lib/datadog/appsec/api_security/route_extractor.rb +6 -0
- data/lib/datadog/appsec/component.rb +1 -1
- data/lib/datadog/appsec/configuration.rb +7 -0
- data/lib/datadog/appsec/contrib/aws_lambda/waf_addresses.rb +37 -4
- data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +64 -19
- data/lib/datadog/appsec/contrib/graphql/integration.rb +1 -0
- data/lib/datadog/appsec/contrib/rack/buffered_input.rb +83 -0
- data/lib/datadog/appsec/contrib/rack/gateway/request.rb +41 -3
- data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +20 -7
- data/lib/datadog/appsec/contrib/rack/input_peeker.rb +92 -0
- data/lib/datadog/appsec/contrib/rails/gateway/request.rb +33 -0
- data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +17 -1
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +20 -3
- data/lib/datadog/appsec/default_header_tags.rb +10 -6
- data/lib/datadog/core/configuration/components.rb +1 -0
- data/lib/datadog/core/configuration/settings.rb +1 -2
- data/lib/datadog/core/configuration/supported_configurations.rb +2 -0
- data/lib/datadog/core/remote/component.rb +1 -1
- data/lib/datadog/core/telemetry/event/app_started.rb +0 -21
- data/lib/datadog/core/utils/at_fork_monkey_patch.rb +1 -1
- data/lib/datadog/core/utils/forking.rb +3 -1
- data/lib/datadog/core/utils/spawn_monkey_patch.rb +3 -1
- data/lib/datadog/core.rb +3 -0
- data/lib/datadog/di/base.rb +4 -1
- data/lib/datadog/di/component.rb +1 -1
- data/lib/datadog/error_tracking/collector.rb +2 -1
- data/lib/datadog/error_tracking/component.rb +2 -2
- data/lib/datadog/kit/tracing/method_tracer.rb +4 -1
- data/lib/datadog/opentelemetry/sdk/propagator.rb +9 -3
- data/lib/datadog/opentelemetry/sdk/span_processor.rb +4 -1
- data/lib/datadog/profiling/collectors/thread_context.rb +1 -0
- data/lib/datadog/profiling/component.rb +13 -15
- data/lib/datadog/profiling/ext/dir_monkey_patches.rb +3 -3
- data/lib/datadog/ruby_version.rb +25 -0
- data/lib/datadog/symbol_database/component.rb +306 -98
- data/lib/datadog/symbol_database/extractor.rb +223 -84
- data/lib/datadog/tracing/configuration/ext.rb +13 -0
- data/lib/datadog/tracing/configuration/settings.rb +17 -0
- data/lib/datadog/tracing/contrib/configuration/resolver.rb +7 -0
- data/lib/datadog/tracing/contrib/grpc/distributed/propagation.rb +2 -0
- data/lib/datadog/tracing/contrib/grpc.rb +1 -0
- data/lib/datadog/tracing/contrib/http/distributed/propagation.rb +2 -0
- data/lib/datadog/tracing/contrib/http.rb +1 -0
- data/lib/datadog/tracing/contrib/karafka/distributed/propagation.rb +2 -0
- data/lib/datadog/tracing/contrib/karafka.rb +1 -0
- data/lib/datadog/tracing/contrib/rack/middlewares.rb +3 -1
- data/lib/datadog/tracing/contrib/rack/route_inference.rb +3 -1
- data/lib/datadog/tracing/contrib/sidekiq/distributed/propagation.rb +2 -0
- data/lib/datadog/tracing/contrib/sidekiq.rb +1 -0
- data/lib/datadog/tracing/contrib/waterdrop/distributed/propagation.rb +2 -0
- data/lib/datadog/tracing/contrib/waterdrop.rb +1 -0
- data/lib/datadog/tracing/distributed/propagation.rb +33 -1
- data/lib/datadog/tracing/distributed/trace_context.rb +11 -2
- data/lib/datadog/tracing/trace_digest.rb +7 -0
- data/lib/datadog/tracing/trace_operation.rb +4 -1
- data/lib/datadog/tracing/tracer.rb +1 -0
- data/lib/datadog/version.rb +1 -1
- data/lib/datadog.rb +4 -1
- metadata +8 -5
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative '../input_peeker'
|
|
3
4
|
require_relative '../../../instrumentation/gateway/argument'
|
|
4
5
|
require_relative '../../../../core/header_collection'
|
|
5
6
|
require_relative '../../../../tracing/client_ip'
|
|
@@ -75,14 +76,51 @@ module Datadog
|
|
|
75
76
|
end
|
|
76
77
|
|
|
77
78
|
def form_hash
|
|
78
|
-
#
|
|
79
|
+
# NOTE: Rack populates `rack.request.form_hash` only as a side effect
|
|
80
|
+
# of {::Rack::Request#POST}, which reads and parses the body.
|
|
79
81
|
request.POST if request.form_data?
|
|
80
82
|
|
|
81
|
-
# usually Hash
|
|
82
|
-
# Hash
|
|
83
|
+
# usually Hash[String, String] but can be a more complex
|
|
84
|
+
# Hash[String, (String|Array|Hash)] when e.g coming from JSON
|
|
83
85
|
env['rack.request.form_hash']
|
|
84
86
|
end
|
|
85
87
|
|
|
88
|
+
# Returns the request body size in bytes using all available methods,
|
|
89
|
+
# or nil when the size cannot be measured within the limit
|
|
90
|
+
#
|
|
91
|
+
# NOTE: The priority of the measurement is the following:
|
|
92
|
+
# size if it's known, content-length if provided, and buffering
|
|
93
|
+
# to the limit if unknown-length
|
|
94
|
+
#
|
|
95
|
+
# WARNING: The buffering path adds overhead for streaming web-servers
|
|
96
|
+
# (Rack 3+) when the body length is unknown
|
|
97
|
+
def body_bytesize(limit)
|
|
98
|
+
io = request.body
|
|
99
|
+
|
|
100
|
+
return 0 unless io
|
|
101
|
+
return io.size if io.respond_to?(:size)
|
|
102
|
+
|
|
103
|
+
# NOTE: {::Rack::Request#content_length} is a plain `CONTENT_LENGTH`
|
|
104
|
+
# getter forces no body read
|
|
105
|
+
content_length = request.content_length
|
|
106
|
+
return content_length.to_i if content_length
|
|
107
|
+
|
|
108
|
+
# NOTE: A parsed-body cache without a raw byte count means the input
|
|
109
|
+
# stream was consumed before measurement, consider it unknown-length
|
|
110
|
+
return if env.key?('rack.request.form_hash')
|
|
111
|
+
|
|
112
|
+
InputPeeker.peek_bytesize(env, limit: limit)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Whether a request body can be collected without forcing a parse:
|
|
116
|
+
# either form data parseable on demand, or a body already parsed upstream
|
|
117
|
+
#
|
|
118
|
+
# NOTE: Rack does not parse JSON itself, a body parser middleware such as
|
|
119
|
+
# {Rack::JSONBodyParser} populates the form hash read by {#form_hash}
|
|
120
|
+
def collectable_body?
|
|
121
|
+
request.form_data? || request.parseable_data? || env.key?('rack.request.form_hash')
|
|
122
|
+
end
|
|
123
|
+
|
|
86
124
|
def client_ip
|
|
87
125
|
remote_ip = remote_addr
|
|
88
126
|
header_collection = Datadog::Core::HeaderCollection.from_hash(headers)
|
|
@@ -87,9 +87,26 @@ module Datadog
|
|
|
87
87
|
gateway.watch('rack.request.body') do |stack, gateway_request|
|
|
88
88
|
context = gateway_request.env[AppSec::Ext::CONTEXT_KEY]
|
|
89
89
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
90
|
+
request = gateway_request.request
|
|
91
|
+
next stack.call(request) unless gateway_request.collectable_body?
|
|
92
|
+
|
|
93
|
+
# NOTE: A limit of 0 disables request body collection entirely.
|
|
94
|
+
limit = Datadog.configuration.appsec.body_parsing_size_limit
|
|
95
|
+
next stack.call(request) if limit.zero?
|
|
96
|
+
|
|
97
|
+
persistent_data = {}
|
|
98
|
+
byte_length = gateway_request.body_bytesize(limit)
|
|
99
|
+
|
|
100
|
+
if byte_length
|
|
101
|
+
persistent_data['server.request.body.byte_length'] = byte_length
|
|
102
|
+
|
|
103
|
+
if byte_length <= limit
|
|
104
|
+
body = gateway_request.form_hash
|
|
105
|
+
persistent_data['server.request.body'] = body if body
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
next stack.call(request) if persistent_data.empty?
|
|
93
110
|
|
|
94
111
|
result = context.run_waf(persistent_data, {}, Datadog.configuration.appsec.waf_timeout)
|
|
95
112
|
|
|
@@ -119,10 +136,6 @@ module Datadog
|
|
|
119
136
|
next stack.call(gateway_request.request) if context.span.nil?
|
|
120
137
|
|
|
121
138
|
gateway_request.headers.each do |name, value|
|
|
122
|
-
if Ext::COLLECTABLE_REQUEST_HEADERS.include?(name)
|
|
123
|
-
context.span["http.request.headers.#{name}"] ||= value
|
|
124
|
-
end
|
|
125
|
-
|
|
126
139
|
if context.state[:has_identity_event] && Ext::IDENTITY_COLLECTABLE_REQUEST_HEADERS.include?(name)
|
|
127
140
|
context.span["http.request.headers.#{name}"] ||= value
|
|
128
141
|
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'stringio'
|
|
4
|
+
require_relative 'buffered_input'
|
|
5
|
+
|
|
6
|
+
module Datadog
|
|
7
|
+
module AppSec
|
|
8
|
+
module Contrib
|
|
9
|
+
module Rack
|
|
10
|
+
# Peeks at `rack.input` without changing what downstream Rack code can read
|
|
11
|
+
#
|
|
12
|
+
# @api private
|
|
13
|
+
module InputPeeker
|
|
14
|
+
module_function
|
|
15
|
+
|
|
16
|
+
# Peeks at `env['rack.input']` and returns the body bytesize when it
|
|
17
|
+
# can be measured within the given limit
|
|
18
|
+
#
|
|
19
|
+
# NOTE: Over-limit bodies cannot be fully measured, so returning `nil`
|
|
20
|
+
# is the tradeoff we accept
|
|
21
|
+
#
|
|
22
|
+
# WARNING: For forward-only input, replaces `env['rack.input']` with a
|
|
23
|
+
# replay stream over the peeked bytes, preserving the rest
|
|
24
|
+
# of the original stream
|
|
25
|
+
#
|
|
26
|
+
# @param env [Hash] Rack environment
|
|
27
|
+
# @param limit [Integer] Maximum body bytesize to measure
|
|
28
|
+
# @return [Integer, nil] `Integer` bytesize when measured within
|
|
29
|
+
# `limit`, including `0`; `nil` when input is missing, over the
|
|
30
|
+
# limit, or cannot be preserved for downstream reads
|
|
31
|
+
def peek_bytesize(env, limit:)
|
|
32
|
+
rack_input = env['rack.input']
|
|
33
|
+
return unless rack_input
|
|
34
|
+
|
|
35
|
+
rewindable = rewind? && rack_input.respond_to?(:rewind)
|
|
36
|
+
|
|
37
|
+
# NOTE: Rack 2 requires `rack.input` to be rewindable. Rewind before peeking
|
|
38
|
+
# in case an upstream framework already consumed part of the stream
|
|
39
|
+
return if rewindable && !rewind(rack_input)
|
|
40
|
+
|
|
41
|
+
buffer = peek(rack_input, limit)
|
|
42
|
+
over_limit = buffer.bytesize > limit
|
|
43
|
+
|
|
44
|
+
if rewindable
|
|
45
|
+
# NOTE: If we cannot rewind after peeking, downstream code would observe
|
|
46
|
+
# a partially consumed body. Treat it as not safely collectable
|
|
47
|
+
return unless rewind(rack_input)
|
|
48
|
+
else
|
|
49
|
+
env['rack.input'] = if over_limit
|
|
50
|
+
BufferedInput.new(rack_input, buffer: StringIO.new(buffer))
|
|
51
|
+
else
|
|
52
|
+
StringIO.new(buffer)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
over_limit ? nil : buffer.bytesize
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private_class_method def rewind?
|
|
60
|
+
return @rewind if defined?(@rewind)
|
|
61
|
+
|
|
62
|
+
@rewind = ::Gem::Version.new(::Rack.release) < ::Gem::Version.new('3')
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private_class_method def rewind(io)
|
|
66
|
+
io.rewind
|
|
67
|
+
true
|
|
68
|
+
rescue => e
|
|
69
|
+
Datadog.logger.debug { "AppSec: Failed to rewind `rack.input`: #{e.class}: #{e.message}" }
|
|
70
|
+
false
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private_class_method def peek(io, limit)
|
|
74
|
+
# NOTE: Read one byte past the limit to distinguish an exact-limit body
|
|
75
|
+
# from an over-limit body without reading the whole stream.
|
|
76
|
+
max = limit + 1
|
|
77
|
+
buffer = +''.b
|
|
78
|
+
|
|
79
|
+
while buffer.bytesize <= limit
|
|
80
|
+
chunk = io.read(max - buffer.bytesize)
|
|
81
|
+
break if chunk.nil? || chunk.empty?
|
|
82
|
+
|
|
83
|
+
buffer << chunk
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
buffer
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative '../../rack/input_peeker'
|
|
3
4
|
require_relative '../../../instrumentation/gateway/argument'
|
|
4
5
|
|
|
5
6
|
module Datadog
|
|
@@ -59,6 +60,38 @@ module Datadog
|
|
|
59
60
|
excluded.include?(k)
|
|
60
61
|
end
|
|
61
62
|
end
|
|
63
|
+
|
|
64
|
+
# Returns the request body size in bytes using all available methods,
|
|
65
|
+
# or nil when the size cannot be measured within the limit
|
|
66
|
+
#
|
|
67
|
+
# NOTE: The priority of the measurement is the following:
|
|
68
|
+
# raw posted data, raw form vars, size if known, raw
|
|
69
|
+
# Content-Length, then buffering to the limit if unknown-length
|
|
70
|
+
def body_bytesize(limit)
|
|
71
|
+
raw_body = env['RAW_POST_DATA']
|
|
72
|
+
return raw_body.bytesize if raw_body
|
|
73
|
+
|
|
74
|
+
form_vars = env['rack.request.form_vars']
|
|
75
|
+
return form_vars.bytesize if form_vars
|
|
76
|
+
|
|
77
|
+
io = request.body
|
|
78
|
+
return 0 unless io
|
|
79
|
+
return io.size if io.respond_to?(:size)
|
|
80
|
+
|
|
81
|
+
# NOTE: Read raw `CONTENT_LENGTH` as {ActionDispatch::Request#content_length}
|
|
82
|
+
# drains `rack.input` into `RAW_POST_DATA` on chunked Transfer-Encoding
|
|
83
|
+
content_length = env['CONTENT_LENGTH']
|
|
84
|
+
return content_length.to_i if content_length
|
|
85
|
+
|
|
86
|
+
# NOTE: An already-read body (e.g. late-parsed multipart on Rack 3+) peeks
|
|
87
|
+
# as 0, so we skip byte_length but still collect the parsed body
|
|
88
|
+
begin
|
|
89
|
+
Rack::InputPeeker.peek_bytesize(env, limit: limit)
|
|
90
|
+
rescue => e
|
|
91
|
+
Datadog.logger.debug { "AppSec: Failed to measure Rails request body: #{e.class}: #{e.message}" }
|
|
92
|
+
nil
|
|
93
|
+
end
|
|
94
|
+
end
|
|
62
95
|
end
|
|
63
96
|
end
|
|
64
97
|
end
|
|
@@ -23,12 +23,28 @@ module Datadog
|
|
|
23
23
|
def watch_request_action(gateway = Instrumentation.gateway)
|
|
24
24
|
gateway.watch('rails.request.action') do |stack, gateway_request|
|
|
25
25
|
context = gateway_request.env[AppSec::Ext::CONTEXT_KEY]
|
|
26
|
+
limit = Datadog.configuration.appsec.body_parsing_size_limit
|
|
26
27
|
|
|
27
28
|
persistent_data = {
|
|
28
|
-
'server.request.body' => gateway_request.parsed_body,
|
|
29
29
|
'server.request.path_params' => gateway_request.route_params
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
unless limit.zero?
|
|
33
|
+
byte_length = gateway_request.body_bytesize(limit)
|
|
34
|
+
|
|
35
|
+
if byte_length
|
|
36
|
+
# NOTE: Params may be parsed before this hook, leaving the
|
|
37
|
+
# body stream at EOF. Keep byte_length unset for that
|
|
38
|
+
# case, but still inspect cached params
|
|
39
|
+
persistent_data['server.request.body.byte_length'] = byte_length if byte_length.positive?
|
|
40
|
+
|
|
41
|
+
if byte_length <= limit
|
|
42
|
+
body = gateway_request.parsed_body
|
|
43
|
+
persistent_data['server.request.body'] = body unless body.nil? || body.empty?
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
32
48
|
result = context.run_waf(persistent_data, {}, Datadog.configuration.appsec.waf_timeout)
|
|
33
49
|
|
|
34
50
|
if result.match?
|
|
@@ -27,9 +27,26 @@ module Datadog
|
|
|
27
27
|
|
|
28
28
|
context.state[:web_framework] = 'sinatra'
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
request = gateway_request.request
|
|
31
|
+
next stack.call(request) unless gateway_request.collectable_body?
|
|
32
|
+
|
|
33
|
+
# NOTE: A limit of 0 disables request body collection entirely.
|
|
34
|
+
limit = Datadog.configuration.appsec.body_parsing_size_limit
|
|
35
|
+
next stack.call(request) if limit.zero?
|
|
36
|
+
|
|
37
|
+
persistent_data = {}
|
|
38
|
+
byte_length = gateway_request.body_bytesize(limit)
|
|
39
|
+
|
|
40
|
+
if byte_length
|
|
41
|
+
persistent_data['server.request.body.byte_length'] = byte_length
|
|
42
|
+
|
|
43
|
+
if byte_length <= limit
|
|
44
|
+
body = gateway_request.form_hash
|
|
45
|
+
persistent_data['server.request.body'] = body if body
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
next stack.call(request) if persistent_data.empty?
|
|
33
50
|
|
|
34
51
|
result = context.run_waf(persistent_data, {}, Datadog.configuration.appsec.waf_timeout)
|
|
35
52
|
|
|
@@ -10,20 +10,24 @@ module Datadog
|
|
|
10
10
|
#
|
|
11
11
|
# @api private
|
|
12
12
|
module DefaultHeaderTags
|
|
13
|
-
|
|
13
|
+
# NOTE: Contains additional WAF vendor headers
|
|
14
|
+
REQUEST_HEADERS_TAGS = %w[
|
|
15
|
+
accept
|
|
16
|
+
content-type
|
|
17
|
+
user-agent
|
|
18
|
+
akamai-user-risk
|
|
14
19
|
x-amzn-trace-id
|
|
15
|
-
cloudfront-viewer-ja3-fingerprint
|
|
16
|
-
cf-ray
|
|
17
20
|
x-cloud-trace-context
|
|
18
21
|
x-appgw-trace-id
|
|
19
22
|
x-sigsci-requestid
|
|
20
23
|
x-sigsci-tags
|
|
21
|
-
|
|
24
|
+
cf-ray
|
|
25
|
+
cloudfront-viewer-ja3-fingerprint
|
|
22
26
|
].freeze
|
|
23
27
|
|
|
24
28
|
RESPONSE_HEADERS_TAGS = %w[
|
|
25
|
-
content-length
|
|
26
29
|
content-type
|
|
30
|
+
content-length
|
|
27
31
|
content-encoding
|
|
28
32
|
content-language
|
|
29
33
|
].freeze
|
|
@@ -31,7 +35,7 @@ module Datadog
|
|
|
31
35
|
module_function
|
|
32
36
|
|
|
33
37
|
def tag_request(span, headers)
|
|
34
|
-
|
|
38
|
+
REQUEST_HEADERS_TAGS.each do |name|
|
|
35
39
|
value = headers.get(name)
|
|
36
40
|
span.set_tag("http.request.headers.#{name}", value) if value
|
|
37
41
|
end
|
|
@@ -618,8 +618,7 @@ module Datadog
|
|
|
618
618
|
o.type :bool
|
|
619
619
|
o.env 'DD_PROFILING_SIGHANDLER_SAMPLING_ENABLED'
|
|
620
620
|
o.default do
|
|
621
|
-
|
|
622
|
-
!(RUBY_VERSION.start_with?('3.3.') && Gem::Version.new(RUBY_VERSION) < Gem::Version.new('3.3.4'))
|
|
621
|
+
RubyVersion.is?('>= 3.2.5') && !RubyVersion.is?('>= 3.3', '< 3.3.4')
|
|
623
622
|
end
|
|
624
623
|
end
|
|
625
624
|
|
|
@@ -25,6 +25,7 @@ module Datadog
|
|
|
25
25
|
"DD_APM_TRACING_ENABLED",
|
|
26
26
|
"DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING",
|
|
27
27
|
"DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE",
|
|
28
|
+
"DD_APPSEC_BODY_PARSING_SIZE_LIMIT",
|
|
28
29
|
"DD_APPSEC_ENABLED",
|
|
29
30
|
"DD_APPSEC_HTTP_BLOCKED_TEMPLATE_HTML",
|
|
30
31
|
"DD_APPSEC_HTTP_BLOCKED_TEMPLATE_JSON",
|
|
@@ -270,6 +271,7 @@ module Datadog
|
|
|
270
271
|
"DD_TRACE_PRESTO_ENABLED",
|
|
271
272
|
"DD_TRACE_PRESTO_PEER_SERVICE",
|
|
272
273
|
"DD_TRACE_PRESTO_SERVICE_NAME",
|
|
274
|
+
"DD_TRACE_PROPAGATION_BEHAVIOR_EXTRACT",
|
|
273
275
|
"DD_TRACE_PROPAGATION_EXTRACT_FIRST",
|
|
274
276
|
"DD_TRACE_PROPAGATION_STYLE",
|
|
275
277
|
"DD_TRACE_PROPAGATION_STYLE_EXTRACT",
|
|
@@ -144,7 +144,7 @@ module Datadog
|
|
|
144
144
|
# timeout and an integer otherwise
|
|
145
145
|
# - before Ruby 3.2, ConditionVariable returns itself
|
|
146
146
|
# so we have to rely on @once having been set
|
|
147
|
-
if
|
|
147
|
+
if RubyVersion.is?('>= 3.2')
|
|
148
148
|
lifted = @condition.wait(@mutex, timeout)
|
|
149
149
|
else
|
|
150
150
|
@condition.wait(@mutex, timeout)
|
|
@@ -146,27 +146,6 @@ module Datadog
|
|
|
146
146
|
)
|
|
147
147
|
end
|
|
148
148
|
|
|
149
|
-
# OpenTelemetry configuration options (using environment variable names)
|
|
150
|
-
otel_exporter_headers_option = resolve_option(settings, 'opentelemetry.exporter.headers')
|
|
151
|
-
otel_exporter_headers_option.values_per_precedence.each do |precedence, value|
|
|
152
|
-
list << conf_value(
|
|
153
|
-
option_telemetry_name(otel_exporter_headers_option),
|
|
154
|
-
# Steep: Value is always a hash for opentelemetry.exporter.headers (ensured by o.type :hash)
|
|
155
|
-
value&.map { |key, header_value| "#{key}=#{header_value}" }&.join(','), # steep:ignore NoMethod
|
|
156
|
-
precedence
|
|
157
|
-
)
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
otel_metrics_headers_option = resolve_option(settings, 'opentelemetry.metrics.headers')
|
|
161
|
-
otel_metrics_headers_option.values_per_precedence.each do |precedence, value|
|
|
162
|
-
list << conf_value(
|
|
163
|
-
option_telemetry_name(otel_metrics_headers_option),
|
|
164
|
-
# Steep: Value is always a hash for opentelemetry.metrics.headers (ensured by o.type :hash)
|
|
165
|
-
value&.map { |key, header_value| "#{key}=#{header_value}" }&.join(','), # steep:ignore NoMethod
|
|
166
|
-
precedence
|
|
167
|
-
)
|
|
168
|
-
end
|
|
169
|
-
|
|
170
149
|
# Add some more custom additional payload values here
|
|
171
150
|
if settings.logger.instance
|
|
172
151
|
logger_instance_option = resolve_option(settings, 'logger.instance')
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative '../../ruby_version'
|
|
4
|
+
|
|
3
5
|
module Datadog
|
|
4
6
|
module Core
|
|
5
7
|
module Utils
|
|
@@ -45,7 +47,7 @@ module Datadog
|
|
|
45
47
|
# object will cause forking to not be detected in the fork when it should have.
|
|
46
48
|
#
|
|
47
49
|
# This wrapper prevents this by initializing the fork PID when the object is created.
|
|
48
|
-
if
|
|
50
|
+
if RubyVersion.is?('>= 3')
|
|
49
51
|
def initialize(*args, **kwargs, &block)
|
|
50
52
|
super
|
|
51
53
|
update_fork_pid!
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative '../../ruby_version'
|
|
4
|
+
|
|
3
5
|
module Datadog
|
|
4
6
|
module Core
|
|
5
7
|
module Utils
|
|
@@ -19,7 +21,7 @@ module Datadog
|
|
|
19
21
|
# Prepends `Process.spawn` to merge `env_provider` output into the child's environment hash.
|
|
20
22
|
module ProcessSpawnPatch
|
|
21
23
|
# The One and Only Correct Delegation Pattern
|
|
22
|
-
if
|
|
24
|
+
if RubyVersion.is?('>= 3')
|
|
23
25
|
def spawn(*args, **kwargs) # steep:ignore DifferentMethodParameterKind
|
|
24
26
|
super(*SpawnMonkeyPatch.inject_envs(args), **kwargs)
|
|
25
27
|
end
|
data/lib/datadog/core.rb
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
# Keep this at the top, this is needed at require-time by some files
|
|
4
|
+
require_relative 'ruby_version'
|
|
5
|
+
|
|
3
6
|
require_relative 'core/deprecations'
|
|
4
7
|
require_relative 'core/configuration/config_helper'
|
|
5
8
|
require_relative 'core/extensions'
|
data/lib/datadog/di/base.rb
CHANGED
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
|
|
11
11
|
require_relative 'code_tracker'
|
|
12
12
|
|
|
13
|
+
# Needed since this file can be loaded without core
|
|
14
|
+
require_relative '../ruby_version'
|
|
15
|
+
|
|
13
16
|
module Datadog
|
|
14
17
|
# Namespace for Datadog dynamic instrumentation.
|
|
15
18
|
#
|
|
@@ -44,7 +47,7 @@ module Datadog
|
|
|
44
47
|
# DI code.
|
|
45
48
|
def activate_tracking
|
|
46
49
|
# :script_compiled trace point was added in Ruby 2.6.
|
|
47
|
-
return unless
|
|
50
|
+
return unless RubyVersion.is?('>= 2.6')
|
|
48
51
|
|
|
49
52
|
begin
|
|
50
53
|
# Activate code tracking by default because line trace points will not work
|
data/lib/datadog/di/component.rb
CHANGED
|
@@ -45,7 +45,7 @@ module Datadog
|
|
|
45
45
|
logger.warn("di: cannot enable dynamic instrumentation: MRI is required, but running on #{RUBY_ENGINE}")
|
|
46
46
|
return false
|
|
47
47
|
end
|
|
48
|
-
if
|
|
48
|
+
if RubyVersion.is?('< 2.6')
|
|
49
49
|
logger.warn("di: cannot enable dynamic instrumentation: Ruby 2.6+ is required, but running on #{RUBY_VERSION}")
|
|
50
50
|
return false
|
|
51
51
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'ext'
|
|
4
|
+
require_relative '../ruby_version'
|
|
4
5
|
|
|
5
6
|
module Datadog
|
|
6
7
|
module ErrorTracking
|
|
@@ -52,7 +53,7 @@ module Datadog
|
|
|
52
53
|
end
|
|
53
54
|
end
|
|
54
55
|
|
|
55
|
-
if
|
|
56
|
+
if RubyVersion.is?(">= #{Ext::RUBY_VERSION_WITH_RESCUE_EVENT}")
|
|
56
57
|
# Starting from ruby3.3, as we are listening to :rescue event,
|
|
57
58
|
# we just want to remove the span event if the error was
|
|
58
59
|
# previously handled
|
|
@@ -33,7 +33,7 @@ module Datadog
|
|
|
33
33
|
if RUBY_ENGINE != 'ruby'
|
|
34
34
|
logger.warn("error tracking: cannot enable error tracking: MRI is required, but running on #{RUBY_ENGINE}")
|
|
35
35
|
false
|
|
36
|
-
elsif
|
|
36
|
+
elsif RubyVersion.is?('< 2.7')
|
|
37
37
|
logger.warn(
|
|
38
38
|
"error tracking: cannot enable error tracking: Ruby 2.7+ is required, but running
|
|
39
39
|
on #{RUBY_VERSION}"
|
|
@@ -63,7 +63,7 @@ module Datadog
|
|
|
63
63
|
# Before Ruby3.3 the TracePoint listen for :raise events.
|
|
64
64
|
# If an error is not handled, we will delete the according
|
|
65
65
|
# span event in the collector.
|
|
66
|
-
event = (
|
|
66
|
+
event = RubyVersion.is?('>= 3.3') ? :rescue : :raise
|
|
67
67
|
|
|
68
68
|
# This TracePoint is in charge of capturing the handled exceptions
|
|
69
69
|
# and of adding the corresponding span events to the collector
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
# Needed since this file can be loaded without core
|
|
4
|
+
require_relative '../../ruby_version'
|
|
5
|
+
|
|
3
6
|
module Datadog
|
|
4
7
|
module Kit
|
|
5
8
|
module Tracing
|
|
@@ -72,7 +75,7 @@ module Datadog
|
|
|
72
75
|
raise ArgumentError, 'span name is not a String'
|
|
73
76
|
end
|
|
74
77
|
|
|
75
|
-
args = (
|
|
78
|
+
args = RubyVersion.is?('>= 2.7') ? '...' : '*args, &block'
|
|
76
79
|
|
|
77
80
|
hook_module = Module.new do
|
|
78
81
|
define_singleton_method(:inspect) do
|
|
@@ -43,6 +43,15 @@ module Datadog
|
|
|
43
43
|
digest = @datadog_propagator.extract(carrier)
|
|
44
44
|
return context unless digest
|
|
45
45
|
|
|
46
|
+
# Always call continue_trace! so span links are created in restart mode
|
|
47
|
+
# (when digest.trace_id is nil) before we attempt to build the OTel SpanContext.
|
|
48
|
+
trace = Tracing.continue_trace!(digest)
|
|
49
|
+
|
|
50
|
+
# In restart mode DD_TRACE_PROPAGATION_BEHAVIOR_EXTRACT=restart, the extracted
|
|
51
|
+
# digest carries no trace_id (fresh start). continue_trace! has already recorded
|
|
52
|
+
# the span link; return the current context so the caller's span starts a new root.
|
|
53
|
+
return context unless digest.trace_id
|
|
54
|
+
|
|
46
55
|
# Converts the {Numeric} Datadog id object to OpenTelemetry's byte array format.
|
|
47
56
|
# 128-bit unsigned, big-endian integer
|
|
48
57
|
trace_id = [digest.trace_id >> 64, digest.trace_id & 0xFFFFFFFFFFFFFFFF].pack('Q>Q>')
|
|
@@ -70,9 +79,6 @@ module Datadog
|
|
|
70
79
|
)
|
|
71
80
|
|
|
72
81
|
span = ::OpenTelemetry::Trace.non_recording_span(span_context)
|
|
73
|
-
|
|
74
|
-
trace = Tracing.continue_trace!(digest)
|
|
75
|
-
|
|
76
82
|
span.datadog_trace = trace
|
|
77
83
|
|
|
78
84
|
::OpenTelemetry::Trace.context_with_span(span, parent_context: context)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'trace/span'
|
|
4
|
+
require_relative '../../core/utils'
|
|
4
5
|
require_relative '../../tracing/span_link'
|
|
5
6
|
require_relative '../../tracing/span_event'
|
|
6
7
|
require_relative '../../tracing/trace_digest'
|
|
@@ -101,12 +102,14 @@ module Datadog
|
|
|
101
102
|
|
|
102
103
|
unless span.links.nil?
|
|
103
104
|
datadog_span.links = span.links.map do |link|
|
|
105
|
+
tracestate = link.span_context.tracestate&.to_s
|
|
106
|
+
|
|
104
107
|
Datadog::Tracing::SpanLink.new(
|
|
105
108
|
Datadog::Tracing::TraceDigest.new(
|
|
106
109
|
trace_id: link.span_context.hex_trace_id.to_i(16),
|
|
107
110
|
span_id: link.span_context.hex_span_id.to_i(16),
|
|
108
111
|
trace_sampling_priority: (link.span_context.trace_flags&.sampled? ? 1 : 0),
|
|
109
|
-
trace_state:
|
|
112
|
+
trace_state: tracestate && ::Datadog::Core::Utils.utf8_encode(tracestate, placeholder: nil),
|
|
110
113
|
span_remote: link.span_context.remote?,
|
|
111
114
|
),
|
|
112
115
|
attributes: link.attributes
|