ddtrace 1.22.0 → 1.23.3
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 +46 -2
- 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/extconf.rb +7 -5
- 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/native_extension_helpers.rb +46 -0
- data/ext/datadog_profiling_native_extension/ruby_helpers.h +3 -0
- 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/components.rb +4 -3
- data/lib/datadog/core/configuration.rb +3 -17
- data/lib/datadog/core/telemetry/component.rb +66 -0
- data/lib/datadog/core/telemetry/emitter.rb +1 -1
- data/lib/datadog/core/telemetry/event.rb +1 -0
- data/lib/datadog/core/telemetry/http/adapters/net.rb +1 -1
- data/lib/datadog/core/telemetry/http/response.rb +4 -0
- data/lib/datadog/core/telemetry/worker.rb +158 -0
- data/lib/datadog/core/utils/only_once_successful.rb +76 -0
- data/lib/datadog/profiling/exporter.rb +6 -3
- data/lib/datadog/profiling/stack_recorder.rb +6 -2
- data/lib/ddtrace/version.rb +2 -2
- metadata +18 -7
- data/lib/datadog/core/telemetry/client.rb +0 -95
- data/lib/datadog/core/telemetry/heartbeat.rb +0 -33
@@ -0,0 +1,158 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'event'
|
4
|
+
|
5
|
+
require_relative '../utils/only_once_successful'
|
6
|
+
require_relative '../workers/polling'
|
7
|
+
require_relative '../workers/queue'
|
8
|
+
|
9
|
+
module Datadog
|
10
|
+
module Core
|
11
|
+
module Telemetry
|
12
|
+
# Accumulates events and sends them to the API at a regular interval, including heartbeat event.
|
13
|
+
class Worker
|
14
|
+
include Core::Workers::Queue
|
15
|
+
include Core::Workers::Polling
|
16
|
+
|
17
|
+
DEFAULT_BUFFER_MAX_SIZE = 1000
|
18
|
+
APP_STARTED_EVENT_RETRIES = 10
|
19
|
+
|
20
|
+
TELEMETRY_STARTED_ONCE = Utils::OnlyOnceSuccessful.new(APP_STARTED_EVENT_RETRIES)
|
21
|
+
|
22
|
+
def initialize(
|
23
|
+
heartbeat_interval_seconds:,
|
24
|
+
emitter:,
|
25
|
+
dependency_collection:,
|
26
|
+
enabled: true,
|
27
|
+
shutdown_timeout: Workers::Polling::DEFAULT_SHUTDOWN_TIMEOUT,
|
28
|
+
buffer_size: DEFAULT_BUFFER_MAX_SIZE
|
29
|
+
)
|
30
|
+
@emitter = emitter
|
31
|
+
@dependency_collection = dependency_collection
|
32
|
+
|
33
|
+
# Workers::Polling settings
|
34
|
+
self.enabled = enabled
|
35
|
+
# Workers::IntervalLoop settings
|
36
|
+
self.loop_base_interval = heartbeat_interval_seconds
|
37
|
+
self.fork_policy = Core::Workers::Async::Thread::FORK_POLICY_STOP
|
38
|
+
|
39
|
+
@shutdown_timeout = shutdown_timeout
|
40
|
+
@buffer_size = buffer_size
|
41
|
+
|
42
|
+
self.buffer = buffer_klass.new(@buffer_size)
|
43
|
+
end
|
44
|
+
|
45
|
+
def start
|
46
|
+
return if !enabled? || forked?
|
47
|
+
|
48
|
+
# starts async worker
|
49
|
+
perform
|
50
|
+
end
|
51
|
+
|
52
|
+
def stop(force_stop = false, timeout = @shutdown_timeout)
|
53
|
+
buffer.close if running?
|
54
|
+
|
55
|
+
super
|
56
|
+
end
|
57
|
+
|
58
|
+
def enqueue(event)
|
59
|
+
return if !enabled? || forked?
|
60
|
+
|
61
|
+
buffer.push(event)
|
62
|
+
end
|
63
|
+
|
64
|
+
def sent_started_event?
|
65
|
+
TELEMETRY_STARTED_ONCE.success?
|
66
|
+
end
|
67
|
+
|
68
|
+
def failed_to_start?
|
69
|
+
TELEMETRY_STARTED_ONCE.failed?
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def perform(*events)
|
75
|
+
return if !enabled? || forked?
|
76
|
+
|
77
|
+
started! unless sent_started_event?
|
78
|
+
|
79
|
+
heartbeat!
|
80
|
+
|
81
|
+
flush_events(events)
|
82
|
+
end
|
83
|
+
|
84
|
+
def flush_events(events)
|
85
|
+
return if events.nil?
|
86
|
+
return if !enabled? || !sent_started_event?
|
87
|
+
|
88
|
+
Datadog.logger.debug { "Sending #{events.count} telemetry events" }
|
89
|
+
events.each do |event|
|
90
|
+
send_event(event)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def heartbeat!
|
95
|
+
return if !enabled? || !sent_started_event?
|
96
|
+
|
97
|
+
send_event(Event::AppHeartbeat.new)
|
98
|
+
end
|
99
|
+
|
100
|
+
def started!
|
101
|
+
return unless enabled?
|
102
|
+
|
103
|
+
if failed_to_start?
|
104
|
+
Datadog.logger.debug('Telemetry app-started event exhausted retries, disabling telemetry worker')
|
105
|
+
self.enabled = false
|
106
|
+
return
|
107
|
+
end
|
108
|
+
|
109
|
+
TELEMETRY_STARTED_ONCE.run do
|
110
|
+
res = send_event(Event::AppStarted.new)
|
111
|
+
|
112
|
+
if res.ok?
|
113
|
+
Datadog.logger.debug('Telemetry app-started event is successfully sent')
|
114
|
+
|
115
|
+
send_event(Event::AppDependenciesLoaded.new) if @dependency_collection
|
116
|
+
|
117
|
+
true
|
118
|
+
else
|
119
|
+
Datadog.logger.debug('Error sending telemetry app-started event, retry after heartbeat interval...')
|
120
|
+
false
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def send_event(event)
|
126
|
+
res = @emitter.request(event)
|
127
|
+
|
128
|
+
disable_on_not_found!(res)
|
129
|
+
|
130
|
+
res
|
131
|
+
end
|
132
|
+
|
133
|
+
def dequeue
|
134
|
+
buffer.pop
|
135
|
+
end
|
136
|
+
|
137
|
+
def work_pending?
|
138
|
+
run_loop? || !buffer.empty?
|
139
|
+
end
|
140
|
+
|
141
|
+
def buffer_klass
|
142
|
+
if Core::Environment::Ext::RUBY_ENGINE == 'ruby'
|
143
|
+
Core::Buffer::CRuby
|
144
|
+
else
|
145
|
+
Core::Buffer::ThreadSafe
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def disable_on_not_found!(response)
|
150
|
+
return unless response.not_found?
|
151
|
+
|
152
|
+
Datadog.logger.debug('Agent does not support telemetry; disabling future telemetry events.')
|
153
|
+
self.enabled = false
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'only_once'
|
4
|
+
|
5
|
+
module Datadog
|
6
|
+
module Core
|
7
|
+
module Utils
|
8
|
+
# Helper class to execute something with only one success.
|
9
|
+
#
|
10
|
+
# This is useful for cases where we want to ensure that a block of code is only executed once, and only if it
|
11
|
+
# succeeds. One such example is sending app-started telemetry event.
|
12
|
+
#
|
13
|
+
# Successful execution is determined by the return value of the block: any truthy value is considered success.
|
14
|
+
#
|
15
|
+
# Thread-safe when used correctly (e.g. be careful of races when lazily initializing instances of this class).
|
16
|
+
#
|
17
|
+
# Note: In its current state, this class is not Ractor-safe.
|
18
|
+
# In https://github.com/DataDog/dd-trace-rb/pull/1398#issuecomment-797378810 we have a discussion of alternatives,
|
19
|
+
# including an alternative implementation that is Ractor-safe once spent.
|
20
|
+
class OnlyOnceSuccessful < OnlyOnce
|
21
|
+
def initialize(limit = 0)
|
22
|
+
super()
|
23
|
+
|
24
|
+
@limit = limit
|
25
|
+
@failed = false
|
26
|
+
@retries = 0
|
27
|
+
end
|
28
|
+
|
29
|
+
def run
|
30
|
+
@mutex.synchronize do
|
31
|
+
return if @ran_once
|
32
|
+
|
33
|
+
result = yield
|
34
|
+
@ran_once = !!result
|
35
|
+
|
36
|
+
if !@ran_once && limited?
|
37
|
+
@retries += 1
|
38
|
+
check_limit!
|
39
|
+
end
|
40
|
+
|
41
|
+
result
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def success?
|
46
|
+
@mutex.synchronize { @ran_once && !@failed }
|
47
|
+
end
|
48
|
+
|
49
|
+
def failed?
|
50
|
+
@mutex.synchronize { @ran_once && @failed }
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def check_limit!
|
56
|
+
if @retries >= @limit
|
57
|
+
@failed = true
|
58
|
+
@ran_once = true
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def limited?
|
63
|
+
!@limit.nil? && @limit > 0
|
64
|
+
end
|
65
|
+
|
66
|
+
def reset_ran_once_state_for_tests
|
67
|
+
@mutex.synchronize do
|
68
|
+
@ran_once = false
|
69
|
+
@failed = false
|
70
|
+
@retries = 0
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -52,10 +52,11 @@ module Datadog
|
|
52
52
|
|
53
53
|
def flush
|
54
54
|
worker_stats = @worker.stats_and_reset_not_thread_safe
|
55
|
-
|
56
|
-
|
55
|
+
serialization_result = pprof_recorder.serialize
|
56
|
+
return if serialization_result.nil?
|
57
57
|
|
58
|
-
|
58
|
+
start, finish, compressed_pprof, profile_stats = serialization_result
|
59
|
+
@last_flush_finish_at = finish
|
59
60
|
|
60
61
|
if duration_below_threshold?(start, finish)
|
61
62
|
Datadog.logger.debug('Skipped exporting profiling events as profile duration is below minimum')
|
@@ -75,6 +76,8 @@ module Datadog
|
|
75
76
|
internal_metadata: internal_metadata.merge(
|
76
77
|
{
|
77
78
|
worker_stats: worker_stats,
|
79
|
+
profile_stats: profile_stats,
|
80
|
+
recorder_stats: pprof_recorder.stats,
|
78
81
|
gc: GC.stat,
|
79
82
|
}
|
80
83
|
),
|
@@ -31,11 +31,11 @@ module Datadog
|
|
31
31
|
status, result = @no_concurrent_synchronize_mutex.synchronize { self.class._native_serialize(self) }
|
32
32
|
|
33
33
|
if status == :ok
|
34
|
-
start, finish, encoded_pprof = result
|
34
|
+
start, finish, encoded_pprof, profile_stats = result
|
35
35
|
|
36
36
|
Datadog.logger.debug { "Encoded profile covering #{start.iso8601} to #{finish.iso8601}" }
|
37
37
|
|
38
|
-
[start, finish, encoded_pprof]
|
38
|
+
[start, finish, encoded_pprof, profile_stats]
|
39
39
|
else
|
40
40
|
error_message = result
|
41
41
|
|
@@ -62,6 +62,10 @@ module Datadog
|
|
62
62
|
def reset_after_fork
|
63
63
|
self.class._native_reset_after_fork(self)
|
64
64
|
end
|
65
|
+
|
66
|
+
def stats
|
67
|
+
self.class._native_stats(self)
|
68
|
+
end
|
65
69
|
end
|
66
70
|
end
|
67
71
|
end
|
data/lib/ddtrace/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ddtrace
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.23.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Datadog, Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-07-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: msgpack
|
@@ -81,6 +81,7 @@ dependencies:
|
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: 0.8.1
|
83
83
|
description: |
|
84
|
+
NOTICE: This gem has been renamed to `datadog` since 2.0.0. Please use `datadog` instead of `ddtrace`.
|
84
85
|
ddtrace is Datadog's tracing client for Ruby. It is used to trace requests
|
85
86
|
as they flow across web servers, databases and microservices so that developers
|
86
87
|
have great visiblity into bottlenecks and troublesome requests.
|
@@ -295,17 +296,17 @@ files:
|
|
295
296
|
- lib/datadog/core/remote/worker.rb
|
296
297
|
- lib/datadog/core/runtime/ext.rb
|
297
298
|
- lib/datadog/core/runtime/metrics.rb
|
298
|
-
- lib/datadog/core/telemetry/
|
299
|
+
- lib/datadog/core/telemetry/component.rb
|
299
300
|
- lib/datadog/core/telemetry/emitter.rb
|
300
301
|
- lib/datadog/core/telemetry/event.rb
|
301
302
|
- lib/datadog/core/telemetry/ext.rb
|
302
|
-
- lib/datadog/core/telemetry/heartbeat.rb
|
303
303
|
- lib/datadog/core/telemetry/http/adapters/net.rb
|
304
304
|
- lib/datadog/core/telemetry/http/env.rb
|
305
305
|
- lib/datadog/core/telemetry/http/ext.rb
|
306
306
|
- lib/datadog/core/telemetry/http/response.rb
|
307
307
|
- lib/datadog/core/telemetry/http/transport.rb
|
308
308
|
- lib/datadog/core/telemetry/request.rb
|
309
|
+
- lib/datadog/core/telemetry/worker.rb
|
309
310
|
- lib/datadog/core/transport/ext.rb
|
310
311
|
- lib/datadog/core/transport/http/adapters/net.rb
|
311
312
|
- lib/datadog/core/transport/http/adapters/registry.rb
|
@@ -326,6 +327,7 @@ files:
|
|
326
327
|
- lib/datadog/core/utils/hash.rb
|
327
328
|
- lib/datadog/core/utils/network.rb
|
328
329
|
- lib/datadog/core/utils/only_once.rb
|
330
|
+
- lib/datadog/core/utils/only_once_successful.rb
|
329
331
|
- lib/datadog/core/utils/safe_dup.rb
|
330
332
|
- lib/datadog/core/utils/sequence.rb
|
331
333
|
- lib/datadog/core/utils/time.rb
|
@@ -876,10 +878,19 @@ files:
|
|
876
878
|
homepage: https://github.com/DataDog/dd-trace-rb
|
877
879
|
licenses:
|
878
880
|
- BSD-3-Clause
|
881
|
+
- Apache-2.0
|
879
882
|
metadata:
|
880
883
|
allowed_push_host: https://rubygems.org
|
881
|
-
changelog_uri: https://github.com/DataDog/dd-trace-rb/blob/
|
882
|
-
|
884
|
+
changelog_uri: https://github.com/DataDog/dd-trace-rb/blob/v1.23.3/CHANGELOG.md
|
885
|
+
source_code_uri: https://github.com/DataDog/dd-trace-rb/tree/v1.23.3
|
886
|
+
post_install_message: |2
|
887
|
+
Thank you for installing ddtrace. We have released our next major version!
|
888
|
+
|
889
|
+
As of version 2, `ddtrace` gem has been renamed to `datadog`.
|
890
|
+
The 1.x series will now only receive maintenance updates for security and critical bug fixes.
|
891
|
+
|
892
|
+
To upgrade, please replace gem `ddtrace` with gem `datadog`.
|
893
|
+
For detailed instructions on migration, see: https://dtdg.co/ruby-v2-upgrade
|
883
894
|
rdoc_options: []
|
884
895
|
require_paths:
|
885
896
|
- lib
|
@@ -897,7 +908,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
897
908
|
- !ruby/object:Gem::Version
|
898
909
|
version: 2.0.0
|
899
910
|
requirements: []
|
900
|
-
rubygems_version: 3.
|
911
|
+
rubygems_version: 3.4.21
|
901
912
|
signing_key:
|
902
913
|
specification_version: 4
|
903
914
|
summary: Datadog tracing code for your Ruby applications
|
@@ -1,95 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'emitter'
|
4
|
-
require_relative 'event'
|
5
|
-
require_relative 'heartbeat'
|
6
|
-
require_relative '../utils/forking'
|
7
|
-
|
8
|
-
module Datadog
|
9
|
-
module Core
|
10
|
-
module Telemetry
|
11
|
-
# Telemetry entrypoint, coordinates sending telemetry events at various points in app lifecycle.
|
12
|
-
class Client
|
13
|
-
attr_reader \
|
14
|
-
:enabled,
|
15
|
-
:unsupported
|
16
|
-
|
17
|
-
include Core::Utils::Forking
|
18
|
-
|
19
|
-
# @param enabled [Boolean] Determines whether telemetry events should be sent to the API
|
20
|
-
# @param heartbeat_interval_seconds [Float] How frequently heartbeats will be reported, in seconds.
|
21
|
-
# @param [Boolean] dependency_collection Whether to send the `app-dependencies-loaded` event
|
22
|
-
def initialize(heartbeat_interval_seconds:, dependency_collection:, enabled: true)
|
23
|
-
@enabled = enabled
|
24
|
-
@emitter = Emitter.new
|
25
|
-
@stopped = false
|
26
|
-
@unsupported = false
|
27
|
-
@started = false
|
28
|
-
@dependency_collection = dependency_collection
|
29
|
-
|
30
|
-
@worker = Telemetry::Heartbeat.new(enabled: @enabled, heartbeat_interval_seconds: heartbeat_interval_seconds) do
|
31
|
-
next unless @started # `started!` should be the first event, thus ensure that `heartbeat!` is not sent first.
|
32
|
-
|
33
|
-
heartbeat!
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def disable!
|
38
|
-
@enabled = false
|
39
|
-
@worker.enabled = false
|
40
|
-
end
|
41
|
-
|
42
|
-
def started!
|
43
|
-
return if !@enabled || forked?
|
44
|
-
|
45
|
-
res = @emitter.request(Event::AppStarted.new)
|
46
|
-
|
47
|
-
if res.not_found? # Telemetry is only supported by agent versions 7.34 and up
|
48
|
-
Datadog.logger.debug('Agent does not support telemetry; disabling future telemetry events.')
|
49
|
-
disable!
|
50
|
-
@unsupported = true # Prevent telemetry from getting re-enabled
|
51
|
-
return res
|
52
|
-
end
|
53
|
-
|
54
|
-
@emitter.request(Event::AppDependenciesLoaded.new) if @dependency_collection
|
55
|
-
|
56
|
-
@started = true
|
57
|
-
end
|
58
|
-
|
59
|
-
def emit_closing!
|
60
|
-
return if !@enabled || forked?
|
61
|
-
|
62
|
-
@emitter.request(Event::AppClosing.new)
|
63
|
-
end
|
64
|
-
|
65
|
-
def stop!
|
66
|
-
return if @stopped
|
67
|
-
|
68
|
-
@worker.stop(true, 0)
|
69
|
-
@stopped = true
|
70
|
-
end
|
71
|
-
|
72
|
-
def integrations_change!
|
73
|
-
return if !@enabled || forked?
|
74
|
-
|
75
|
-
@emitter.request(Event::AppIntegrationsChange.new)
|
76
|
-
end
|
77
|
-
|
78
|
-
# Report configuration changes caused by Remote Configuration.
|
79
|
-
def client_configuration_change!(changes)
|
80
|
-
return if !@enabled || forked?
|
81
|
-
|
82
|
-
@emitter.request(Event::AppClientConfigurationChange.new(changes, 'remote_config'))
|
83
|
-
end
|
84
|
-
|
85
|
-
private
|
86
|
-
|
87
|
-
def heartbeat!
|
88
|
-
return if !@enabled || forked?
|
89
|
-
|
90
|
-
@emitter.request(Event::AppHeartbeat.new)
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
@@ -1,33 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative '../worker'
|
4
|
-
require_relative '../workers/polling'
|
5
|
-
|
6
|
-
module Datadog
|
7
|
-
module Core
|
8
|
-
module Telemetry
|
9
|
-
# Periodically (every DEFAULT_INTERVAL_SECONDS) sends a heartbeat event to the telemetry API.
|
10
|
-
class Heartbeat < Core::Worker
|
11
|
-
include Core::Workers::Polling
|
12
|
-
|
13
|
-
def initialize(heartbeat_interval_seconds:, enabled: true, &block)
|
14
|
-
# Workers::Polling settings
|
15
|
-
self.enabled = enabled
|
16
|
-
# Workers::IntervalLoop settings
|
17
|
-
self.loop_base_interval = heartbeat_interval_seconds
|
18
|
-
self.fork_policy = Core::Workers::Async::Thread::FORK_POLICY_STOP
|
19
|
-
super(&block)
|
20
|
-
start
|
21
|
-
end
|
22
|
-
|
23
|
-
def loop_wait_before_first_iteration?; end
|
24
|
-
|
25
|
-
private
|
26
|
-
|
27
|
-
def start
|
28
|
-
perform
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|