datadog 2.0.0.beta2 → 2.0.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 +65 -1
- data/ext/datadog_profiling_native_extension/NativeExtensionDesign.md +1 -1
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +8 -20
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +18 -10
- data/ext/datadog_profiling_native_extension/crashtracker.c +108 -0
- data/ext/datadog_profiling_native_extension/extconf.rb +9 -23
- data/ext/datadog_profiling_native_extension/heap_recorder.c +38 -3
- data/ext/datadog_profiling_native_extension/heap_recorder.h +5 -0
- data/ext/datadog_profiling_native_extension/http_transport.c +0 -93
- data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +86 -0
- data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +4 -0
- data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +2 -12
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +25 -86
- data/ext/datadog_profiling_native_extension/profiling.c +2 -0
- data/ext/datadog_profiling_native_extension/ruby_helpers.h +3 -5
- data/ext/datadog_profiling_native_extension/stack_recorder.c +156 -55
- data/lib/datadog/appsec/contrib/devise/tracking.rb +8 -0
- data/lib/datadog/core/configuration/settings.rb +10 -79
- data/lib/datadog/core/remote/client.rb +1 -5
- data/lib/datadog/core/remote/configuration/repository.rb +1 -1
- data/lib/datadog/core/remote/dispatcher.rb +3 -3
- data/lib/datadog/core/telemetry/emitter.rb +1 -1
- data/lib/datadog/core/telemetry/http/response.rb +4 -0
- data/lib/datadog/opentelemetry/sdk/span_processor.rb +23 -1
- data/lib/datadog/opentelemetry/sdk/trace/span.rb +3 -1
- data/lib/datadog/profiling/component.rb +26 -2
- data/lib/datadog/profiling/crashtracker.rb +91 -0
- data/lib/datadog/profiling/exporter.rb +6 -3
- data/lib/datadog/profiling/http_transport.rb +7 -11
- data/lib/datadog/profiling/profiler.rb +9 -2
- data/lib/datadog/profiling/stack_recorder.rb +6 -2
- data/lib/datadog/profiling.rb +1 -0
- data/lib/datadog/tracing/component.rb +5 -1
- data/lib/datadog/tracing/configuration/dynamic.rb +39 -1
- data/lib/datadog/tracing/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +1 -0
- data/lib/datadog/tracing/contrib/active_record/integration.rb +10 -0
- data/lib/datadog/tracing/contrib/configuration/resolver.rb +43 -0
- data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +1 -1
- data/lib/datadog/tracing/correlation.rb +10 -6
- data/lib/datadog/tracing/remote.rb +5 -1
- data/lib/datadog/tracing/sampling/ext.rb +5 -1
- data/lib/datadog/tracing/sampling/matcher.rb +60 -31
- data/lib/datadog/tracing/sampling/rule.rb +12 -5
- data/lib/datadog/tracing/sampling/rule_sampler.rb +17 -1
- data/lib/datadog/tracing/sampling/span/matcher.rb +13 -41
- data/lib/datadog/tracing/span_link.rb +12 -6
- data/lib/datadog/tracing/span_operation.rb +6 -4
- data/lib/datadog/version.rb +1 -1
- metadata +7 -4
@@ -7,8 +7,8 @@ module Datadog
|
|
7
7
|
class Dispatcher
|
8
8
|
attr_reader :receivers
|
9
9
|
|
10
|
-
def initialize
|
11
|
-
@receivers =
|
10
|
+
def initialize(receivers)
|
11
|
+
@receivers = receivers
|
12
12
|
end
|
13
13
|
|
14
14
|
def dispatch(changes, repository)
|
@@ -45,7 +45,7 @@ module Datadog
|
|
45
45
|
@block.call(path)
|
46
46
|
end
|
47
47
|
|
48
|
-
# Matches on the
|
48
|
+
# Matches on the product's path
|
49
49
|
class Product < Matcher
|
50
50
|
def initialize(products)
|
51
51
|
block = ->(path) { products.include?(path.product) }
|
@@ -26,7 +26,7 @@ module Datadog
|
|
26
26
|
seq_id = self.class.sequence.next
|
27
27
|
payload = Request.build_payload(event, seq_id)
|
28
28
|
res = @http_transport.request(request_type: event.type, payload: payload.to_json)
|
29
|
-
Datadog.logger.debug { "Telemetry sent for event `#{event.type}` (
|
29
|
+
Datadog.logger.debug { "Telemetry sent for event `#{event.type}` (code: #{res.code.inspect})" }
|
30
30
|
res
|
31
31
|
rescue => e
|
32
32
|
Datadog.logger.debug("Unable to send telemetry request for event `#{event.type rescue 'unknown'}`: #{e}")
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'trace/span'
|
4
|
+
require_relative '../../tracing/span_link'
|
5
|
+
require_relative '../../tracing/trace_digest'
|
4
6
|
|
5
7
|
module Datadog
|
6
8
|
module OpenTelemetry
|
@@ -87,6 +89,21 @@ module Datadog
|
|
87
89
|
datadog_span.set_error([nil, span.status.description]) unless span.status.ok?
|
88
90
|
datadog_span.set_tags(span.attributes)
|
89
91
|
|
92
|
+
unless span.links.nil?
|
93
|
+
datadog_span.links = span.links.map do |link|
|
94
|
+
Datadog::Tracing::SpanLink.new(
|
95
|
+
Datadog::Tracing::TraceDigest.new(
|
96
|
+
trace_id: link.span_context.hex_trace_id.to_i(16),
|
97
|
+
span_id: link.span_context.hex_span_id.to_i(16),
|
98
|
+
trace_sampling_priority: (link.span_context.trace_flags&.sampled? ? 1 : 0),
|
99
|
+
trace_state: link.span_context.tracestate&.to_s,
|
100
|
+
span_remote: link.span_context.remote?,
|
101
|
+
),
|
102
|
+
attributes: link.attributes
|
103
|
+
)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
90
107
|
datadog_span
|
91
108
|
end
|
92
109
|
|
@@ -95,6 +112,11 @@ module Datadog
|
|
95
112
|
if attributes.key?('analytics.event') && (analytics_event = attributes['analytics.event']).respond_to?(:casecmp)
|
96
113
|
attributes[Tracing::Metadata::Ext::Analytics::TAG_SAMPLE_RATE] = analytics_event.casecmp('true') == 0 ? 1 : 0
|
97
114
|
end
|
115
|
+
|
116
|
+
if attributes.key?('http.response.status_code')
|
117
|
+
attributes[Tracing::Metadata::Ext::HTTP::TAG_STATUS_CODE] = attributes.delete('http.response.status_code')
|
118
|
+
end
|
119
|
+
|
98
120
|
attributes[Tracing::Metadata::Ext::TAG_KIND] = span.kind || 'internal'
|
99
121
|
|
100
122
|
kwargs = { start_time: ns_to_time(span.start_timestamp) }
|
@@ -119,7 +141,7 @@ module Datadog
|
|
119
141
|
end
|
120
142
|
attributes.flatten!(1)
|
121
143
|
|
122
|
-
kwargs[:tags] = attributes
|
144
|
+
kwargs[:tags] = attributes.to_h
|
123
145
|
|
124
146
|
[name, kwargs]
|
125
147
|
end
|
@@ -141,10 +141,12 @@ module Datadog
|
|
141
141
|
value.casecmp('true') == 0 ? 1 : 0
|
142
142
|
)
|
143
143
|
end
|
144
|
+
|
145
|
+
span.set_tag('http.status_code', value) if key == 'http.response.status_code'
|
144
146
|
end
|
145
147
|
|
146
148
|
DATADOG_SPAN_ATTRIBUTE_OVERRIDES = ['analytics.event', 'operation.name', 'resource.name', 'service.name',
|
147
|
-
'span.type'].freeze
|
149
|
+
'span.type', 'http.response.status_code'].freeze
|
148
150
|
|
149
151
|
::OpenTelemetry::SDK::Trace::Span.prepend(self)
|
150
152
|
end
|
@@ -72,8 +72,10 @@ module Datadog
|
|
72
72
|
exporter = build_profiler_exporter(settings, recorder, worker, internal_metadata: internal_metadata)
|
73
73
|
transport = build_profiler_transport(settings, agent_settings)
|
74
74
|
scheduler = Profiling::Scheduler.new(exporter: exporter, transport: transport, interval: upload_period_seconds)
|
75
|
+
crashtracker = build_crashtracker(settings, transport)
|
76
|
+
profiler = Profiling::Profiler.new(worker: worker, scheduler: scheduler, optional_crashtracker: crashtracker)
|
75
77
|
|
76
|
-
[
|
78
|
+
[profiler, { profiling_enabled: true }]
|
77
79
|
end
|
78
80
|
|
79
81
|
private_class_method def self.build_thread_context_collector(settings, recorder, optional_tracer, timeline_enabled)
|
@@ -110,6 +112,28 @@ module Datadog
|
|
110
112
|
)
|
111
113
|
end
|
112
114
|
|
115
|
+
private_class_method def self.build_crashtracker(settings, transport)
|
116
|
+
return unless settings.profiling.advanced.experimental_crash_tracking_enabled
|
117
|
+
|
118
|
+
# By default, the transport is an instance of HttpTransport, which validates the configuration and makes
|
119
|
+
# it available for us to use here.
|
120
|
+
# But we support overriding the transport with a user-specific one, which may e.g. write stuff to a file,
|
121
|
+
# and thus can't really provide a valid configuration to talk to a Datadog agent. Thus, in this situation,
|
122
|
+
# we can't use the crashtracker, even if enabled.
|
123
|
+
unless transport.respond_to?(:exporter_configuration)
|
124
|
+
Datadog.logger.warn(
|
125
|
+
'Cannot enable profiling crash tracking as a custom settings.profiling.exporter.transport is configured'
|
126
|
+
)
|
127
|
+
return
|
128
|
+
end
|
129
|
+
|
130
|
+
Datadog::Profiling::Crashtracker.new(
|
131
|
+
exporter_configuration: transport.exporter_configuration,
|
132
|
+
tags: Datadog::Profiling::TagBuilder.call(settings: settings),
|
133
|
+
upload_timeout_seconds: settings.profiling.upload.timeout_seconds,
|
134
|
+
)
|
135
|
+
end
|
136
|
+
|
113
137
|
private_class_method def self.enable_gc_profiling?(settings)
|
114
138
|
return false unless settings.profiling.advanced.gc_enabled
|
115
139
|
|
@@ -243,7 +267,7 @@ module Datadog
|
|
243
267
|
|
244
268
|
private_class_method def self.no_signals_workaround_enabled?(settings) # rubocop:disable Metrics/MethodLength
|
245
269
|
setting_value = settings.profiling.advanced.no_signals_workaround_enabled
|
246
|
-
legacy_ruby_that_should_use_workaround = RUBY_VERSION.start_with?('2.
|
270
|
+
legacy_ruby_that_should_use_workaround = RUBY_VERSION.start_with?('2.5.')
|
247
271
|
|
248
272
|
unless [true, false, :auto].include?(setting_value)
|
249
273
|
Datadog.logger.error(
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'libdatadog'
|
4
|
+
|
5
|
+
module Datadog
|
6
|
+
module Profiling
|
7
|
+
# Used to report Ruby VM crashes.
|
8
|
+
# The interesting bits are implemented as native code and using libdatadog.
|
9
|
+
#
|
10
|
+
# NOTE: The crashtracker native state is a singleton; so even if you create multiple instances of `Crashtracker`
|
11
|
+
# and start them, it only works as "last writer wins". Same for stop -- there's only one state, so calling stop
|
12
|
+
# on it will stop the crash tracker, regardless of which instance started it.
|
13
|
+
#
|
14
|
+
# Methods prefixed with _native_ are implemented in `crashtracker.c`
|
15
|
+
class Crashtracker
|
16
|
+
private
|
17
|
+
|
18
|
+
attr_reader \
|
19
|
+
:exporter_configuration,
|
20
|
+
:tags_as_array,
|
21
|
+
:path_to_crashtracking_receiver_binary,
|
22
|
+
:ld_library_path,
|
23
|
+
:upload_timeout_seconds
|
24
|
+
|
25
|
+
public
|
26
|
+
|
27
|
+
def initialize(
|
28
|
+
exporter_configuration:,
|
29
|
+
tags:,
|
30
|
+
upload_timeout_seconds:,
|
31
|
+
path_to_crashtracking_receiver_binary: Libdatadog.path_to_crashtracking_receiver_binary,
|
32
|
+
ld_library_path: Libdatadog.ld_library_path
|
33
|
+
)
|
34
|
+
@exporter_configuration = exporter_configuration
|
35
|
+
@tags_as_array = tags.to_a
|
36
|
+
@upload_timeout_seconds = upload_timeout_seconds
|
37
|
+
@path_to_crashtracking_receiver_binary = path_to_crashtracking_receiver_binary
|
38
|
+
@ld_library_path = ld_library_path
|
39
|
+
end
|
40
|
+
|
41
|
+
def start
|
42
|
+
start_or_update_on_fork(action: :start)
|
43
|
+
end
|
44
|
+
|
45
|
+
def reset_after_fork
|
46
|
+
start_or_update_on_fork(action: :update_on_fork)
|
47
|
+
end
|
48
|
+
|
49
|
+
def stop
|
50
|
+
begin
|
51
|
+
self.class._native_stop
|
52
|
+
Datadog.logger.debug('Crash tracking stopped successfully')
|
53
|
+
rescue => e
|
54
|
+
Datadog.logger.error("Failed to stop crash tracking: #{e.message}")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def start_or_update_on_fork(action:)
|
61
|
+
unless path_to_crashtracking_receiver_binary
|
62
|
+
Datadog.logger.warn(
|
63
|
+
"Cannot #{action} profiling crash tracking as no path_to_crashtracking_receiver_binary was found"
|
64
|
+
)
|
65
|
+
return
|
66
|
+
end
|
67
|
+
|
68
|
+
unless ld_library_path
|
69
|
+
Datadog.logger.warn(
|
70
|
+
"Cannot #{action} profiling crash tracking as no ld_library_path was found"
|
71
|
+
)
|
72
|
+
return
|
73
|
+
end
|
74
|
+
|
75
|
+
begin
|
76
|
+
self.class._native_start_or_update_on_fork(
|
77
|
+
action: action,
|
78
|
+
exporter_configuration: exporter_configuration,
|
79
|
+
path_to_crashtracking_receiver_binary: path_to_crashtracking_receiver_binary,
|
80
|
+
ld_library_path: ld_library_path,
|
81
|
+
tags_as_array: tags_as_array,
|
82
|
+
upload_timeout_seconds: Integer(upload_timeout_seconds),
|
83
|
+
)
|
84
|
+
Datadog.logger.debug("Crash tracking #{action} successful")
|
85
|
+
rescue => e
|
86
|
+
Datadog.logger.error("Failed to #{action} crash tracking: #{e.message}")
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -54,10 +54,11 @@ module Datadog
|
|
54
54
|
|
55
55
|
def flush
|
56
56
|
worker_stats = @worker.stats_and_reset_not_thread_safe
|
57
|
-
|
58
|
-
|
57
|
+
serialization_result = pprof_recorder.serialize
|
58
|
+
return if serialization_result.nil?
|
59
59
|
|
60
|
-
|
60
|
+
start, finish, compressed_pprof, profile_stats = serialization_result
|
61
|
+
@last_flush_finish_at = finish
|
61
62
|
|
62
63
|
if duration_below_threshold?(start, finish)
|
63
64
|
Datadog.logger.debug('Skipped exporting profiling events as profile duration is below minimum')
|
@@ -77,6 +78,8 @@ module Datadog
|
|
77
78
|
internal_metadata: internal_metadata.merge(
|
78
79
|
{
|
79
80
|
worker_stats: worker_stats,
|
81
|
+
profile_stats: profile_stats,
|
82
|
+
recorder_stats: pprof_recorder.stats,
|
80
83
|
gc: GC.stat,
|
81
84
|
}
|
82
85
|
),
|
@@ -7,6 +7,8 @@ module Datadog
|
|
7
7
|
# Used to report profiling data to Datadog.
|
8
8
|
# Methods prefixed with _native_ are implemented in `http_transport.c`
|
9
9
|
class HttpTransport
|
10
|
+
attr_reader :exporter_configuration
|
11
|
+
|
10
12
|
def initialize(agent_settings:, site:, api_key:, upload_timeout_seconds:)
|
11
13
|
@upload_timeout_milliseconds = (upload_timeout_seconds * 1_000).to_i
|
12
14
|
|
@@ -14,19 +16,19 @@ module Datadog
|
|
14
16
|
|
15
17
|
@exporter_configuration =
|
16
18
|
if agentless?(site, api_key)
|
17
|
-
[:agentless, site, api_key]
|
19
|
+
[:agentless, site, api_key].freeze
|
18
20
|
else
|
19
|
-
[:agent, base_url_from(agent_settings)]
|
21
|
+
[:agent, base_url_from(agent_settings)].freeze
|
20
22
|
end
|
21
23
|
|
22
|
-
status, result = validate_exporter(
|
24
|
+
status, result = validate_exporter(exporter_configuration)
|
23
25
|
|
24
26
|
raise(ArgumentError, "Failed to initialize transport: #{result}") if status == :error
|
25
27
|
end
|
26
28
|
|
27
29
|
def export(flush)
|
28
30
|
status, result = do_export(
|
29
|
-
exporter_configuration:
|
31
|
+
exporter_configuration: exporter_configuration,
|
30
32
|
upload_timeout_milliseconds: @upload_timeout_milliseconds,
|
31
33
|
|
32
34
|
# why "timespec"?
|
@@ -66,12 +68,6 @@ module Datadog
|
|
66
68
|
end
|
67
69
|
end
|
68
70
|
|
69
|
-
# Used to log soft failures in `ddog_Vec_tag_push` (e.g. we still report the profile in these cases)
|
70
|
-
# Called from native code
|
71
|
-
def self.log_failure_to_process_tag(failure_details)
|
72
|
-
Datadog.logger.warn("Failed to add tag to profiling request: #{failure_details}")
|
73
|
-
end
|
74
|
-
|
75
71
|
private
|
76
72
|
|
77
73
|
def base_url_from(agent_settings)
|
@@ -136,7 +132,7 @@ module Datadog
|
|
136
132
|
end
|
137
133
|
|
138
134
|
def config_without_api_key
|
139
|
-
[
|
135
|
+
[exporter_configuration[0..1]].to_h
|
140
136
|
end
|
141
137
|
end
|
142
138
|
end
|
@@ -8,21 +8,24 @@ module Datadog
|
|
8
8
|
|
9
9
|
private
|
10
10
|
|
11
|
-
attr_reader :worker, :scheduler
|
11
|
+
attr_reader :worker, :scheduler, :optional_crashtracker
|
12
12
|
|
13
13
|
public
|
14
14
|
|
15
|
-
def initialize(worker:, scheduler:)
|
15
|
+
def initialize(worker:, scheduler:, optional_crashtracker:)
|
16
16
|
@worker = worker
|
17
17
|
@scheduler = scheduler
|
18
|
+
@optional_crashtracker = optional_crashtracker
|
18
19
|
end
|
19
20
|
|
20
21
|
def start
|
21
22
|
after_fork! do
|
23
|
+
optional_crashtracker.reset_after_fork if optional_crashtracker
|
22
24
|
worker.reset_after_fork
|
23
25
|
scheduler.reset_after_fork
|
24
26
|
end
|
25
27
|
|
28
|
+
optional_crashtracker.start if optional_crashtracker
|
26
29
|
worker.start(on_failure_proc: proc { component_failed(:worker) })
|
27
30
|
scheduler.start(on_failure_proc: proc { component_failed(:scheduler) })
|
28
31
|
end
|
@@ -32,6 +35,7 @@ module Datadog
|
|
32
35
|
|
33
36
|
stop_worker
|
34
37
|
stop_scheduler
|
38
|
+
optional_crashtracker.stop if optional_crashtracker
|
35
39
|
end
|
36
40
|
|
37
41
|
private
|
@@ -51,6 +55,9 @@ module Datadog
|
|
51
55
|
'See previous log messages for details.'
|
52
56
|
)
|
53
57
|
|
58
|
+
# We explicitly not stop the crash tracker in this situation, under the assumption that, if a component failed,
|
59
|
+
# we're operating in a degraded state and crash tracking may still be helpful.
|
60
|
+
|
54
61
|
if failed_component == :worker
|
55
62
|
stop_scheduler
|
56
63
|
elsif failed_component == :scheduler
|
@@ -33,11 +33,11 @@ module Datadog
|
|
33
33
|
status, result = @no_concurrent_synchronize_mutex.synchronize { self.class._native_serialize(self) }
|
34
34
|
|
35
35
|
if status == :ok
|
36
|
-
start, finish, encoded_pprof = result
|
36
|
+
start, finish, encoded_pprof, profile_stats = result
|
37
37
|
|
38
38
|
Datadog.logger.debug { "Encoded profile covering #{start.iso8601} to #{finish.iso8601}" }
|
39
39
|
|
40
|
-
[start, finish, encoded_pprof]
|
40
|
+
[start, finish, encoded_pprof, profile_stats]
|
41
41
|
else
|
42
42
|
error_message = result
|
43
43
|
|
@@ -64,6 +64,10 @@ module Datadog
|
|
64
64
|
def reset_after_fork
|
65
65
|
self.class._native_reset_after_fork(self)
|
66
66
|
end
|
67
|
+
|
68
|
+
def stats
|
69
|
+
self.class._native_stats(self)
|
70
|
+
end
|
67
71
|
end
|
68
72
|
end
|
69
73
|
end
|
data/lib/datadog/profiling.rb
CHANGED
@@ -143,6 +143,7 @@ module Datadog
|
|
143
143
|
require_relative 'profiling/collectors/idle_sampling_helper'
|
144
144
|
require_relative 'profiling/collectors/stack'
|
145
145
|
require_relative 'profiling/collectors/thread_context'
|
146
|
+
require_relative 'profiling/crashtracker'
|
146
147
|
require_relative 'profiling/stack_recorder'
|
147
148
|
require_relative 'profiling/exporter'
|
148
149
|
require_relative 'profiling/flush'
|
@@ -124,9 +124,13 @@ module Datadog
|
|
124
124
|
end
|
125
125
|
|
126
126
|
WRITER_RECORD_ENVIRONMENT_INFORMATION_CALLBACK = lambda do |_, responses|
|
127
|
-
|
127
|
+
WRITER_RECORD_ENVIRONMENT_INFORMATION_ONLY_ONCE.run do
|
128
|
+
Tracing::Diagnostics::EnvironmentLogger.collect_and_log!(responses: responses)
|
129
|
+
end
|
128
130
|
end
|
129
131
|
|
132
|
+
WRITER_RECORD_ENVIRONMENT_INFORMATION_ONLY_ONCE = Core::Utils::OnlyOnce.new
|
133
|
+
|
130
134
|
# Create new lambda for writer callback,
|
131
135
|
# capture the current sampler in the callback closure.
|
132
136
|
def writer_update_priority_sampler_rates_callback(sampler)
|
@@ -53,8 +53,46 @@ module Datadog
|
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
56
|
+
# Dynamic configuration for `DD_TRACE_SAMPLING_RULES`.
|
57
|
+
class TracingSamplingRules < SimpleOption
|
58
|
+
def initialize
|
59
|
+
super('tracing_sampling_rules', 'DD_TRACE_SAMPLING_RULES', :rules)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Ensures sampler is rebuilt and new configuration is applied
|
63
|
+
def call(tracing_sampling_rules)
|
64
|
+
# Modify the remote configuration value that it matches the
|
65
|
+
# local environment variable it configures.
|
66
|
+
if tracing_sampling_rules
|
67
|
+
tracing_sampling_rules.each do |rule|
|
68
|
+
next unless (tags = rule['tags'])
|
69
|
+
|
70
|
+
# Tag maps come in as arrays of 'key' and `value_glob`.
|
71
|
+
# We need to convert them into a hash for local use.
|
72
|
+
tag_array = tags.map! do |tag|
|
73
|
+
[tag['key'], tag['value_glob']]
|
74
|
+
end
|
75
|
+
|
76
|
+
rule['tags'] = tag_array.to_h
|
77
|
+
end
|
78
|
+
|
79
|
+
# The configuration is stored as JSON, so we need to convert it back
|
80
|
+
tracing_sampling_rules = tracing_sampling_rules.to_json
|
81
|
+
end
|
82
|
+
|
83
|
+
super(tracing_sampling_rules)
|
84
|
+
Datadog.send(:components).reconfigure_live_sampler
|
85
|
+
end
|
86
|
+
|
87
|
+
protected
|
88
|
+
|
89
|
+
def configuration_object
|
90
|
+
Datadog.configuration.tracing.sampling
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
56
94
|
# List of all tracing dynamic configuration options supported.
|
57
|
-
OPTIONS = [LogInjectionEnabled, TracingHeaderTags, TracingSamplingRate].map do |option_class|
|
95
|
+
OPTIONS = [LogInjectionEnabled, TracingHeaderTags, TracingSamplingRate, TracingSamplingRules].map do |option_class|
|
58
96
|
option = option_class.new
|
59
97
|
[option.name, option.env_var, option]
|
60
98
|
end
|
@@ -4,6 +4,7 @@ require_relative 'configuration/resolver'
|
|
4
4
|
require_relative 'configuration/settings'
|
5
5
|
require_relative 'events'
|
6
6
|
require_relative 'patcher'
|
7
|
+
require_relative '../component'
|
7
8
|
require_relative '../integration'
|
8
9
|
require_relative '../rails/ext'
|
9
10
|
require_relative '../rails/utils'
|
@@ -50,6 +51,15 @@ module Datadog
|
|
50
51
|
def resolver
|
51
52
|
@resolver ||= Configuration::Resolver.new
|
52
53
|
end
|
54
|
+
|
55
|
+
def reset_resolver_cache
|
56
|
+
@resolver&.reset_cache
|
57
|
+
end
|
58
|
+
|
59
|
+
Contrib::Component.register('activerecord') do |_config|
|
60
|
+
# Ensure resolver cache is reset on configuration change
|
61
|
+
Datadog.configuration.tracing.fetch_integration(:active_record).reset_resolver_cache
|
62
|
+
end
|
53
63
|
end
|
54
64
|
end
|
55
65
|
end
|
@@ -79,6 +79,49 @@ module Datadog
|
|
79
79
|
matcher
|
80
80
|
end
|
81
81
|
end
|
82
|
+
|
83
|
+
# The {CachingResolver} is a mixin that provides caching functionality to the {Resolver} class.
|
84
|
+
# This is useful when {Resolver#resolve} values that are expensive to compute.
|
85
|
+
# This is a size-limited, FIFO cache.
|
86
|
+
#
|
87
|
+
# @example
|
88
|
+
# class MyResolver < Datadog::Tracing::Contrib::Configuration::Resolver
|
89
|
+
# prepend Datadog::Tracing::Contrib::Configuration::CachingResolver
|
90
|
+
# # ...
|
91
|
+
# end
|
92
|
+
module CachingResolver
|
93
|
+
# @param [Integer] cache_limit maximum number of entries to cache
|
94
|
+
def initialize(*args, cache_limit: 200)
|
95
|
+
super(*args)
|
96
|
+
|
97
|
+
@cache_limit = cache_limit
|
98
|
+
@cache = {}
|
99
|
+
end
|
100
|
+
|
101
|
+
# (see Resolver#resolve)
|
102
|
+
def resolve(value)
|
103
|
+
if @cache.key?(value)
|
104
|
+
@cache[value]
|
105
|
+
else
|
106
|
+
if @cache.size >= @cache_limit
|
107
|
+
@cache.shift # Remove the oldest entry if cache is full
|
108
|
+
end
|
109
|
+
|
110
|
+
@cache[value] = super
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# (see Resolver#add)
|
115
|
+
def add(matcher, value)
|
116
|
+
reset_cache # Bust the cache when a new matcher is added
|
117
|
+
super
|
118
|
+
end
|
119
|
+
|
120
|
+
# Clears the internal cache.
|
121
|
+
def reset_cache
|
122
|
+
@cache.clear
|
123
|
+
end
|
124
|
+
end
|
82
125
|
end
|
83
126
|
end
|
84
127
|
end
|
@@ -24,7 +24,7 @@ module Datadog
|
|
24
24
|
|
25
25
|
Tracing.trace(Ext::SPAN_QUERY, service: service) do |span, trace_op|
|
26
26
|
span.resource = sql
|
27
|
-
span.
|
27
|
+
span.type = Tracing::Metadata::Ext::SQL::TYPE
|
28
28
|
|
29
29
|
if datadog_configuration[:peer_service]
|
30
30
|
span.set_tag(
|
@@ -73,12 +73,7 @@ module Datadog
|
|
73
73
|
end
|
74
74
|
|
75
75
|
def trace_id
|
76
|
-
|
77
|
-
!Tracing::Utils::TraceId.to_high_order(@trace_id).zero?
|
78
|
-
Kernel.format('%032x', @trace_id)
|
79
|
-
else
|
80
|
-
Tracing::Utils::TraceId.to_low_order(@trace_id).to_s
|
81
|
-
end
|
76
|
+
Correlation.format_trace_id(@trace_id)
|
82
77
|
end
|
83
78
|
end
|
84
79
|
|
@@ -97,6 +92,15 @@ module Datadog
|
|
97
92
|
trace_id: digest.trace_id,
|
98
93
|
)
|
99
94
|
end
|
95
|
+
|
96
|
+
def format_trace_id(trace_id)
|
97
|
+
if Datadog.configuration.tracing.trace_id_128_bit_logging_enabled &&
|
98
|
+
!Tracing::Utils::TraceId.to_high_order(trace_id).zero?
|
99
|
+
Kernel.format('%032x', trace_id)
|
100
|
+
else
|
101
|
+
Tracing::Utils::TraceId.to_low_order(trace_id).to_s
|
102
|
+
end
|
103
|
+
end
|
100
104
|
end
|
101
105
|
end
|
102
106
|
end
|
@@ -12,12 +12,16 @@ module Datadog
|
|
12
12
|
class << self
|
13
13
|
PRODUCT = 'APM_TRACING'
|
14
14
|
|
15
|
+
CAPABILITIES = [
|
16
|
+
1 << 29 # APM_TRACING_SAMPLE_RULES: Dynamic trace sampling rules configuration
|
17
|
+
].freeze
|
18
|
+
|
15
19
|
def products
|
16
20
|
[PRODUCT]
|
17
21
|
end
|
18
22
|
|
19
23
|
def capabilities
|
20
|
-
|
24
|
+
CAPABILITIES
|
21
25
|
end
|
22
26
|
|
23
27
|
def process_config(config, content)
|
@@ -40,7 +40,7 @@ module Datadog
|
|
40
40
|
DEFAULT = '-0'
|
41
41
|
# The sampling rate received in the agent's http response.
|
42
42
|
AGENT_RATE = '-1'
|
43
|
-
#
|
43
|
+
# Locally configured rule.
|
44
44
|
TRACE_SAMPLING_RULE = '-3'
|
45
45
|
# User directly sets sampling priority via {Tracing.reject!} or {Tracing.keep!},
|
46
46
|
# or by a custom sampler implementation.
|
@@ -49,6 +49,10 @@ module Datadog
|
|
49
49
|
ASM = '-5'
|
50
50
|
# Single Span Sampled.
|
51
51
|
SPAN_SAMPLING_RATE = '-8'
|
52
|
+
# Dynamically configured rule, explicitly created by the user.
|
53
|
+
REMOTE_USER_RULE = '-11'
|
54
|
+
# Dynamically configured rule, automatically generated by Datadog.
|
55
|
+
REMOTE_DYNAMIC_RULE = '-12'
|
52
56
|
end
|
53
57
|
end
|
54
58
|
end
|