datadog 2.20.0 → 2.22.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 +73 -1
- data/README.md +0 -1
- data/ext/LIBDATADOG_DEVELOPMENT.md +60 -0
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +1 -1
- data/ext/libdatadog_api/ddsketch.c +106 -0
- data/ext/libdatadog_api/init.c +3 -0
- data/ext/libdatadog_api/library_config.c +35 -27
- data/ext/libdatadog_api/process_discovery.c +24 -18
- data/ext/libdatadog_extconf_helpers.rb +1 -1
- data/lib/datadog/appsec/api_security/endpoint_collection/grape_route_serializer.rb +26 -0
- data/lib/datadog/appsec/api_security/endpoint_collection/rails_collector.rb +59 -0
- data/lib/datadog/appsec/api_security/endpoint_collection/rails_route_serializer.rb +29 -0
- data/lib/datadog/appsec/api_security/endpoint_collection/sinatra_route_serializer.rb +26 -0
- data/lib/datadog/appsec/api_security/endpoint_collection.rb +10 -0
- data/lib/datadog/appsec/api_security/route_extractor.rb +6 -2
- data/lib/datadog/appsec/assets/waf_rules/README.md +30 -36
- data/lib/datadog/appsec/assets/waf_rules/recommended.json +359 -4
- data/lib/datadog/appsec/assets/waf_rules/strict.json +43 -2
- data/lib/datadog/appsec/autoload.rb +1 -1
- data/lib/datadog/appsec/compressed_json.rb +1 -1
- data/lib/datadog/appsec/configuration/settings.rb +9 -0
- data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +3 -1
- data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +3 -2
- data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +3 -1
- data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +3 -1
- data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +9 -4
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +5 -1
- data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +7 -2
- data/lib/datadog/appsec/contrib/rails/patcher.rb +30 -0
- data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +3 -1
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +10 -4
- data/lib/datadog/appsec/event.rb +12 -14
- data/lib/datadog/appsec/metrics/collector.rb +19 -3
- data/lib/datadog/appsec/metrics/telemetry_exporter.rb +2 -1
- data/lib/datadog/appsec/monitor/gateway/watcher.rb +4 -4
- data/lib/datadog/appsec/remote.rb +25 -13
- data/lib/datadog/appsec/security_engine/result.rb +28 -9
- data/lib/datadog/appsec/security_engine/runner.rb +17 -7
- data/lib/datadog/appsec/security_event.rb +5 -7
- data/lib/datadog/core/configuration/agent_settings_resolver.rb +4 -4
- data/lib/datadog/core/configuration/components.rb +22 -8
- data/lib/datadog/core/configuration/config_helper.rb +100 -0
- data/lib/datadog/core/configuration/deprecations.rb +36 -0
- data/lib/datadog/core/configuration/ext.rb +0 -1
- data/lib/datadog/core/configuration/option.rb +38 -43
- data/lib/datadog/core/configuration/option_definition.rb +0 -9
- data/lib/datadog/core/configuration/options.rb +1 -5
- data/lib/datadog/core/configuration/settings.rb +10 -6
- data/lib/datadog/core/configuration/stable_config.rb +10 -0
- data/lib/datadog/core/configuration/supported_configurations.rb +337 -0
- data/lib/datadog/core/configuration.rb +2 -2
- data/lib/datadog/core/ddsketch.rb +21 -0
- data/lib/datadog/core/deprecations.rb +2 -2
- data/lib/datadog/core/environment/ext.rb +0 -2
- data/lib/datadog/core/environment/git.rb +2 -2
- data/lib/datadog/core/environment/variable_helpers.rb +3 -3
- data/lib/datadog/core/environment/yjit.rb +2 -1
- data/lib/datadog/core/metrics/client.rb +2 -2
- data/lib/datadog/core/pin.rb +4 -8
- data/lib/datadog/core/process_discovery/tracer_memfd.rb +2 -4
- data/lib/datadog/core/process_discovery.rb +48 -23
- data/lib/datadog/core/remote/component.rb +4 -6
- data/lib/datadog/core/runtime/ext.rb +0 -1
- data/lib/datadog/core/telemetry/component.rb +11 -0
- data/lib/datadog/core/telemetry/emitter.rb +6 -6
- data/lib/datadog/core/telemetry/event/app_endpoints_loaded.rb +30 -0
- data/lib/datadog/core/telemetry/event/app_started.rb +2 -2
- data/lib/datadog/core/telemetry/event.rb +1 -0
- data/lib/datadog/core/transport/response.rb +4 -1
- data/lib/datadog/core/utils/network.rb +19 -0
- data/lib/datadog/core.rb +2 -0
- data/lib/datadog/di/boot.rb +5 -3
- data/lib/datadog/di/component.rb +14 -0
- data/lib/datadog/di/context.rb +70 -0
- data/lib/datadog/di/el/compiler.rb +164 -0
- data/lib/datadog/di/el/evaluator.rb +159 -0
- data/lib/datadog/di/el/expression.rb +42 -0
- data/lib/datadog/di/el.rb +5 -0
- data/lib/datadog/di/error.rb +25 -0
- data/lib/datadog/di/instrumenter.rb +101 -32
- data/lib/datadog/di/probe.rb +35 -15
- data/lib/datadog/di/probe_builder.rb +39 -1
- data/lib/datadog/di/probe_file_loader.rb +1 -1
- data/lib/datadog/di/probe_manager.rb +3 -2
- data/lib/datadog/di/probe_notification_builder.rb +50 -51
- data/lib/datadog/di/serializer.rb +151 -7
- data/lib/datadog/opentelemetry/sdk/configurator.rb +1 -1
- data/lib/datadog/profiling/collectors/info.rb +1 -1
- data/lib/datadog/profiling/ext.rb +2 -1
- data/lib/datadog/profiling/http_transport.rb +1 -1
- data/lib/datadog/profiling/tasks/exec.rb +2 -2
- data/lib/datadog/tracing/component.rb +6 -17
- data/lib/datadog/tracing/configuration/dynamic.rb +2 -2
- data/lib/datadog/tracing/configuration/ext.rb +0 -3
- data/lib/datadog/tracing/configuration/settings.rb +15 -10
- data/lib/datadog/tracing/contrib/component.rb +2 -2
- data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +7 -0
- data/lib/datadog/tracing/contrib/graphql/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +63 -27
- data/lib/datadog/tracing/contrib/rack/request_queue.rb +1 -0
- data/lib/datadog/tracing/contrib/rack/trace_proxy_middleware.rb +7 -1
- data/lib/datadog/tracing/contrib/rails/ext.rb +2 -1
- data/lib/datadog/tracing/contrib/rails/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/span_attribute_schema.rb +1 -1
- data/lib/datadog/tracing/metadata/ext.rb +8 -0
- data/lib/datadog/version.rb +1 -1
- metadata +25 -9
- data/ext/libdatadog_api/macos_development.md +0 -26
@@ -64,6 +64,9 @@ module Datadog
|
|
64
64
|
# a common base class but are all of different classes) or for Mongoid
|
65
65
|
# models (that do not have a common base class at all but include a
|
66
66
|
# standard Mongoid module).
|
67
|
+
#
|
68
|
+
# Important: these serializers are NOT used in log messages.
|
69
|
+
# They are only used for variables that are captured in the snapshots.
|
67
70
|
@@flat_registry = []
|
68
71
|
def self.register(condition: nil, &block)
|
69
72
|
@@flat_registry << {condition: condition, proc: block}
|
@@ -79,6 +82,18 @@ module Datadog
|
|
79
82
|
attr_reader :redactor
|
80
83
|
attr_reader :telemetry
|
81
84
|
|
85
|
+
def combine_args(args, kwargs, target_self)
|
86
|
+
counter = 0
|
87
|
+
combined = args.each_with_object({}) do |value, c|
|
88
|
+
counter += 1
|
89
|
+
# Conversion to symbol is needed here to put args ahead of
|
90
|
+
# kwargs when they are merged below.
|
91
|
+
c[:"arg#{counter}"] = value
|
92
|
+
end.update(kwargs)
|
93
|
+
combined[:self] = target_self
|
94
|
+
combined
|
95
|
+
end
|
96
|
+
|
82
97
|
# Serializes positional and keyword arguments to a method,
|
83
98
|
# as obtained by a method probe.
|
84
99
|
#
|
@@ -93,13 +108,7 @@ module Datadog
|
|
93
108
|
def serialize_args(args, kwargs, target_self,
|
94
109
|
depth: settings.dynamic_instrumentation.max_capture_depth,
|
95
110
|
attribute_count: settings.dynamic_instrumentation.max_capture_attribute_count)
|
96
|
-
|
97
|
-
combined = args.each_with_object({}) do |value, c|
|
98
|
-
counter += 1
|
99
|
-
# Conversion to symbol is needed here to put args ahead of
|
100
|
-
# kwargs when they are merged below.
|
101
|
-
c[:"arg#{counter}"] = value
|
102
|
-
end.update(kwargs).update(self: target_self)
|
111
|
+
combined = combine_args(args, kwargs, target_self)
|
103
112
|
serialize_vars(combined, depth: depth, attribute_count: attribute_count)
|
104
113
|
end
|
105
114
|
|
@@ -150,6 +159,8 @@ module Datadog
|
|
150
159
|
end
|
151
160
|
|
152
161
|
serialized = {type: class_name(cls)}
|
162
|
+
# https://github.com/soutaro/steep/issues/1860
|
163
|
+
# @type var serialized: untyped
|
153
164
|
case value
|
154
165
|
when NilClass
|
155
166
|
serialized.update(isNull: true)
|
@@ -261,8 +272,120 @@ module Datadog
|
|
261
272
|
end
|
262
273
|
end
|
263
274
|
|
275
|
+
# This method is used for serializing arbitrary values into log messages.
|
276
|
+
# Because the output is meant to be human-readable, we cannot use
|
277
|
+
# the "normal" serialization format which is meant to be machine-readable.
|
278
|
+
# Serialize objects with depth of 1 and include the class name.
|
279
|
+
#
|
280
|
+
# Note that this method does not (currently) utilize the custom
|
281
|
+
# serializers that the "normal" serialization logic uses.
|
282
|
+
#
|
283
|
+
# This serializer differs from the RFC in two ways:
|
284
|
+
# 1. We omit the middle of long strings rather than the end,
|
285
|
+
# and also the inner entries in arrays/hashes/objects.
|
286
|
+
# 2. We use Ruby-ish syntax for hashes and objects.
|
287
|
+
#
|
288
|
+
# We also use the Ruby-like syntax for symbols, which don't exist
|
289
|
+
# in other languages.
|
290
|
+
def serialize_value_for_message(value, depth = 1)
|
291
|
+
# This method is more verbose than "normal" Ruby code to avoid
|
292
|
+
# array allocations.
|
293
|
+
case value
|
294
|
+
when NilClass
|
295
|
+
'nil'
|
296
|
+
when Integer, Float, TrueClass, FalseClass, Time, Date
|
297
|
+
value.to_s
|
298
|
+
when String
|
299
|
+
serialize_string_or_symbol_for_message(value)
|
300
|
+
when Symbol
|
301
|
+
':' + serialize_string_or_symbol_for_message(value)
|
302
|
+
when Array
|
303
|
+
return '...' if depth <= 0
|
304
|
+
|
305
|
+
max = max_capture_collection_size_for_message
|
306
|
+
if value.length > max
|
307
|
+
value_ = value[0...max - 1] || []
|
308
|
+
value_ << '...'
|
309
|
+
value_ << value[-1]
|
310
|
+
value = value_
|
311
|
+
end
|
312
|
+
'[' + value.map do |item|
|
313
|
+
serialize_value_for_message(item, depth - 1)
|
314
|
+
end.join(', ') + ']'
|
315
|
+
when Hash
|
316
|
+
return '...' if depth <= 0
|
317
|
+
|
318
|
+
max = max_capture_collection_size_for_message
|
319
|
+
keys = value.keys
|
320
|
+
truncated = false
|
321
|
+
if value.length > max
|
322
|
+
keys_ = keys[0...max - 1] || []
|
323
|
+
keys_ << keys[-1]
|
324
|
+
keys = keys_
|
325
|
+
truncated = true
|
326
|
+
end
|
327
|
+
serialized = keys.map do |key|
|
328
|
+
"#{serialize_value_for_message(key, depth - 1)} => #{serialize_value_for_message(value[key], depth - 1)}"
|
329
|
+
end
|
330
|
+
if truncated
|
331
|
+
serialized[serialized.length] = serialized[serialized.length - 1]
|
332
|
+
serialized[serialized.length - 2] = '...'
|
333
|
+
end
|
334
|
+
"{#{serialized.join(", ")}}"
|
335
|
+
else
|
336
|
+
return '...' if depth <= 0
|
337
|
+
|
338
|
+
vars = value.instance_variables
|
339
|
+
truncated = false
|
340
|
+
max = max_capture_attribute_count_for_message
|
341
|
+
if vars.length > max
|
342
|
+
vars_ = vars[0...max - 1] || []
|
343
|
+
vars_ << vars[-1]
|
344
|
+
truncated = true
|
345
|
+
vars = vars_
|
346
|
+
end
|
347
|
+
serialized = vars.map do |var|
|
348
|
+
# +var+ here is always the instance variable name which is a
|
349
|
+
# symbol, we do not need to run it through our serializer.
|
350
|
+
"#{var}=#{serialize_value_for_message(value.send(:instance_variable_get, var), depth - 1)}"
|
351
|
+
end
|
352
|
+
if truncated
|
353
|
+
serialized << serialized.last
|
354
|
+
serialized[-2] = '...'
|
355
|
+
end
|
356
|
+
serialized = if serialized.any?
|
357
|
+
' ' + serialized.join(' ')
|
358
|
+
end
|
359
|
+
"#<#{class_name(value.class)}#{serialized}>"
|
360
|
+
end
|
361
|
+
rescue => exc
|
362
|
+
telemetry&.report(exc, description: "Error serializing for message")
|
363
|
+
# TODO class_name(foo) can also fail, which we don't handle here.
|
364
|
+
# Telemetry reporting could potentially also fail?
|
365
|
+
"#<#{class_name(value.class)}: serialization error>"
|
366
|
+
end
|
367
|
+
|
264
368
|
private
|
265
369
|
|
370
|
+
MAX_MESSAGE_COLLECTION_SIZE = 3
|
371
|
+
MAX_MESSAGE_ATTRIBUTE_COUNT = 5
|
372
|
+
|
373
|
+
def max_capture_collection_size_for_message
|
374
|
+
max = settings.dynamic_instrumentation.max_capture_collection_size
|
375
|
+
if max > MAX_MESSAGE_COLLECTION_SIZE
|
376
|
+
max = MAX_MESSAGE_COLLECTION_SIZE
|
377
|
+
end
|
378
|
+
max
|
379
|
+
end
|
380
|
+
|
381
|
+
def max_capture_attribute_count_for_message
|
382
|
+
max = settings.dynamic_instrumentation.max_capture_attribute_count
|
383
|
+
if max > MAX_MESSAGE_ATTRIBUTE_COUNT
|
384
|
+
max = MAX_MESSAGE_ATTRIBUTE_COUNT
|
385
|
+
end
|
386
|
+
max
|
387
|
+
end
|
388
|
+
|
266
389
|
# Returns the name for the specified class object.
|
267
390
|
#
|
268
391
|
# Ruby can have nameless classes, e.g. Class.new is a class object
|
@@ -273,6 +396,27 @@ module Datadog
|
|
273
396
|
# and we don't want to invoke user code.
|
274
397
|
cls.name || "[Unnamed class]"
|
275
398
|
end
|
399
|
+
|
400
|
+
def serialize_string_or_symbol_for_message(value)
|
401
|
+
max = settings.dynamic_instrumentation.max_capture_string_length
|
402
|
+
if max > 100
|
403
|
+
max = 100
|
404
|
+
end
|
405
|
+
value = value.to_s
|
406
|
+
if (length = value.length) > max
|
407
|
+
if max < 5
|
408
|
+
value[0...max]
|
409
|
+
else
|
410
|
+
upper = length - max / 2 + 1
|
411
|
+
if max % 2 == 0
|
412
|
+
upper += 1
|
413
|
+
end
|
414
|
+
value[0...max / 2 - 1] + '...' + value[upper...length]
|
415
|
+
end
|
416
|
+
else
|
417
|
+
value
|
418
|
+
end
|
419
|
+
end
|
276
420
|
end
|
277
421
|
end
|
278
422
|
end
|
@@ -19,7 +19,7 @@ module Datadog
|
|
19
19
|
# Ensure Datadog-configure propagation styles have are applied when configured.
|
20
20
|
#
|
21
21
|
# DEV: Support configuring propagation through the environment variable
|
22
|
-
# DEV: `OTEL_PROPAGATORS`,
|
22
|
+
# DEV: `OTEL_PROPAGATORS`, alias to `DD_TRACE_PROPAGATION_STYLE`
|
23
23
|
def configure_propagation
|
24
24
|
@propagators = [Propagator.new(Tracing::Contrib::HTTP)]
|
25
25
|
super
|
@@ -146,7 +146,7 @@ module Datadog
|
|
146
146
|
return @gc_tuning_info if defined?(@gc_tuning_info)
|
147
147
|
|
148
148
|
@gc_tuning_info = RUBY_GC_TUNING_ENV_VARS.each_with_object({}) do |var, hash|
|
149
|
-
current_value =
|
149
|
+
current_value = DATADOG_ENV[var]
|
150
150
|
hash[var.to_sym] = current_value if current_value
|
151
151
|
end.freeze
|
152
152
|
end
|
@@ -6,9 +6,10 @@ module Datadog
|
|
6
6
|
ENV_ENABLED = "DD_PROFILING_ENABLED"
|
7
7
|
ENV_UPLOAD_TIMEOUT = "DD_PROFILING_UPLOAD_TIMEOUT"
|
8
8
|
ENV_MAX_FRAMES = "DD_PROFILING_MAX_FRAMES"
|
9
|
-
ENV_AGENTLESS = "DD_PROFILING_AGENTLESS"
|
10
9
|
ENV_ENDPOINT_COLLECTION_ENABLED = "DD_PROFILING_ENDPOINT_COLLECTION_ENABLED"
|
11
10
|
|
11
|
+
# WARNING: This should not be used, only for internal testing
|
12
|
+
ENV_AGENTLESS = "DD_PROFILING_AGENTLESS" # rubocop:disable CustomCops/EnvStringValidationCop
|
12
13
|
module Transport
|
13
14
|
module HTTP
|
14
15
|
FORM_FIELD_TAG_PROFILER_VERSION = "profiler_version"
|
@@ -56,7 +56,7 @@ module Datadog
|
|
56
56
|
private
|
57
57
|
|
58
58
|
def agentless?(site, api_key)
|
59
|
-
site && api_key &&
|
59
|
+
site && api_key && %w[1 true].include?(ENV[Profiling::Ext::ENV_AGENTLESS] || '') # rubocop:disable CustomCops/EnvUsageCop
|
60
60
|
end
|
61
61
|
|
62
62
|
def config_without_api_key
|
@@ -25,9 +25,9 @@ module Datadog
|
|
25
25
|
private
|
26
26
|
|
27
27
|
def set_rubyopt!
|
28
|
-
existing_rubyopt = ENV["RUBYOPT"]
|
28
|
+
existing_rubyopt = ENV["RUBYOPT"] # rubocop:disable CustomCops/EnvUsageCop
|
29
29
|
|
30
|
-
ENV["RUBYOPT"] = existing_rubyopt ? "#{existing_rubyopt} #{rubyopts.join(" ")}" : rubyopts.join(" ")
|
30
|
+
ENV["RUBYOPT"] = existing_rubyopt ? "#{existing_rubyopt} #{rubyopts.join(" ")}" : rubyopts.join(" ") # rubocop:disable CustomCops/EnvUsageCop
|
31
31
|
end
|
32
32
|
|
33
33
|
# If there's an error here, rather than throwing a cryptic stack trace, let's instead have clearer messages, and
|
@@ -12,16 +12,7 @@ module Datadog
|
|
12
12
|
module Tracing
|
13
13
|
# Tracing component
|
14
14
|
module Component
|
15
|
-
|
16
|
-
module InstanceMethods
|
17
|
-
# Hot-swaps with a new sampler.
|
18
|
-
# This operation acquires the Components lock to ensure
|
19
|
-
# there is no concurrent modification of the sampler.
|
20
|
-
def reconfigure_live_sampler
|
21
|
-
sampler = self.class.build_sampler(Datadog.configuration)
|
22
|
-
Datadog.send(:safely_synchronize) { tracer.sampler.sampler = sampler }
|
23
|
-
end
|
24
|
-
end
|
15
|
+
module_function
|
25
16
|
|
26
17
|
def build_tracer(settings, agent_settings, logger:)
|
27
18
|
# If a custom tracer has been provided, use it instead.
|
@@ -156,11 +147,6 @@ module Datadog
|
|
156
147
|
Tracing::Sampling::Span::Sampler.new(rules || [])
|
157
148
|
end
|
158
149
|
|
159
|
-
# Configure non-privileged components.
|
160
|
-
def configure_tracing(settings)
|
161
|
-
Datadog::Tracing::Contrib::Component.configure(settings)
|
162
|
-
end
|
163
|
-
|
164
150
|
# Sampler wrapper component, to allow for hot-swapping
|
165
151
|
# the sampler instance used by the tracer.
|
166
152
|
# Swapping samplers happens during Dynamic Configuration.
|
@@ -182,8 +168,7 @@ module Datadog
|
|
182
168
|
end
|
183
169
|
end
|
184
170
|
|
185
|
-
private
|
186
|
-
|
171
|
+
# @api private
|
187
172
|
def build_tracer_tags(settings)
|
188
173
|
settings.tags.dup.tap do |tags|
|
189
174
|
tags[Core::Environment::Ext::TAG_ENV] = settings.env unless settings.env.nil?
|
@@ -193,6 +178,7 @@ module Datadog
|
|
193
178
|
|
194
179
|
# Build a post-sampler that limits the rate of traces to one per `seconds`.
|
195
180
|
# E.g.: `build_rate_limit_post_sampler(seconds: 60)` will limit the rate to one trace per minute.
|
181
|
+
# @api private
|
196
182
|
def build_rate_limit_post_sampler(seconds:)
|
197
183
|
Tracing::Sampling::RuleSampler.new(
|
198
184
|
rate_limiter: Datadog::Core::TokenBucket.new(1.0 / seconds, 1.0),
|
@@ -200,11 +186,13 @@ module Datadog
|
|
200
186
|
)
|
201
187
|
end
|
202
188
|
|
189
|
+
# @api private
|
203
190
|
def build_test_mode_trace_flush(settings)
|
204
191
|
# If context flush behavior is provided, use it instead.
|
205
192
|
settings.tracing.test_mode.trace_flush || build_trace_flush(settings)
|
206
193
|
end
|
207
194
|
|
195
|
+
# @api private
|
208
196
|
def build_test_mode_sampler
|
209
197
|
# Do not sample any spans for tests; all must be preserved.
|
210
198
|
# Set priority sampler to ensure the agent doesn't drop any traces.
|
@@ -214,6 +202,7 @@ module Datadog
|
|
214
202
|
)
|
215
203
|
end
|
216
204
|
|
205
|
+
# @api private
|
217
206
|
def build_test_mode_writer(settings, agent_settings)
|
218
207
|
writer_options = settings.tracing.test_mode.writer_options || {}
|
219
208
|
|
@@ -41,7 +41,7 @@ module Datadog
|
|
41
41
|
# Ensures sampler is rebuilt and new configuration is applied
|
42
42
|
def call(tracing_sampling_rate)
|
43
43
|
super
|
44
|
-
Datadog.send(:components).
|
44
|
+
Datadog.send(:components).reconfigure_sampler
|
45
45
|
end
|
46
46
|
|
47
47
|
protected
|
@@ -79,7 +79,7 @@ module Datadog
|
|
79
79
|
end
|
80
80
|
|
81
81
|
super
|
82
|
-
Datadog.send(:components).
|
82
|
+
Datadog.send(:components).reconfigure_sampler
|
83
83
|
end
|
84
84
|
|
85
85
|
protected
|
@@ -9,7 +9,6 @@ module Datadog
|
|
9
9
|
# e.g. Env vars, default values, enums, etc...
|
10
10
|
module Ext
|
11
11
|
ENV_ENABLED = 'DD_TRACE_ENABLED'
|
12
|
-
ENV_OTEL_TRACES_EXPORTER = 'OTEL_TRACES_EXPORTER'
|
13
12
|
ENV_HEADER_TAGS = 'DD_TRACE_HEADER_TAGS'
|
14
13
|
ENV_BAGGAGE_TAG_KEYS = 'DD_TRACE_BAGGAGE_TAG_KEYS'
|
15
14
|
ENV_TRACE_ID_128_BIT_GENERATION_ENABLED = 'DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED'
|
@@ -55,7 +54,6 @@ module Datadog
|
|
55
54
|
# Has lower precedence than `DD_TRACE_PROPAGATION_STYLE_INJECT` or
|
56
55
|
# `DD_TRACE_PROPAGATION_STYLE_EXTRACT`.
|
57
56
|
ENV_PROPAGATION_STYLE = 'DD_TRACE_PROPAGATION_STYLE'
|
58
|
-
ENV_OTEL_PROPAGATION_STYLE = 'OTEL_PROPAGATORS'
|
59
57
|
|
60
58
|
ENV_PROPAGATION_STYLE_INJECT = 'DD_TRACE_PROPAGATION_STYLE_INJECT'
|
61
59
|
|
@@ -81,7 +79,6 @@ module Datadog
|
|
81
79
|
ENV_SAMPLE_RATE = 'DD_TRACE_SAMPLE_RATE'
|
82
80
|
ENV_RATE_LIMIT = 'DD_TRACE_RATE_LIMIT'
|
83
81
|
ENV_RULES = 'DD_TRACE_SAMPLING_RULES'
|
84
|
-
ENV_OTEL_TRACES_SAMPLER = 'OTEL_TRACES_SAMPLER'
|
85
82
|
OTEL_TRACES_SAMPLER_ARG = 'OTEL_TRACES_SAMPLER_ARG'
|
86
83
|
|
87
84
|
# @public_api
|
@@ -93,9 +93,10 @@ module Datadog
|
|
93
93
|
# @return [Array<String>]
|
94
94
|
option :propagation_style do |o|
|
95
95
|
o.type :array
|
96
|
-
|
96
|
+
# Note: Alias (DD_TRACE_PROPAGATION_STYLE) defined in supported-configurations.json
|
97
|
+
o.env Configuration::Ext::Distributed::ENV_PROPAGATION_STYLE
|
97
98
|
o.default []
|
98
|
-
o.after_set do |styles|
|
99
|
+
o.after_set do |styles, _, precedence|
|
99
100
|
next if styles.empty?
|
100
101
|
|
101
102
|
# Make values case-insensitive
|
@@ -109,8 +110,8 @@ module Datadog
|
|
109
110
|
false
|
110
111
|
end
|
111
112
|
end
|
112
|
-
set_option(:propagation_style_extract, styles)
|
113
|
-
set_option(:propagation_style_inject, styles)
|
113
|
+
set_option(:propagation_style_extract, styles, precedence: precedence)
|
114
|
+
set_option(:propagation_style_inject, styles, precedence: precedence)
|
114
115
|
end
|
115
116
|
end
|
116
117
|
|
@@ -133,13 +134,16 @@ module Datadog
|
|
133
134
|
# @default `DD_TRACE_ENABLED` environment variable, otherwise `true`
|
134
135
|
# @return [Boolean]
|
135
136
|
option :enabled do |o|
|
136
|
-
|
137
|
+
# Note: Alias (OTEL_TRACES_EXPORTER) defined in supported-configurations.json
|
138
|
+
o.env Tracing::Configuration::Ext::ENV_ENABLED
|
137
139
|
o.default true
|
138
140
|
o.type :bool
|
139
141
|
o.env_parser do |value|
|
140
142
|
value = value&.downcase
|
141
143
|
# Tracing is disabled when OTEL_TRACES_EXPORTER is none or
|
142
144
|
# DD_TRACE_ENABLED is 0 or false.
|
145
|
+
# DEV: The current implementation accepts all of the mentioned values
|
146
|
+
# for both environment variables, which is incorrect.
|
143
147
|
if ['none', 'false', '0'].include?(value)
|
144
148
|
false
|
145
149
|
# Tracing is enabled when DD_TRACE_ENABLED is true or 1
|
@@ -302,7 +306,8 @@ module Datadog
|
|
302
306
|
# @return [Float, nil]
|
303
307
|
option :default_rate do |o|
|
304
308
|
o.type :float, nilable: true
|
305
|
-
|
309
|
+
# Note: Alias (OTEL_TRACES_SAMPLER) defined in supported-configurations.json
|
310
|
+
o.env Tracing::Configuration::Ext::Sampling::ENV_SAMPLE_RATE
|
306
311
|
o.env_parser do |value|
|
307
312
|
# Parse the value as a float
|
308
313
|
next if value.nil?
|
@@ -321,7 +326,7 @@ module Datadog
|
|
321
326
|
when 'parentbased_always_off'
|
322
327
|
0.0
|
323
328
|
when 'parentbased_traceidratio'
|
324
|
-
|
329
|
+
DATADOG_ENV.fetch(Configuration::Ext::Sampling::OTEL_TRACES_SAMPLER_ARG, 1.0).to_f
|
325
330
|
else
|
326
331
|
value.to_f
|
327
332
|
end
|
@@ -355,7 +360,7 @@ module Datadog
|
|
355
360
|
# @public_api
|
356
361
|
option :rules do |o|
|
357
362
|
o.type :string, nilable: true
|
358
|
-
o.default {
|
363
|
+
o.default { DATADOG_ENV.fetch(Configuration::Ext::Sampling::ENV_RULES, nil) }
|
359
364
|
end
|
360
365
|
|
361
366
|
# Single span sampling rules.
|
@@ -372,8 +377,8 @@ module Datadog
|
|
372
377
|
option :span_rules do |o|
|
373
378
|
o.type :string, nilable: true
|
374
379
|
o.default do
|
375
|
-
rules =
|
376
|
-
rules_file =
|
380
|
+
rules = DATADOG_ENV[Tracing::Configuration::Ext::Sampling::Span::ENV_SPAN_SAMPLING_RULES]
|
381
|
+
rules_file = DATADOG_ENV[Tracing::Configuration::Ext::Sampling::Span::ENV_SPAN_SAMPLING_RULES_FILE]
|
377
382
|
|
378
383
|
if rules
|
379
384
|
if rules_file
|
@@ -9,13 +9,13 @@ module Datadog
|
|
9
9
|
# Register a callback to be invoked when components are reconfigured.
|
10
10
|
# @param name [String] the name of the integration
|
11
11
|
# @param callback [Proc] the callback to invoke
|
12
|
-
# @yieldparam config [Datadog::Configuration] the configuration to pass to callbacks
|
12
|
+
# @yieldparam config [Datadog::Core::Configuration::Settings] the configuration to pass to callbacks
|
13
13
|
def register(name, &callback)
|
14
14
|
@registry[name] = callback
|
15
15
|
end
|
16
16
|
|
17
17
|
# Invoke all registered callbacks with the given configuration.
|
18
|
-
# @param config [Datadog::Configuration] the configuration to pass to callbacks
|
18
|
+
# @param config [Datadog::Core::Configuration::Settings] the configuration to pass to callbacks
|
19
19
|
def configure(config)
|
20
20
|
@registry.each do |name, callback|
|
21
21
|
callback.call(config)
|
@@ -58,6 +58,13 @@ module Datadog
|
|
58
58
|
o.default []
|
59
59
|
o.env_parser { |v| ErrorExtensionEnvParser.call(v) }
|
60
60
|
end
|
61
|
+
|
62
|
+
# Surface GraphQL errors in Error Tracking.
|
63
|
+
option :error_tracking do |o|
|
64
|
+
o.env Ext::ENV_ERROR_TRACKING
|
65
|
+
o.type :bool
|
66
|
+
o.default false
|
67
|
+
end
|
61
68
|
end
|
62
69
|
end
|
63
70
|
end
|
@@ -13,6 +13,7 @@ module Datadog
|
|
13
13
|
ENV_ANALYTICS_SAMPLE_RATE = 'DD_TRACE_GRAPHQL_ANALYTICS_SAMPLE_RATE'
|
14
14
|
ENV_WITH_UNIFIED_TRACER = 'DD_TRACE_GRAPHQL_WITH_UNIFIED_TRACER'
|
15
15
|
ENV_ERROR_EXTENSIONS = 'DD_TRACE_GRAPHQL_ERROR_EXTENSIONS'
|
16
|
+
ENV_ERROR_TRACKING = 'DD_TRACE_GRAPHQL_ERROR_TRACKING'
|
16
17
|
SERVICE_NAME = 'graphql'
|
17
18
|
TAG_COMPONENT = 'graphql'
|
18
19
|
|
@@ -11,11 +11,45 @@ module Datadog
|
|
11
11
|
# which is required to use features such as API Catalog.
|
12
12
|
# DEV-3.0: This tracer should be the default one in the next major version.
|
13
13
|
module UnifiedTrace
|
14
|
+
include ::GraphQL::Tracing::PlatformTrace
|
15
|
+
|
14
16
|
def initialize(*args, **kwargs)
|
15
17
|
@has_prepare_span = respond_to?(:prepare_span)
|
18
|
+
|
19
|
+
# Cache configuration values to avoid repeated lookups
|
20
|
+
config = Datadog.configuration.tracing[:graphql]
|
21
|
+
@service_name = config[:service_name]
|
22
|
+
@analytics_enabled = config[:analytics_enabled]
|
23
|
+
@analytics_sample_rate = config[:analytics_sample_rate]
|
24
|
+
@error_extensions_config = config[:error_extensions]
|
25
|
+
|
26
|
+
load_error_event_attributes(config[:error_tracking])
|
27
|
+
|
16
28
|
super
|
17
29
|
end
|
18
30
|
|
31
|
+
def load_error_event_attributes(error_tracking)
|
32
|
+
if error_tracking
|
33
|
+
@event_name = Tracing::Metadata::Ext::Errors::EVENT_NAME
|
34
|
+
@message_key = Tracing::Metadata::Ext::Errors::ATTRIBUTE_MESSAGE
|
35
|
+
@type_key = Tracing::Metadata::Ext::Errors::ATTRIBUTE_TYPE
|
36
|
+
@stacktrace_key = Tracing::Metadata::Ext::Errors::ATTRIBUTE_STACKTRACE
|
37
|
+
@locations_key = 'graphql.error.locations'
|
38
|
+
@path_key = 'graphql.error.path'
|
39
|
+
@extensions_key = 'graphql.error.extensions.'
|
40
|
+
else
|
41
|
+
@event_name = Ext::EVENT_QUERY_ERROR
|
42
|
+
@message_key = 'message'
|
43
|
+
@type_key = 'type'
|
44
|
+
@stacktrace_key = 'stacktrace'
|
45
|
+
@locations_key = 'locations'
|
46
|
+
@path_key = 'path'
|
47
|
+
@extensions_key = 'extensions.'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private :load_error_event_attributes
|
52
|
+
|
19
53
|
def lex(*args, query_string:, **kwargs)
|
20
54
|
trace(proc { super }, 'lex', query_string, query_string: query_string)
|
21
55
|
end
|
@@ -50,10 +84,13 @@ module Datadog
|
|
50
84
|
trace(
|
51
85
|
proc { super },
|
52
86
|
'execute',
|
53
|
-
query.
|
87
|
+
operation_resource(query.selected_operation),
|
54
88
|
lambda { |span|
|
89
|
+
# Ensure this span can be aggregated by in the Datadog App, and generates RED metrics.
|
90
|
+
span.set_tag(Tracing::Metadata::Ext::TAG_KIND, Tracing::Metadata::Ext::SpanKind::TAG_SERVER)
|
91
|
+
|
55
92
|
span.set_tag('graphql.source', query.query_string)
|
56
|
-
span.set_tag('graphql.operation.type', query.selected_operation
|
93
|
+
span.set_tag('graphql.operation.type', query.selected_operation&.operation_type)
|
57
94
|
if query.selected_operation_name
|
58
95
|
span.set_tag(
|
59
96
|
'graphql.operation.name',
|
@@ -127,8 +164,6 @@ module Datadog
|
|
127
164
|
resolve_type_span(proc { super }, 'resolve_type_lazy', **kwargs)
|
128
165
|
end
|
129
166
|
|
130
|
-
include ::GraphQL::Tracing::PlatformTrace
|
131
|
-
|
132
167
|
def platform_field_key(field, *args, **kwargs)
|
133
168
|
field.path
|
134
169
|
end
|
@@ -153,16 +188,14 @@ module Datadog
|
|
153
188
|
# @param kwargs [Hash] the arguments to pass to `prepare_span`
|
154
189
|
# @yield [Span] the block to run before the trace, same as the `before` parameter
|
155
190
|
def trace(callable, trace_key, resource, before = nil, after = nil, **kwargs, &before_block)
|
156
|
-
config = Datadog.configuration.tracing[:graphql]
|
157
|
-
|
158
191
|
Tracing.trace(
|
159
192
|
"graphql.#{trace_key}",
|
160
193
|
type: 'graphql',
|
161
194
|
resource: resource,
|
162
|
-
service:
|
195
|
+
service: @service_name
|
163
196
|
) do |span|
|
164
|
-
if Contrib::Analytics.enabled?(
|
165
|
-
Contrib::Analytics.set_sample_rate(span,
|
197
|
+
if Contrib::Analytics.enabled?(@analytics_enabled)
|
198
|
+
Contrib::Analytics.set_sample_rate(span, @analytics_sample_rate)
|
166
199
|
end
|
167
200
|
|
168
201
|
# A sanity check for us.
|
@@ -194,27 +227,30 @@ module Datadog
|
|
194
227
|
end
|
195
228
|
end
|
196
229
|
|
230
|
+
def operation_resource(operation)
|
231
|
+
if operation&.name
|
232
|
+
"#{operation.operation_type} #{operation.name}"
|
233
|
+
else
|
234
|
+
'anonymous'
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
197
238
|
# Create a Span Event for each error that occurs at query level.
|
198
|
-
#
|
199
|
-
# These are represented in the Datadog App as special GraphQL errors,
|
200
|
-
# given their event name `dd.graphql.query.error`.
|
201
239
|
def add_query_error_events(span, errors)
|
202
|
-
capture_extensions = Datadog.configuration.tracing[:graphql][:error_extensions]
|
203
240
|
errors.each do |error|
|
204
|
-
|
241
|
+
attributes = if !@error_extensions_config.empty? && (extensions = error.extensions)
|
205
242
|
# Capture extensions, ensuring all values are primitives
|
206
243
|
extensions.each_with_object({}) do |(key, value), hash|
|
207
|
-
next unless
|
244
|
+
next unless @error_extensions_config.include?(key.to_s)
|
208
245
|
|
209
246
|
value = case value
|
210
247
|
when TrueClass, FalseClass, Integer, Float
|
211
248
|
value
|
212
249
|
else
|
213
|
-
# Stringify anything that is not a boolean or a number
|
214
250
|
value.to_s
|
215
251
|
end
|
216
252
|
|
217
|
-
hash[
|
253
|
+
hash[@extensions_key + key.to_s] = value
|
218
254
|
end
|
219
255
|
else
|
220
256
|
{}
|
@@ -224,16 +260,16 @@ module Datadog
|
|
224
260
|
# This is an unwritten contract in the `graphql` library.
|
225
261
|
# See for an example: https://github.com/rmosolgo/graphql-ruby/blob/0afa241775e5a113863766cce126214dee093464/lib/graphql/execution_error.rb#L32
|
226
262
|
graphql_error = error.to_h
|
227
|
-
|
228
|
-
|
229
|
-
span.span_events <<
|
230
|
-
|
231
|
-
attributes:
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
263
|
+
parsed_error = Core::Error.build_from(error)
|
264
|
+
|
265
|
+
span.span_events << SpanEvent.new(
|
266
|
+
@event_name,
|
267
|
+
attributes: attributes.merge!(
|
268
|
+
@type_key => parsed_error.type,
|
269
|
+
@stacktrace_key => parsed_error.backtrace,
|
270
|
+
@message_key => graphql_error['message'],
|
271
|
+
@locations_key => serialize_error_locations(graphql_error['locations']),
|
272
|
+
@path_key => graphql_error['path'],
|
237
273
|
)
|
238
274
|
)
|
239
275
|
end
|
@@ -24,6 +24,7 @@ module Datadog
|
|
24
24
|
# nginx header is seconds in the format "t=1512379167.574"
|
25
25
|
# apache header is microseconds in the format "t=1570633834463123"
|
26
26
|
# heroku header is milliseconds in the format "1570634024294"
|
27
|
+
# @see https://github.com/heroku/vegur/blob/65d168f757e0ddb448f41cfb9e4b0281c747378d/README.md?plain=1#L383-L384
|
27
28
|
time_string = header.to_s.delete('^0-9')
|
28
29
|
return if time_string.nil?
|
29
30
|
|
@@ -43,7 +43,13 @@ module Datadog
|
|
43
43
|
# excluding the time spent processing the request itself
|
44
44
|
queue_span.finish
|
45
45
|
|
46
|
-
yield
|
46
|
+
yield
|
47
|
+
ensure
|
48
|
+
# Ensure that the spans are finished even if an exception is raised.
|
49
|
+
# **This is very important** to prevent the trace from leaking between requests,
|
50
|
+
# especially because `queue_span` is normally a root span.
|
51
|
+
queue_span&.finish
|
52
|
+
request_span&.finish
|
47
53
|
end
|
48
54
|
end
|
49
55
|
end
|