ddtrace 0.52.0 → 0.54.2
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 +174 -11
- data/ddtrace.gemspec +6 -3
- data/docs/DevelopmentGuide.md +1 -6
- data/docs/GettingStarted.md +109 -18
- data/docs/ProfilingDevelopment.md +2 -2
- data/ext/ddtrace_profiling_native_extension/NativeExtensionDesign.md +86 -0
- data/ext/ddtrace_profiling_native_extension/clock_id.h +4 -0
- data/ext/ddtrace_profiling_native_extension/clock_id_from_pthread.c +52 -0
- data/ext/ddtrace_profiling_native_extension/clock_id_noop.c +14 -0
- data/ext/ddtrace_profiling_native_extension/extconf.rb +177 -8
- data/ext/ddtrace_profiling_native_extension/private_vm_api_access.c +35 -0
- data/ext/ddtrace_profiling_native_extension/private_vm_api_access.h +3 -0
- data/ext/ddtrace_profiling_native_extension/profiling.c +6 -1
- data/lib/datadog/ci/contrib/cucumber/formatter.rb +1 -0
- data/lib/datadog/ci/contrib/rspec/example.rb +1 -0
- data/lib/datadog/ci/contrib/rspec/integration.rb +2 -2
- data/lib/datadog/ci/ext/environment.rb +64 -22
- data/lib/datadog/ci/ext/test.rb +1 -0
- data/lib/datadog/ci/test.rb +5 -1
- data/lib/datadog/contrib.rb +2 -0
- data/lib/datadog/core/environment/vm_cache.rb +46 -0
- data/lib/ddtrace/buffer.rb +28 -16
- data/lib/ddtrace/configuration/agent_settings_resolver.rb +131 -53
- data/lib/ddtrace/configuration/components.rb +1 -1
- data/lib/ddtrace/configuration/settings.rb +13 -3
- data/lib/ddtrace/context.rb +10 -2
- data/lib/ddtrace/contrib/action_cable/instrumentation.rb +46 -0
- data/lib/ddtrace/contrib/action_cable/patcher.rb +1 -0
- data/lib/ddtrace/contrib/action_mailer/configuration/settings.rb +32 -0
- data/lib/ddtrace/contrib/action_mailer/event.rb +50 -0
- data/lib/ddtrace/contrib/action_mailer/events/deliver.rb +54 -0
- data/lib/ddtrace/contrib/action_mailer/events/process.rb +41 -0
- data/lib/ddtrace/contrib/action_mailer/events.rb +31 -0
- data/lib/ddtrace/contrib/action_mailer/ext.rb +32 -0
- data/lib/ddtrace/contrib/action_mailer/integration.rb +45 -0
- data/lib/ddtrace/contrib/action_mailer/patcher.rb +27 -0
- data/lib/ddtrace/contrib/active_job/configuration/settings.rb +33 -0
- data/lib/ddtrace/contrib/active_job/event.rb +54 -0
- data/lib/ddtrace/contrib/active_job/events/discard.rb +46 -0
- data/lib/ddtrace/contrib/active_job/events/enqueue.rb +45 -0
- data/lib/ddtrace/contrib/active_job/events/enqueue_at.rb +45 -0
- data/lib/ddtrace/contrib/active_job/events/enqueue_retry.rb +47 -0
- data/lib/ddtrace/contrib/active_job/events/perform.rb +45 -0
- data/lib/ddtrace/contrib/active_job/events/retry_stopped.rb +46 -0
- data/lib/ddtrace/contrib/active_job/events.rb +39 -0
- data/lib/ddtrace/contrib/active_job/ext.rb +32 -0
- data/lib/ddtrace/contrib/active_job/integration.rb +46 -0
- data/lib/ddtrace/contrib/active_job/log_injection.rb +21 -0
- data/lib/ddtrace/contrib/active_job/patcher.rb +33 -0
- data/lib/ddtrace/contrib/auto_instrument.rb +0 -1
- data/lib/ddtrace/contrib/delayed_job/plugin.rb +2 -2
- data/lib/ddtrace/contrib/mongodb/instrumentation.rb +1 -1
- data/lib/ddtrace/contrib/mongodb/integration.rb +5 -0
- data/lib/ddtrace/contrib/rails/auto_instrument_railtie.rb +0 -1
- data/lib/ddtrace/contrib/rails/configuration/settings.rb +7 -0
- data/lib/ddtrace/contrib/rails/framework.rb +24 -1
- data/lib/ddtrace/contrib/rails/patcher.rb +19 -10
- data/lib/ddtrace/contrib/redis/instrumentation.rb +90 -0
- data/lib/ddtrace/contrib/redis/patcher.rb +2 -84
- data/lib/ddtrace/contrib/registerable.rb +0 -1
- data/lib/ddtrace/contrib/resque/integration.rb +1 -5
- data/lib/ddtrace/contrib/sidekiq/ext.rb +3 -0
- data/lib/ddtrace/contrib/sidekiq/integration.rb +10 -0
- data/lib/ddtrace/contrib/sidekiq/patcher.rb +26 -0
- data/lib/ddtrace/contrib/sidekiq/server_internal_tracer/heartbeat.rb +30 -0
- data/lib/ddtrace/contrib/sidekiq/server_internal_tracer/job_fetch.rb +30 -0
- data/lib/ddtrace/contrib/sidekiq/server_internal_tracer/scheduled_push.rb +29 -0
- data/lib/ddtrace/contrib/sinatra/env.rb +2 -1
- data/lib/ddtrace/contrib/sinatra/tracer.rb +15 -2
- data/lib/ddtrace/ext/git.rb +12 -0
- data/lib/ddtrace/ext/priority.rb +6 -4
- data/lib/ddtrace/ext/profiling.rb +8 -11
- data/lib/ddtrace/ext/runtime.rb +3 -0
- data/lib/ddtrace/ext/transport.rb +11 -0
- data/lib/ddtrace/metrics.rb +2 -2
- data/lib/ddtrace/profiling/collectors/stack.rb +112 -72
- data/lib/ddtrace/profiling/encoding/profile.rb +10 -2
- data/lib/ddtrace/profiling/events/stack.rb +13 -13
- data/lib/ddtrace/profiling/native_extension.rb +23 -1
- data/lib/ddtrace/profiling/pprof/builder.rb +8 -2
- data/lib/ddtrace/profiling/pprof/converter.rb +22 -9
- data/lib/ddtrace/profiling/pprof/stack_sample.rb +32 -9
- data/lib/ddtrace/profiling/pprof/template.rb +2 -2
- data/lib/ddtrace/profiling/scheduler.rb +20 -4
- data/lib/ddtrace/profiling/tasks/setup.rb +21 -13
- data/lib/ddtrace/profiling/trace_identifiers/ddtrace.rb +10 -9
- data/lib/ddtrace/profiling/trace_identifiers/helper.rb +5 -5
- data/lib/ddtrace/profiling/transport/http/api/endpoint.rb +8 -15
- data/lib/ddtrace/profiling/transport/http.rb +8 -17
- data/lib/ddtrace/profiling.rb +0 -2
- data/lib/ddtrace/runtime/metrics.rb +14 -0
- data/lib/ddtrace/sampler.rb +18 -8
- data/lib/ddtrace/sampling/rule_sampler.rb +13 -1
- data/lib/ddtrace/span.rb +7 -19
- data/lib/ddtrace/tracer.rb +1 -1
- data/lib/ddtrace/transport/http/adapters/net.rb +13 -3
- data/lib/ddtrace/transport/http/adapters/test.rb +4 -2
- data/lib/ddtrace/transport/http/adapters/unix_socket.rb +23 -12
- data/lib/ddtrace/transport/http/builder.rb +13 -6
- data/lib/ddtrace/transport/http.rb +5 -11
- data/lib/ddtrace/utils/time.rb +11 -6
- data/lib/ddtrace/version.rb +2 -2
- data/lib/ddtrace/workers/{loop.rb → interval_loop.rb} +0 -16
- data/lib/ddtrace/workers/polling.rb +1 -1
- metadata +40 -10
- data/lib/ddtrace/profiling/ext/cpu.rb +0 -67
- data/lib/ddtrace/profiling/ext/cthread.rb +0 -156
|
@@ -30,8 +30,9 @@ module Datadog
|
|
|
30
30
|
def initialize(*_)
|
|
31
31
|
super
|
|
32
32
|
|
|
33
|
+
@most_recent_trace_samples = {}
|
|
33
34
|
@processed_unique_stacks = 0
|
|
34
|
-
@
|
|
35
|
+
@processed_with_trace = 0
|
|
35
36
|
end
|
|
36
37
|
|
|
37
38
|
def add_events!(stack_samples)
|
|
@@ -40,9 +41,28 @@ module Datadog
|
|
|
40
41
|
end
|
|
41
42
|
|
|
42
43
|
def stack_sample_group_key(stack_sample)
|
|
44
|
+
# We want to make sure we have the most recent sample for any trace.
|
|
45
|
+
# (This is done here to save an iteration over all samples.)
|
|
46
|
+
update_most_recent_trace_sample(stack_sample)
|
|
47
|
+
|
|
43
48
|
stack_sample.hash
|
|
44
49
|
end
|
|
45
50
|
|
|
51
|
+
# Track the most recent sample for each trace (identified by root span id)
|
|
52
|
+
def update_most_recent_trace_sample(stack_sample)
|
|
53
|
+
return unless stack_sample.root_span_id && stack_sample.trace_resource
|
|
54
|
+
|
|
55
|
+
# Update trace resource with most recent value
|
|
56
|
+
if (most_recent_trace_sample = @most_recent_trace_samples[stack_sample.root_span_id])
|
|
57
|
+
if most_recent_trace_sample.timestamp < stack_sample.timestamp
|
|
58
|
+
@most_recent_trace_samples[stack_sample.root_span_id] = stack_sample
|
|
59
|
+
end
|
|
60
|
+
else
|
|
61
|
+
# Add trace resource
|
|
62
|
+
@most_recent_trace_samples[stack_sample.root_span_id] = stack_sample
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
46
66
|
def build_samples(stack_samples)
|
|
47
67
|
groups = group_events(stack_samples, &method(:stack_sample_group_key))
|
|
48
68
|
groups.collect do |_group_key, group|
|
|
@@ -64,7 +84,7 @@ module Datadog
|
|
|
64
84
|
)
|
|
65
85
|
end
|
|
66
86
|
|
|
67
|
-
def
|
|
87
|
+
def build_event_values(stack_sample)
|
|
68
88
|
no_value = Datadog::Ext::Profiling::Pprof::SAMPLE_VALUE_NO_VALUE
|
|
69
89
|
values = super(stack_sample)
|
|
70
90
|
values[sample_value_index(:cpu_time_ns)] = stack_sample.cpu_time_interval_ns || no_value
|
|
@@ -80,15 +100,15 @@ module Datadog
|
|
|
80
100
|
)
|
|
81
101
|
]
|
|
82
102
|
|
|
83
|
-
|
|
103
|
+
root_span_id = stack_sample.root_span_id || 0
|
|
84
104
|
span_id = stack_sample.span_id || 0
|
|
85
105
|
|
|
86
|
-
if
|
|
87
|
-
@
|
|
106
|
+
if root_span_id != 0 && span_id != 0
|
|
107
|
+
@processed_with_trace += 1
|
|
88
108
|
|
|
89
109
|
labels << Perftools::Profiles::Label.new(
|
|
90
|
-
key: builder.string_table.fetch(Datadog::Ext::Profiling::Pprof::
|
|
91
|
-
str: builder.string_table.fetch(
|
|
110
|
+
key: builder.string_table.fetch(Datadog::Ext::Profiling::Pprof::LABEL_KEY_LOCAL_ROOT_SPAN_ID),
|
|
111
|
+
str: builder.string_table.fetch(root_span_id.to_s)
|
|
92
112
|
)
|
|
93
113
|
|
|
94
114
|
labels << Perftools::Profiles::Label.new(
|
|
@@ -96,7 +116,10 @@ module Datadog
|
|
|
96
116
|
str: builder.string_table.fetch(span_id.to_s)
|
|
97
117
|
)
|
|
98
118
|
|
|
99
|
-
|
|
119
|
+
# Use most up-to-date trace resource, if available.
|
|
120
|
+
# Otherwise, use the trace resource provided.
|
|
121
|
+
trace_resource = @most_recent_trace_samples.fetch(stack_sample.root_span_id, stack_sample).trace_resource
|
|
122
|
+
|
|
100
123
|
if trace_resource && !trace_resource.empty?
|
|
101
124
|
labels << Perftools::Profiles::Label.new(
|
|
102
125
|
key: builder.string_table.fetch(Datadog::Ext::Profiling::Pprof::LABEL_KEY_TRACE_ENDPOINT),
|
|
@@ -109,7 +132,7 @@ module Datadog
|
|
|
109
132
|
end
|
|
110
133
|
|
|
111
134
|
def debug_statistics
|
|
112
|
-
"unique stacks: #{@processed_unique_stacks}, of which had active traces: #{@
|
|
135
|
+
"unique stacks: #{@processed_unique_stacks}, of which had active traces: #{@processed_with_trace}"
|
|
113
136
|
end
|
|
114
137
|
end
|
|
115
138
|
end
|
|
@@ -80,8 +80,8 @@ module Datadog
|
|
|
80
80
|
converters.values.map(&:debug_statistics).join(', ')
|
|
81
81
|
end
|
|
82
82
|
|
|
83
|
-
def to_pprof
|
|
84
|
-
profile = builder.build_profile
|
|
83
|
+
def to_pprof(start:, finish:)
|
|
84
|
+
profile = builder.build_profile(start: start, finish: finish)
|
|
85
85
|
data = builder.encode_profile(profile)
|
|
86
86
|
types = sample_type_mappings.keys
|
|
87
87
|
|
|
@@ -17,6 +17,10 @@ module Datadog
|
|
|
17
17
|
# Profiles with duration less than this will not be reported
|
|
18
18
|
PROFILE_DURATION_THRESHOLD_SECONDS = 1
|
|
19
19
|
|
|
20
|
+
# We sleep for at most this duration seconds before reporting data to avoid multi-process applications all
|
|
21
|
+
# reporting profiles at the exact same time
|
|
22
|
+
DEFAULT_FLUSH_JITTER_MAXIMUM_SECONDS = 3
|
|
23
|
+
|
|
20
24
|
private_constant :DEFAULT_INTERVAL_SECONDS, :MINIMUM_INTERVAL_SECONDS, :PROFILE_DURATION_THRESHOLD_SECONDS
|
|
21
25
|
|
|
22
26
|
attr_reader \
|
|
@@ -62,10 +66,6 @@ module Datadog
|
|
|
62
66
|
end
|
|
63
67
|
end
|
|
64
68
|
|
|
65
|
-
def loop_back_off?
|
|
66
|
-
false
|
|
67
|
-
end
|
|
68
|
-
|
|
69
69
|
def after_fork
|
|
70
70
|
# Clear recorder's buffers by flushing events.
|
|
71
71
|
# Objects from parent process will copy-on-write,
|
|
@@ -110,6 +110,22 @@ module Datadog
|
|
|
110
110
|
return flush
|
|
111
111
|
end
|
|
112
112
|
|
|
113
|
+
# Sleep for a bit to cause misalignment between profilers in multi-process applications
|
|
114
|
+
#
|
|
115
|
+
# When not being run in a loop, it means the scheduler has not been started or was stopped, and thus
|
|
116
|
+
# a) it's being shutting down (and is trying to report the last profile)
|
|
117
|
+
# b) it's being run as a one-shot, usually in a test
|
|
118
|
+
# ...so in those cases we don't sleep
|
|
119
|
+
#
|
|
120
|
+
# During PR review (https://github.com/DataDog/dd-trace-rb/pull/1807) we discussed the possible alternative of
|
|
121
|
+
# just sleeping before starting the scheduler loop. We ended up not going with that option to avoid the first
|
|
122
|
+
# profile containing up to DEFAULT_INTERVAL_SECONDS + DEFAULT_FLUSH_JITTER_MAXIMUM_SECONDS instead of the
|
|
123
|
+
# usual DEFAULT_INTERVAL_SECONDS size.
|
|
124
|
+
if run_loop?
|
|
125
|
+
jitter_seconds = rand * DEFAULT_FLUSH_JITTER_MAXIMUM_SECONDS # floating point number between (0.0...maximum)
|
|
126
|
+
sleep(jitter_seconds)
|
|
127
|
+
end
|
|
128
|
+
|
|
113
129
|
# Send events to each exporter
|
|
114
130
|
if flush.event_count > 0
|
|
115
131
|
exporters.each do |exporter|
|
|
@@ -1,22 +1,20 @@
|
|
|
1
1
|
# typed: false
|
|
2
|
-
require 'ddtrace'
|
|
3
2
|
require 'ddtrace/utils/only_once'
|
|
4
3
|
require 'ddtrace/profiling'
|
|
5
|
-
require 'ddtrace/profiling/ext/cpu'
|
|
6
4
|
require 'ddtrace/profiling/ext/forking'
|
|
7
5
|
|
|
8
6
|
module Datadog
|
|
9
7
|
module Profiling
|
|
10
8
|
module Tasks
|
|
11
|
-
# Takes care of loading our extensions/monkey patches to handle fork() and CPU profiling
|
|
9
|
+
# Takes care of loading our extensions/monkey patches to handle fork() and validating if CPU-time profiling is usable
|
|
12
10
|
class Setup
|
|
13
11
|
ACTIVATE_EXTENSIONS_ONLY_ONCE = Datadog::Utils::OnlyOnce.new
|
|
14
12
|
|
|
15
13
|
def run
|
|
16
14
|
ACTIVATE_EXTENSIONS_ONLY_ONCE.run do
|
|
17
15
|
begin
|
|
16
|
+
check_if_cpu_time_profiling_is_supported
|
|
18
17
|
activate_forking_extensions
|
|
19
|
-
activate_cpu_extensions
|
|
20
18
|
setup_at_fork_hooks
|
|
21
19
|
rescue StandardError, ScriptError => e
|
|
22
20
|
Datadog.logger.warn do
|
|
@@ -40,19 +38,15 @@ module Datadog
|
|
|
40
38
|
end
|
|
41
39
|
end
|
|
42
40
|
|
|
43
|
-
def
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
41
|
+
def check_if_cpu_time_profiling_is_supported
|
|
42
|
+
unsupported = cpu_time_profiling_unsupported_reason
|
|
43
|
+
|
|
44
|
+
if unsupported
|
|
47
45
|
Datadog.logger.info do
|
|
48
46
|
'CPU time profiling skipped because native CPU time is not supported: ' \
|
|
49
|
-
"#{
|
|
47
|
+
"#{unsupported}. Profiles containing 'Wall time' data will still be reported."
|
|
50
48
|
end
|
|
51
49
|
end
|
|
52
|
-
rescue StandardError, ScriptError => e
|
|
53
|
-
Datadog.logger.warn do
|
|
54
|
-
"Profiler CPU profiling extensions unavailable. Cause: #{e.message} Location: #{Array(e.backtrace).first}"
|
|
55
|
-
end
|
|
56
50
|
end
|
|
57
51
|
|
|
58
52
|
def setup_at_fork_hooks
|
|
@@ -76,6 +70,20 @@ module Datadog
|
|
|
76
70
|
end
|
|
77
71
|
end
|
|
78
72
|
end
|
|
73
|
+
|
|
74
|
+
def cpu_time_profiling_unsupported_reason
|
|
75
|
+
# NOTE: Only the first matching reason is returned, so try to keep a nice order on reasons
|
|
76
|
+
|
|
77
|
+
if RUBY_ENGINE == 'jruby'
|
|
78
|
+
'JRuby is not supported'
|
|
79
|
+
elsif RUBY_PLATFORM.include?('darwin')
|
|
80
|
+
'Feature requires Linux; macOS is not supported'
|
|
81
|
+
elsif RUBY_PLATFORM =~ /(mswin|mingw)/
|
|
82
|
+
'Feature requires Linux; Windows is not supported'
|
|
83
|
+
elsif !RUBY_PLATFORM.include?('linux')
|
|
84
|
+
"Feature requires Linux; #{RUBY_PLATFORM} is not supported"
|
|
85
|
+
end
|
|
86
|
+
end
|
|
79
87
|
end
|
|
80
88
|
end
|
|
81
89
|
end
|
|
@@ -6,10 +6,10 @@ require 'ddtrace/ext/http'
|
|
|
6
6
|
module Datadog
|
|
7
7
|
module Profiling
|
|
8
8
|
module TraceIdentifiers
|
|
9
|
-
# Used by Datadog::Profiling::TraceIdentifiers::Helper to get the trace identifiers (
|
|
10
|
-
# given thread, if there is an active trace for that thread in
|
|
9
|
+
# Used by Datadog::Profiling::TraceIdentifiers::Helper to get the trace identifiers (root span id and span id)
|
|
10
|
+
# for a given thread, if there is an active trace for that thread in the supplied tracer object.
|
|
11
11
|
class Ddtrace
|
|
12
|
-
def initialize(tracer:
|
|
12
|
+
def initialize(tracer:)
|
|
13
13
|
@tracer = (tracer if tracer.respond_to?(:call_context))
|
|
14
14
|
end
|
|
15
15
|
|
|
@@ -19,10 +19,13 @@ module Datadog
|
|
|
19
19
|
context = @tracer.call_context(thread)
|
|
20
20
|
return unless context
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
span, root_span = context.current_span_and_root_span
|
|
23
|
+
return unless span && root_span
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
root_span_id = root_span.span_id || 0
|
|
26
|
+
span_id = span.span_id || 0
|
|
27
|
+
|
|
28
|
+
[root_span_id, span_id, maybe_extract_resource(root_span)] if root_span_id != 0 && span_id != 0
|
|
26
29
|
end
|
|
27
30
|
|
|
28
31
|
private
|
|
@@ -31,9 +34,7 @@ module Datadog
|
|
|
31
34
|
# Resources MUST NOT include personal identifiable information (PII); this should not be the case with
|
|
32
35
|
# ddtrace integrations, but worth mentioning just in case :)
|
|
33
36
|
def maybe_extract_resource(root_span)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
root_span.resource_container if root_span.span_type == Datadog::Ext::HTTP::TYPE_INBOUND
|
|
37
|
+
root_span.resource if root_span.span_type == Datadog::Ext::HTTP::TYPE_INBOUND
|
|
37
38
|
end
|
|
38
39
|
end
|
|
39
40
|
end
|
|
@@ -6,7 +6,7 @@ require 'ddtrace/profiling/trace_identifiers/ddtrace'
|
|
|
6
6
|
module Datadog
|
|
7
7
|
module Profiling
|
|
8
8
|
module TraceIdentifiers
|
|
9
|
-
# Helper used to retrieve the trace identifiers (
|
|
9
|
+
# Helper used to retrieve the trace identifiers (root span id and span id) for a given thread,
|
|
10
10
|
# if there is an active trace for that thread for the supported tracing APIs.
|
|
11
11
|
#
|
|
12
12
|
# This data is used to connect profiles to the traces -- samples in a profile will be tagged with this data and
|
|
@@ -20,21 +20,21 @@ module Datadog
|
|
|
20
20
|
def initialize(
|
|
21
21
|
tracer:,
|
|
22
22
|
# If this is disabled, the helper will strip the optional trace_resource_container even if provided by the api
|
|
23
|
-
|
|
23
|
+
endpoint_collection_enabled:,
|
|
24
24
|
supported_apis: DEFAULT_SUPPORTED_APIS.map { |api| api.new(tracer: tracer) }
|
|
25
25
|
)
|
|
26
|
-
@
|
|
26
|
+
@endpoint_collection_enabled = endpoint_collection_enabled
|
|
27
27
|
@supported_apis = supported_apis
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
# Expected output of the #trace_identifiers_for
|
|
31
|
-
# duck type is [
|
|
31
|
+
# duck type is [root_span_id, span_id, (optional trace_resource_container)]
|
|
32
32
|
def trace_identifiers_for(thread)
|
|
33
33
|
@supported_apis.each do |api|
|
|
34
34
|
trace_identifiers = api.trace_identifiers_for(thread)
|
|
35
35
|
|
|
36
36
|
if trace_identifiers
|
|
37
|
-
return @
|
|
37
|
+
return @endpoint_collection_enabled ? trace_identifiers : trace_identifiers[0..1]
|
|
38
38
|
end
|
|
39
39
|
end
|
|
40
40
|
|
|
@@ -41,11 +41,10 @@ module Datadog
|
|
|
41
41
|
|
|
42
42
|
def build_form(env)
|
|
43
43
|
flush = env.request.parcel.data
|
|
44
|
-
pprof_file
|
|
44
|
+
pprof_file = build_pprof(flush)
|
|
45
45
|
|
|
46
46
|
form = {
|
|
47
|
-
|
|
48
|
-
FORM_FIELD_RUNTIME_ID => flush.runtime_id,
|
|
47
|
+
FORM_FIELD_INTAKE_VERSION => '3', # Aka 1.3 intake format
|
|
49
48
|
FORM_FIELD_RECORDING_START => flush.start.utc.iso8601,
|
|
50
49
|
FORM_FIELD_RECORDING_END => flush.finish.utc.iso8601,
|
|
51
50
|
FORM_FIELD_TAGS => [
|
|
@@ -64,14 +63,10 @@ module Datadog
|
|
|
64
63
|
.reject { |tag_key| TAGS_TO_IGNORE_IN_TAGS_HASH.include?(tag_key) }
|
|
65
64
|
.map { |tag_key, tag_value| "#{tag_key}:#{tag_value}" }
|
|
66
65
|
],
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
FORM_FIELD_FORMAT => FORM_FIELD_FORMAT_PPROF
|
|
66
|
+
FORM_FIELD_PPROF_DATA => pprof_file,
|
|
67
|
+
FORM_FIELD_FAMILY => flush.language,
|
|
70
68
|
}
|
|
71
69
|
|
|
72
|
-
# Add types
|
|
73
|
-
form[FORM_FIELD_TYPES] = types.join(',')
|
|
74
|
-
|
|
75
70
|
# Optional fields
|
|
76
71
|
form[FORM_FIELD_TAGS] << "#{FORM_FIELD_TAG_SERVICE}:#{flush.service}" unless flush.service.nil?
|
|
77
72
|
form[FORM_FIELD_TAGS] << "#{FORM_FIELD_TAG_ENV}:#{flush.env}" unless flush.env.nil?
|
|
@@ -83,15 +78,13 @@ module Datadog
|
|
|
83
78
|
def build_pprof(flush)
|
|
84
79
|
pprof = encoder.encode(flush)
|
|
85
80
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
StringIO.new(
|
|
81
|
+
gzipped_pprof_data = Datadog::Utils::Compression.gzip(pprof.data)
|
|
82
|
+
|
|
83
|
+
Datadog::Vendor::Multipart::Post::UploadIO.new(
|
|
84
|
+
StringIO.new(gzipped_pprof_data),
|
|
90
85
|
HEADER_CONTENT_TYPE_OCTET_STREAM,
|
|
91
86
|
PPROF_DEFAULT_FILENAME
|
|
92
87
|
)
|
|
93
|
-
|
|
94
|
-
[pprof_file, [FORM_FIELD_TYPES_AUTO]]
|
|
95
88
|
end
|
|
96
89
|
end
|
|
97
90
|
end
|
|
@@ -64,22 +64,10 @@ module Datadog
|
|
|
64
64
|
end
|
|
65
65
|
end
|
|
66
66
|
|
|
67
|
-
private_class_method def self.default_adapter
|
|
68
|
-
:net_http
|
|
69
|
-
end
|
|
70
|
-
|
|
71
67
|
private_class_method def self.configure_for_agent(transport, profiling_upload_timeout_seconds:, agent_settings:)
|
|
72
68
|
apis = API.agent_defaults
|
|
73
69
|
|
|
74
|
-
transport.adapter(
|
|
75
|
-
default_adapter,
|
|
76
|
-
agent_settings.hostname,
|
|
77
|
-
agent_settings.port,
|
|
78
|
-
# We explictly use profiling_upload_timeout_seconds instead of agent_settings.timeout because profile
|
|
79
|
-
# uploads are bigger and thus we employ a separate configuration.
|
|
80
|
-
timeout: profiling_upload_timeout_seconds,
|
|
81
|
-
ssl: agent_settings.ssl
|
|
82
|
-
)
|
|
70
|
+
transport.adapter(agent_settings.merge(timeout_seconds: profiling_upload_timeout_seconds))
|
|
83
71
|
transport.api(API::V1, apis[API::V1], default: true)
|
|
84
72
|
|
|
85
73
|
# NOTE: This proc, when it exists, usually overrides the transport specified above
|
|
@@ -96,7 +84,7 @@ module Datadog
|
|
|
96
84
|
port = site_uri.port
|
|
97
85
|
|
|
98
86
|
transport.adapter(
|
|
99
|
-
|
|
87
|
+
Datadog::Ext::Transport::HTTP::ADAPTER,
|
|
100
88
|
hostname,
|
|
101
89
|
port,
|
|
102
90
|
timeout: profiling_upload_timeout_seconds,
|
|
@@ -111,9 +99,12 @@ module Datadog
|
|
|
111
99
|
end
|
|
112
100
|
|
|
113
101
|
# Add adapters to registry
|
|
114
|
-
Datadog::Transport::HTTP::Builder::REGISTRY.set(Datadog::Transport::HTTP::Adapters::Net,
|
|
115
|
-
|
|
116
|
-
Datadog::Transport::HTTP::Builder::REGISTRY.set(Datadog::Transport::HTTP::Adapters::
|
|
102
|
+
Datadog::Transport::HTTP::Builder::REGISTRY.set(Datadog::Transport::HTTP::Adapters::Net,
|
|
103
|
+
Datadog::Ext::Transport::HTTP::ADAPTER)
|
|
104
|
+
Datadog::Transport::HTTP::Builder::REGISTRY.set(Datadog::Transport::HTTP::Adapters::Test,
|
|
105
|
+
Datadog::Ext::Transport::Test::ADAPTER)
|
|
106
|
+
Datadog::Transport::HTTP::Builder::REGISTRY.set(Datadog::Transport::HTTP::Adapters::UnixSocket,
|
|
107
|
+
Datadog::Ext::Transport::UnixSocket::ADAPTER)
|
|
117
108
|
end
|
|
118
109
|
end
|
|
119
110
|
end
|
data/lib/ddtrace/profiling.rb
CHANGED
|
@@ -128,9 +128,7 @@ module Datadog
|
|
|
128
128
|
private_class_method def self.load_profiling
|
|
129
129
|
return false unless supported?
|
|
130
130
|
|
|
131
|
-
require 'ddtrace/profiling/ext/cpu'
|
|
132
131
|
require 'ddtrace/profiling/ext/forking'
|
|
133
|
-
|
|
134
132
|
require 'ddtrace/profiling/collectors/stack'
|
|
135
133
|
require 'ddtrace/profiling/exporter'
|
|
136
134
|
require 'ddtrace/profiling/recorder'
|
|
@@ -7,6 +7,7 @@ require 'datadog/core/environment/class_count'
|
|
|
7
7
|
require 'datadog/core/environment/gc'
|
|
8
8
|
require 'datadog/core/environment/identity'
|
|
9
9
|
require 'datadog/core/environment/thread_count'
|
|
10
|
+
require 'datadog/core/environment/vm_cache'
|
|
10
11
|
|
|
11
12
|
module Datadog
|
|
12
13
|
module Runtime
|
|
@@ -58,12 +59,25 @@ module Datadog
|
|
|
58
59
|
gauge(Ext::Runtime::Metrics::METRIC_CLASS_COUNT, Core::Environment::ClassCount.value)
|
|
59
60
|
end
|
|
60
61
|
end
|
|
62
|
+
|
|
61
63
|
try_flush do
|
|
62
64
|
if Core::Environment::ThreadCount.available?
|
|
63
65
|
gauge(Ext::Runtime::Metrics::METRIC_THREAD_COUNT, Core::Environment::ThreadCount.value)
|
|
64
66
|
end
|
|
65
67
|
end
|
|
68
|
+
|
|
66
69
|
try_flush { gc_metrics.each { |metric, value| gauge(metric, value) } if Core::Environment::GC.available? }
|
|
70
|
+
|
|
71
|
+
try_flush do
|
|
72
|
+
if Core::Environment::VMCache.available?
|
|
73
|
+
gauge(Ext::Runtime::Metrics::METRIC_GLOBAL_CONSTANT_STATE, Core::Environment::VMCache.global_constant_state)
|
|
74
|
+
|
|
75
|
+
# global_method_state is not available since Ruby >= 3.0,
|
|
76
|
+
# as method caching was moved to a per-class basis.
|
|
77
|
+
global_method_state = Core::Environment::VMCache.global_method_state
|
|
78
|
+
gauge(Ext::Runtime::Metrics::METRIC_GLOBAL_METHOD_STATE, global_method_state) if global_method_state
|
|
79
|
+
end
|
|
80
|
+
end
|
|
67
81
|
end
|
|
68
82
|
|
|
69
83
|
def gc_metrics
|
data/lib/ddtrace/sampler.rb
CHANGED
|
@@ -194,6 +194,12 @@ module Datadog
|
|
|
194
194
|
class PrioritySampler
|
|
195
195
|
extend Forwardable
|
|
196
196
|
|
|
197
|
+
# NOTE: We do not advise using a pre-sampler. It can save resources,
|
|
198
|
+
# but pre-sampling at rates < 100% may result in partial traces, unless
|
|
199
|
+
# the pre-sampler knows exactly how to drop a span without dropping its ancestors.
|
|
200
|
+
#
|
|
201
|
+
# Additionally, as service metrics are calculated in the Datadog Agent,
|
|
202
|
+
# the service's throughput will be underestimated.
|
|
197
203
|
attr_reader :pre_sampler, :priority_sampler
|
|
198
204
|
|
|
199
205
|
SAMPLE_RATE_METRIC_KEY = '_sample_rate'.freeze
|
|
@@ -209,17 +215,21 @@ module Datadog
|
|
|
209
215
|
|
|
210
216
|
def sample!(span)
|
|
211
217
|
# If pre-sampling is configured, do it first. (By default, this will sample at 100%.)
|
|
212
|
-
# NOTE: Pre-sampling at rates < 100% may result in partial traces; not recommended.
|
|
213
218
|
span.sampled = pre_sample?(span) ? @pre_sampler.sample!(span) : true
|
|
214
219
|
|
|
215
220
|
if span.sampled
|
|
216
|
-
# If priority sampling has already been applied upstream, use that
|
|
217
|
-
|
|
218
|
-
# Roll the dice and determine whether how we set the priority.
|
|
219
|
-
priority = priority_sample!(span) ? Datadog::Ext::Priority::AUTO_KEEP : Datadog::Ext::Priority::AUTO_REJECT
|
|
221
|
+
# If priority sampling has already been applied upstream, use that value.
|
|
222
|
+
return true if priority_assigned?(span)
|
|
220
223
|
|
|
221
|
-
|
|
222
|
-
|
|
224
|
+
# Check with post sampler how we set the priority.
|
|
225
|
+
sample = priority_sample!(span)
|
|
226
|
+
|
|
227
|
+
# Check if post sampler has already assigned a priority.
|
|
228
|
+
return true if priority_assigned?(span)
|
|
229
|
+
|
|
230
|
+
# If not, use agent priority values.
|
|
231
|
+
priority = sample ? Datadog::Ext::Priority::AUTO_KEEP : Datadog::Ext::Priority::AUTO_REJECT
|
|
232
|
+
assign_priority!(span, priority)
|
|
223
233
|
else
|
|
224
234
|
# If discarded by pre-sampling, set "reject" priority, so other
|
|
225
235
|
# services for the same trace don't sample needlessly.
|
|
@@ -244,7 +254,7 @@ module Datadog
|
|
|
244
254
|
end
|
|
245
255
|
end
|
|
246
256
|
|
|
247
|
-
def
|
|
257
|
+
def priority_assigned?(span)
|
|
248
258
|
span.context && !span.context.sampling_priority.nil?
|
|
249
259
|
end
|
|
250
260
|
|
|
@@ -97,11 +97,13 @@ module Datadog
|
|
|
97
97
|
sampled = rule.sample?(span)
|
|
98
98
|
sample_rate = rule.sample_rate(span)
|
|
99
99
|
|
|
100
|
+
set_priority(span, sampled)
|
|
100
101
|
set_rule_metrics(span, sample_rate)
|
|
101
102
|
|
|
102
103
|
return false unless sampled
|
|
103
104
|
|
|
104
|
-
rate_limiter.allow?(1).tap do
|
|
105
|
+
rate_limiter.allow?(1).tap do |allowed|
|
|
106
|
+
set_priority(span, allowed)
|
|
105
107
|
set_limiter_metrics(span, rate_limiter.effective_rate)
|
|
106
108
|
end
|
|
107
109
|
rescue StandardError => e
|
|
@@ -109,6 +111,16 @@ module Datadog
|
|
|
109
111
|
yield(span)
|
|
110
112
|
end
|
|
111
113
|
|
|
114
|
+
# Span priority should only be set when the {RuleSampler}
|
|
115
|
+
# was responsible for the sampling decision.
|
|
116
|
+
def set_priority(span, sampled)
|
|
117
|
+
if sampled
|
|
118
|
+
ForcedTracing.keep(span)
|
|
119
|
+
else
|
|
120
|
+
ForcedTracing.drop(span)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
112
124
|
def set_rule_metrics(span, sample_rate)
|
|
113
125
|
span.set_metric(Ext::Sampling::RULE_SAMPLE_RATE, sample_rate)
|
|
114
126
|
end
|
data/lib/ddtrace/span.rb
CHANGED
|
@@ -53,12 +53,7 @@ module Datadog
|
|
|
53
53
|
Ext::NET::TAG_HOSTNAME => true
|
|
54
54
|
}.freeze
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
# The profiler keeps a reference to these objects while sampling so it can extract the latest resource after the
|
|
58
|
-
# fact, as some integrations only set the correct name at the end of the span.
|
|
59
|
-
ResourceContainer = Struct.new(:latest)
|
|
60
|
-
|
|
61
|
-
attr_accessor :name, :service, :span_type,
|
|
56
|
+
attr_accessor :name, :service, :resource, :span_type,
|
|
62
57
|
:span_id, :trace_id, :parent_id,
|
|
63
58
|
:status, :sampled,
|
|
64
59
|
:tracer, :context
|
|
@@ -71,7 +66,8 @@ module Datadog
|
|
|
71
66
|
# and then <tt>finish()</tt> once the tracer operation is over.
|
|
72
67
|
#
|
|
73
68
|
# * +service+: the service name for this span
|
|
74
|
-
# * +resource+: the resource this span refers, or +name+ if it's missing
|
|
69
|
+
# * +resource+: the resource this span refers, or +name+ if it's missing.
|
|
70
|
+
# +nil+ can be used as a placeholder, when the resource value is not yet known at +#initialize+ time.
|
|
75
71
|
# * +span_type+: the type of the span (such as +http+, +db+ and so on)
|
|
76
72
|
# * +parent_id+: the identifier of the parent span
|
|
77
73
|
# * +trace_id+: the identifier of the root span for this trace
|
|
@@ -81,7 +77,7 @@ module Datadog
|
|
|
81
77
|
|
|
82
78
|
@name = name
|
|
83
79
|
@service = options.fetch(:service, nil)
|
|
84
|
-
@
|
|
80
|
+
@resource = options.fetch(:resource, name)
|
|
85
81
|
@span_type = options.fetch(:span_type, nil)
|
|
86
82
|
|
|
87
83
|
@span_id = Datadog::Utils.next_id
|
|
@@ -289,7 +285,7 @@ module Datadog
|
|
|
289
285
|
trace_id: @trace_id,
|
|
290
286
|
name: @name,
|
|
291
287
|
service: @service,
|
|
292
|
-
resource: resource,
|
|
288
|
+
resource: @resource,
|
|
293
289
|
type: @span_type,
|
|
294
290
|
meta: @meta,
|
|
295
291
|
metrics: @metrics,
|
|
@@ -343,7 +339,7 @@ module Datadog
|
|
|
343
339
|
packer.write('service')
|
|
344
340
|
packer.write(@service)
|
|
345
341
|
packer.write('resource')
|
|
346
|
-
packer.write(resource)
|
|
342
|
+
packer.write(@resource)
|
|
347
343
|
packer.write('type')
|
|
348
344
|
packer.write(@span_type)
|
|
349
345
|
packer.write('meta')
|
|
@@ -375,7 +371,7 @@ module Datadog
|
|
|
375
371
|
q.text "Trace ID: #{@trace_id}\n"
|
|
376
372
|
q.text "Type: #{@span_type}\n"
|
|
377
373
|
q.text "Service: #{@service}\n"
|
|
378
|
-
q.text "Resource: #{resource}\n"
|
|
374
|
+
q.text "Resource: #{@resource}\n"
|
|
379
375
|
q.text "Error: #{@status}\n"
|
|
380
376
|
q.text "Start: #{start_time}\n"
|
|
381
377
|
q.text "End: #{end_time}\n"
|
|
@@ -414,14 +410,6 @@ module Datadog
|
|
|
414
410
|
end
|
|
415
411
|
end
|
|
416
412
|
|
|
417
|
-
def resource
|
|
418
|
-
@resource_container.latest
|
|
419
|
-
end
|
|
420
|
-
|
|
421
|
-
def resource=(resource)
|
|
422
|
-
@resource_container.latest = resource
|
|
423
|
-
end
|
|
424
|
-
|
|
425
413
|
private
|
|
426
414
|
|
|
427
415
|
def duration_marker
|
data/lib/ddtrace/tracer.rb
CHANGED
|
@@ -200,7 +200,7 @@ module Datadog
|
|
|
200
200
|
if parent.nil?
|
|
201
201
|
# root span
|
|
202
202
|
@sampler.sample!(span)
|
|
203
|
-
span.set_tag(
|
|
203
|
+
span.set_tag(Datadog::Ext::Runtime::TAG_PID, Process.pid)
|
|
204
204
|
span.set_tag(Datadog::Ext::Runtime::TAG_ID, Datadog::Core::Environment::Identity.id)
|
|
205
205
|
|
|
206
206
|
if ctx && ctx.trace_id
|
|
@@ -16,13 +16,23 @@ module Datadog
|
|
|
16
16
|
|
|
17
17
|
DEFAULT_TIMEOUT = 30
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
@
|
|
19
|
+
# @deprecated Positional parameters are deprecated. Use named parameters instead.
|
|
20
|
+
def initialize(hostname = nil, port = nil, **options)
|
|
21
|
+
@hostname = hostname || options.fetch(:hostname)
|
|
22
|
+
@port = port || options.fetch(:port)
|
|
22
23
|
@timeout = options[:timeout] || DEFAULT_TIMEOUT
|
|
23
24
|
@ssl = options.key?(:ssl) ? options[:ssl] == true : false
|
|
24
25
|
end
|
|
25
26
|
|
|
27
|
+
def self.build(agent_settings)
|
|
28
|
+
new(
|
|
29
|
+
hostname: agent_settings.hostname,
|
|
30
|
+
port: agent_settings.port,
|
|
31
|
+
timeout: agent_settings.timeout_seconds,
|
|
32
|
+
ssl: agent_settings.ssl
|
|
33
|
+
)
|
|
34
|
+
end
|
|
35
|
+
|
|
26
36
|
def open(&block)
|
|
27
37
|
# DEV Initializing +Net::HTTP+ directly help us avoid expensive
|
|
28
38
|
# options processing done in +Net::HTTP.start+:
|
|
@@ -11,8 +11,10 @@ module Datadog
|
|
|
11
11
|
:buffer,
|
|
12
12
|
:status
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
# @param buffer [Array] an optional array that will capture all spans sent to this adapter, defaults to +nil+
|
|
15
|
+
# @deprecated Positional parameters are deprecated. Use named parameters instead.
|
|
16
|
+
def initialize(buffer = nil, **options)
|
|
17
|
+
@buffer = buffer || options[:buffer]
|
|
16
18
|
@mutex = Mutex.new
|
|
17
19
|
@status = 200
|
|
18
20
|
end
|