ddtrace 1.22.0 → 1.23.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -38,6 +38,7 @@ module Datadog
38
38
  private
39
39
 
40
40
  def products
41
+ # @type var products: Hash[Symbol, Hash[Symbol, Object]]
41
42
  products = {
42
43
  appsec: {
43
44
  enabled: Datadog::AppSec.enabled?,
@@ -13,7 +13,7 @@ module Datadog
13
13
  :timeout,
14
14
  :ssl
15
15
 
16
- DEFAULT_TIMEOUT = 30
16
+ DEFAULT_TIMEOUT = 2
17
17
 
18
18
  def initialize(hostname:, port: nil, timeout: DEFAULT_TIMEOUT, ssl: true)
19
19
  @hostname = hostname
@@ -32,6 +32,10 @@ module Datadog
32
32
  nil
33
33
  end
34
34
 
35
+ def code
36
+ nil
37
+ end
38
+
35
39
  def inspect
36
40
  "#{self.class} ok?:#{ok?} unsupported?:#{unsupported?}, " \
37
41
  "not_found?:#{not_found?}, client_error?:#{client_error?}, " \
@@ -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
- start, finish, compressed_pprof = pprof_recorder.serialize
56
- @last_flush_finish_at = finish
55
+ serialization_result = pprof_recorder.serialize
56
+ return if serialization_result.nil?
57
57
 
58
- return if compressed_pprof.nil? # We don't want to report empty profiles
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
@@ -3,8 +3,8 @@
3
3
  module DDTrace
4
4
  module VERSION
5
5
  MAJOR = 1
6
- MINOR = 22
7
- PATCH = 0
6
+ MINOR = 23
7
+ PATCH = 3
8
8
  PRE = nil
9
9
  BUILD = nil
10
10
  # PRE and BUILD above are modified for dev gems during gem build GHA workflow
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.22.0
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-04-16 00:00:00.000000000 Z
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/client.rb
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/master/CHANGELOG.md
882
- post_install_message:
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.5.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