datadog 2.30.0 → 2.31.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.
Files changed (137) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +44 -1
  3. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +18 -0
  4. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +10 -0
  5. data/ext/datadog_profiling_native_extension/extconf.rb +2 -0
  6. data/ext/libdatadog_api/crashtracker.c +5 -8
  7. data/ext/libdatadog_api/datadog_ruby_common.c +18 -0
  8. data/ext/libdatadog_api/datadog_ruby_common.h +10 -0
  9. data/ext/libdatadog_api/di.c +79 -0
  10. data/ext/libdatadog_api/extconf.rb +2 -0
  11. data/ext/libdatadog_api/init.c +5 -2
  12. data/ext/libdatadog_extconf_helpers.rb +9 -1
  13. data/lib/datadog/ai_guard/component.rb +2 -0
  14. data/lib/datadog/ai_guard/contrib/ruby_llm/chat_instrumentation.rb +41 -3
  15. data/lib/datadog/ai_guard/evaluation/content_builder.rb +31 -0
  16. data/lib/datadog/ai_guard/evaluation/content_part.rb +36 -0
  17. data/lib/datadog/ai_guard/evaluation/no_op_result.rb +3 -1
  18. data/lib/datadog/ai_guard/evaluation/request.rb +14 -9
  19. data/lib/datadog/ai_guard/evaluation/result.rb +3 -1
  20. data/lib/datadog/ai_guard/evaluation.rb +36 -7
  21. data/lib/datadog/ai_guard.rb +26 -8
  22. data/lib/datadog/appsec/autoload.rb +1 -1
  23. data/lib/datadog/appsec/component.rb +11 -7
  24. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +1 -1
  25. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +6 -7
  26. data/lib/datadog/appsec/contrib/rails/patcher.rb +2 -2
  27. data/lib/datadog/appsec/instrumentation/gateway.rb +0 -13
  28. data/lib/datadog/appsec/monitor/gateway/watcher.rb +2 -0
  29. data/lib/datadog/appsec/utils/http/media_type.rb +1 -2
  30. data/lib/datadog/appsec/utils/http/url_encoded.rb +2 -2
  31. data/lib/datadog/appsec.rb +5 -9
  32. data/lib/datadog/core/configuration/base.rb +17 -5
  33. data/lib/datadog/core/configuration/components.rb +21 -8
  34. data/lib/datadog/core/configuration/config_helper.rb +9 -0
  35. data/lib/datadog/core/configuration/option.rb +30 -5
  36. data/lib/datadog/core/configuration/option_definition.rb +38 -12
  37. data/lib/datadog/core/configuration/options.rb +40 -6
  38. data/lib/datadog/core/configuration/settings.rb +15 -0
  39. data/lib/datadog/core/configuration/supported_configurations.rb +1 -0
  40. data/lib/datadog/core/contrib/rails/railtie.rb +32 -0
  41. data/lib/datadog/core/contrib/rails/utils.rb +7 -3
  42. data/lib/datadog/core/crashtracking/component.rb +3 -3
  43. data/lib/datadog/core/environment/container.rb +2 -2
  44. data/lib/datadog/core/environment/ext.rb +1 -0
  45. data/lib/datadog/core/environment/identity.rb +25 -3
  46. data/lib/datadog/core/environment/process.rb +12 -0
  47. data/lib/datadog/core/metrics/client.rb +5 -5
  48. data/lib/datadog/core/remote/component.rb +38 -21
  49. data/lib/datadog/core/runtime/metrics.rb +1 -1
  50. data/lib/datadog/core/telemetry/component.rb +3 -0
  51. data/lib/datadog/core/telemetry/event/app_client_configuration_change.rb +2 -3
  52. data/lib/datadog/core/telemetry/event/app_extended_heartbeat.rb +32 -0
  53. data/lib/datadog/core/telemetry/event/app_started.rb +151 -169
  54. data/lib/datadog/core/telemetry/event.rb +1 -7
  55. data/lib/datadog/core/telemetry/ext.rb +1 -0
  56. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +5 -0
  57. data/lib/datadog/core/telemetry/worker.rb +20 -0
  58. data/lib/datadog/core/utils/only_once.rb +1 -1
  59. data/lib/datadog/core/utils/spawn_monkey_patch.rb +36 -0
  60. data/lib/datadog/core/workers/async.rb +1 -1
  61. data/lib/datadog/core.rb +0 -1
  62. data/lib/datadog/data_streams/pathway_context.rb +1 -1
  63. data/lib/datadog/di/boot.rb +2 -4
  64. data/lib/datadog/di/component.rb +4 -0
  65. data/lib/datadog/di/instrumenter.rb +10 -4
  66. data/lib/datadog/di/probe_notification_builder.rb +109 -1
  67. data/lib/datadog/di/serializer.rb +1 -1
  68. data/lib/datadog/di.rb +81 -0
  69. data/lib/datadog/kit/enable_core_dumps.rb +1 -1
  70. data/lib/datadog/open_feature/evaluation_engine.rb +1 -1
  71. data/lib/datadog/open_feature/exposures/reporter.rb +1 -1
  72. data/lib/datadog/open_feature/exposures/worker.rb +1 -1
  73. data/lib/datadog/open_feature/remote.rb +1 -1
  74. data/lib/datadog/open_feature/transport.rb +1 -1
  75. data/lib/datadog/opentelemetry/configuration/settings.rb +2 -0
  76. data/lib/datadog/profiling/collectors/code_provenance.rb +2 -3
  77. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +1 -1
  78. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -1
  79. data/lib/datadog/profiling/component.rb +11 -1
  80. data/lib/datadog/profiling/load_native_extension.rb +1 -1
  81. data/lib/datadog/profiling/profiler.rb +0 -4
  82. data/lib/datadog/profiling/scheduler.rb +2 -2
  83. data/lib/datadog/profiling/tasks/exec.rb +2 -2
  84. data/lib/datadog/profiling/tasks/setup.rb +2 -2
  85. data/lib/datadog/profiling.rb +1 -2
  86. data/lib/datadog/single_step_instrument.rb +1 -1
  87. data/lib/datadog/tracing/buffer.rb +3 -3
  88. data/lib/datadog/tracing/component.rb +11 -0
  89. data/lib/datadog/tracing/configuration/settings.rb +2 -1
  90. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +2 -2
  91. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +20 -0
  92. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/patcher.rb +3 -1
  93. data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +1 -1
  94. data/lib/datadog/tracing/contrib/active_job/events/discard.rb +1 -1
  95. data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +1 -1
  96. data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +1 -1
  97. data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +1 -1
  98. data/lib/datadog/tracing/contrib/active_job/events/perform.rb +1 -1
  99. data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +1 -1
  100. data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +1 -1
  101. data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +1 -1
  102. data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +1 -1
  103. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +1 -1
  104. data/lib/datadog/tracing/contrib/active_record/utils.rb +1 -1
  105. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +1 -1
  106. data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +2 -2
  107. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +1 -1
  108. data/lib/datadog/tracing/contrib/configurable.rb +18 -3
  109. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +1 -1
  110. data/lib/datadog/tracing/contrib/excon/middleware.rb +2 -2
  111. data/lib/datadog/tracing/contrib/faraday/middleware.rb +2 -2
  112. data/lib/datadog/tracing/contrib/grape/endpoint.rb +5 -5
  113. data/lib/datadog/tracing/contrib/http/instrumentation.rb +1 -1
  114. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +1 -1
  115. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +1 -1
  116. data/lib/datadog/tracing/contrib/rails/log_injection.rb +1 -1
  117. data/lib/datadog/tracing/contrib/rails/patcher.rb +0 -1
  118. data/lib/datadog/tracing/contrib/rails/runner.rb +1 -1
  119. data/lib/datadog/tracing/contrib/rake/instrumentation.rb +2 -2
  120. data/lib/datadog/tracing/contrib/redis/tags.rb +1 -1
  121. data/lib/datadog/tracing/contrib/status_range_matcher.rb +4 -0
  122. data/lib/datadog/tracing/contrib/stripe/request.rb +1 -1
  123. data/lib/datadog/tracing/distributed/datadog.rb +4 -2
  124. data/lib/datadog/tracing/event.rb +1 -1
  125. data/lib/datadog/tracing/remote.rb +1 -1
  126. data/lib/datadog/tracing/sampling/ext.rb +2 -0
  127. data/lib/datadog/tracing/sampling/priority_sampler.rb +13 -0
  128. data/lib/datadog/tracing/sampling/rule.rb +1 -1
  129. data/lib/datadog/tracing/sampling/rule_sampler.rb +54 -25
  130. data/lib/datadog/tracing/sampling/span/rule_parser.rb +1 -1
  131. data/lib/datadog/tracing/span_operation.rb +1 -1
  132. data/lib/datadog/tracing/trace_operation.rb +50 -6
  133. data/lib/datadog/tracing/tracer.rb +25 -0
  134. data/lib/datadog/tracing/transport/io/client.rb +1 -1
  135. data/lib/datadog/tracing/transport/trace_formatter.rb +1 -1
  136. data/lib/datadog/version.rb +1 -1
  137. metadata +13 -7
@@ -16,17 +16,47 @@ module Datadog
16
16
  # Class behavior for a configuration object with options
17
17
  # @public_api
18
18
  module ClassMethods
19
+ def settings_path
20
+ defined?(@settings_path) ? @settings_path : nil
21
+ end
22
+
23
+ # Registry of nested settings classes keyed by the option name declared
24
+ # via `Base.settings`. This lets us propagate a new settings path down the
25
+ # tree without storing parent references on the nested class or on the
26
+ # option definition itself.
27
+ def settings_children
28
+ @settings_children ||= if superclass <= Options
29
+ superclass.settings_children.dup
30
+ else
31
+ {}
32
+ end
33
+ end
34
+
35
+ def settings_path=(path)
36
+ @settings_path = path
37
+
38
+ # Keep nested settings paths in sync when a parent settings path is
39
+ # assigned later, which is how contrib integrations inject
40
+ # "tracing.<integration>" into their configuration classes.
41
+ settings_children.each do |name, settings_class|
42
+ nested_settings_path = path ? "#{path}.#{name}" : name.to_s
43
+ settings_class.settings_path = nested_settings_path
44
+ end
45
+ end
46
+
19
47
  def options
20
48
  # Allows for class inheritance of option definitions
21
- @options ||= (superclass <= Options) ? superclass.options.dup : {}
49
+ @options ||= if superclass <= Options
50
+ superclass.options.dup
51
+ else
52
+ {}
53
+ end
22
54
  end
23
55
 
24
56
  protected
25
57
 
26
- def option(name, meta = {}, &block)
27
- settings_name = defined?(@settings_name) && @settings_name
28
- option_name = settings_name ? "#{settings_name}.#{name}" : name
29
- builder = OptionDefinition::Builder.new(option_name, meta, &block)
58
+ def option(name, attributes = {}, &block)
59
+ builder = OptionDefinition::Builder.new(name, attributes, &block)
30
60
  options[name] = builder.to_definition.tap do
31
61
  # Resolve and define helper functions
32
62
  helpers = default_helpers(name)
@@ -75,7 +105,11 @@ module Datadog
75
105
  end
76
106
 
77
107
  def set_option(name, value, precedence: Configuration::Option::Precedence::PROGRAMMATIC)
78
- resolve_option(name).set(value, precedence: precedence)
108
+ option = resolve_option(name)
109
+ # Populate lower-precedence values so telemetry and `unset` can
110
+ # still observe the fallback chain after a first programmatic set.
111
+ option.get
112
+ option.set(value, precedence: precedence)
79
113
  end
80
114
 
81
115
  def unset_option(name, precedence: Configuration::Option::Precedence::PROGRAMMATIC)
@@ -221,6 +221,8 @@ module Datadog
221
221
  #
222
222
  # @return Logger::Severity
223
223
  option :instance do |o|
224
+ # Telemetry for this option is manually modified and added in the AppStarted event.
225
+ o.skip_telemetry true
224
226
  o.after_set { |value| set_option(:level, value.level) unless value.nil? }
225
227
  end
226
228
 
@@ -912,6 +914,19 @@ module Datadog
912
914
  o.default 60.0
913
915
  end
914
916
 
917
+ # The interval in seconds when extended heartbeat must be sent.
918
+ #
919
+ # This method is used internally, for testing purposes only.
920
+ #
921
+ # @default `DD_TELEMETRY_EXTENDED_HEARTBEAT_INTERVAL` environment variable, otherwise `86400`.
922
+ # @return [Integer]
923
+ # @!visibility private
924
+ option :extended_heartbeat_interval_seconds do |o|
925
+ o.type :int
926
+ o.env Core::Telemetry::Ext::ENV_EXTENDED_HEARTBEAT_INTERVAL
927
+ o.default 86400
928
+ end
929
+
915
930
  # The interval in seconds when telemetry metrics are aggregated.
916
931
  # Should be a denominator of `heartbeat_interval_seconds`.
917
932
  #
@@ -107,6 +107,7 @@ module Datadog
107
107
  "DD_TAGS",
108
108
  "DD_TELEMETRY_AGENTLESS_URL",
109
109
  "DD_TELEMETRY_DEPENDENCY_COLLECTION_ENABLED",
110
+ "DD_TELEMETRY_EXTENDED_HEARTBEAT_INTERVAL",
110
111
  "DD_TELEMETRY_HEARTBEAT_INTERVAL",
111
112
  "DD_TELEMETRY_LOG_COLLECTION_ENABLED",
112
113
  "DD_TELEMETRY_METRICS_AGGREGATION_INTERVAL",
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'utils'
4
+ require_relative '../../environment/process'
5
+ require_relative '../../process_discovery'
6
+
7
+ module Datadog
8
+ module Core
9
+ module Contrib
10
+ module Rails
11
+ # Railtie for core Rails setup that benefits all Datadog products.
12
+ class Railtie < ::Rails::Railtie
13
+ def self.after_initialize
14
+ if Datadog.configuration.experimental_propagate_process_tags_enabled
15
+ Datadog::Core::Environment::Process.rails_application_name =
16
+ Datadog::Core::Contrib::Rails::Utils.app_name
17
+ end
18
+
19
+ # Process Discovery should always publish after_initialize since it has access to more information
20
+ Datadog::Core::ProcessDiscovery.publish(Datadog.configuration)
21
+ end
22
+
23
+ # Registered after the method definition so the method exists if on_load fires immediately
24
+ # (which happens when the Railtie is loaded into an already-initialized Rails app).
25
+ ::ActiveSupport.on_load(:after_initialize) do
26
+ Datadog::Core::Contrib::Rails::Railtie.after_initialize
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -7,11 +7,15 @@ module Datadog
7
7
  # common utilities for Rails
8
8
  module Utils
9
9
  def self.app_name
10
- if ::Rails::VERSION::MAJOR >= 6
11
- ::Rails.application.class.module_parent_name.underscore
10
+ application_name = if ::Rails::VERSION::MAJOR >= 6
11
+ ::Rails.application.class.module_parent_name
12
12
  else
13
- ::Rails.application.class.parent_name.underscore
13
+ ::Rails.application.class.parent_name
14
14
  end
15
+ application_name&.underscore
16
+ rescue => e
17
+ Datadog.logger.debug("Failed to extract Rails application name: #{e.class}: #{e}")
18
+ nil
15
19
  end
16
20
 
17
21
  def self.railtie_supported?
@@ -55,7 +55,7 @@ module Datadog
55
55
  # Unhandled exception report triggering means that the application is already in a bad state
56
56
  # We don't want to swallow non-StandardError exceptions here; we would rather just let the
57
57
  # application crash
58
- Datadog.logger.debug("Crashtracker failed to report unhandled exception: #{e.message}")
58
+ Datadog.logger.debug { "Crashtracker failed to report unhandled exception: #{e.class}: #{e}" }
59
59
  end
60
60
  end
61
61
 
@@ -130,7 +130,7 @@ module Datadog
130
130
  self.class._native_stop
131
131
  logger.debug('Crash tracking stopped successfully')
132
132
  rescue => e
133
- logger.error("Failed to stop crash tracking: #{e.message}")
133
+ logger.error("Failed to stop crash tracking: #{e.class}: #{e}")
134
134
  end
135
135
 
136
136
  private
@@ -149,7 +149,7 @@ module Datadog
149
149
  )
150
150
  logger.debug("Crash tracking action: #{action} successful")
151
151
  rescue => e
152
- logger.error("Failed to #{action} crash tracking: #{e.message}")
152
+ logger.error("Failed to #{action} crash tracking: #{e.class}: #{e}")
153
153
  end
154
154
  end
155
155
  end
@@ -108,7 +108,7 @@ module Datadog
108
108
  end
109
109
  rescue => e
110
110
  Datadog.logger.debug(
111
- "Error while checking cgroup namespace. Cause: #{e.class.name} #{e.message} Location: #{Array(e.backtrace).first}"
111
+ "Error while checking cgroup namespace. Cause: #{e.class}: #{e} Location: #{Array(e.backtrace).first}"
112
112
  )
113
113
  false
114
114
  end
@@ -172,7 +172,7 @@ module Datadog
172
172
  @entry = Entry.new # Empty entry if no valid cgroup entry is found
173
173
  rescue => e
174
174
  Datadog.logger.debug(
175
- "Error while reading container entry. Cause: #{e.class.name} #{e.message} Location: #{Array(e.backtrace).first}"
175
+ "Error while reading container entry. Cause: #{e.class}: #{e} Location: #{Array(e.backtrace).first}"
176
176
  )
177
177
  @entry = Entry.new unless defined?(@entry)
178
178
  @entry
@@ -41,6 +41,7 @@ module Datadog
41
41
  TAG_ENTRYPOINT_NAME = "entrypoint.name"
42
42
  TAG_ENTRYPOINT_WORKDIR = "entrypoint.workdir"
43
43
  TAG_ENTRYPOINT_TYPE = "entrypoint.type"
44
+ TAG_RAILS_APPLICATION = "rails.application"
44
45
  TAG_PROCESS_TAGS = "_dd.tags.process"
45
46
  TAG_SERVICE = 'service'
46
47
  TAG_VERSION = 'version'
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'securerandom'
4
4
 
5
+ require_relative '../configuration/config_helper'
5
6
  require_relative 'ext'
6
7
  require_relative '../utils/forking'
7
8
 
@@ -13,18 +14,39 @@ module Datadog
13
14
  module Identity
14
15
  extend Core::Utils::Forking
15
16
 
17
+ ENV_ROOT_SESSION_ID = '_DD_ROOT_RB_SESSION_ID'
18
+ ENV_PARENT_SESSION_ID = '_DD_PARENT_RB_SESSION_ID'
19
+
16
20
  module_function
17
21
 
18
- # Retrieves number of classes from runtime
22
+ @root_runtime_id = DATADOG_ENV[ENV_ROOT_SESSION_ID]&.freeze
23
+ @parent_runtime_id = DATADOG_ENV[ENV_PARENT_SESSION_ID]&.freeze
24
+
19
25
  def id
20
26
  @id ||= ::SecureRandom.uuid.freeze
21
27
 
22
- # Check if runtime has changed, e.g. forked.
23
- after_fork! { @id = ::SecureRandom.uuid.freeze }
28
+ after_fork! do
29
+ # Order matters: capture @id before overwriting
30
+ @parent_runtime_id = @id
31
+ @root_runtime_id ||= @id
32
+ @id = ::SecureRandom.uuid.freeze
33
+ end
24
34
 
25
35
  @id
26
36
  end
27
37
 
38
+ def root_runtime_id
39
+ @root_runtime_id
40
+ end
41
+
42
+ def parent_runtime_id
43
+ @parent_runtime_id
44
+ end
45
+
46
+ def runtime_propagation_envs
47
+ {ENV_ROOT_SESSION_ID => root_runtime_id || id, ENV_PARENT_SESSION_ID => id}.freeze
48
+ end
49
+
28
50
  def pid
29
51
  ::Process.pid
30
52
  end
@@ -35,6 +35,9 @@ module Datadog
35
35
 
36
36
  tags << "#{Environment::Ext::TAG_ENTRYPOINT_TYPE}:#{TagNormalizer.normalize(entrypoint_type, remove_digit_start_char: false)}"
37
37
 
38
+ rails_application_name = TagNormalizer.normalize_process_value(@rails_application_name.to_s)
39
+ tags << "#{Environment::Ext::TAG_RAILS_APPLICATION}:#{rails_application_name}" unless rails_application_name.empty?
40
+
38
41
  @tags = tags.freeze
39
42
  end
40
43
 
@@ -80,6 +83,15 @@ module Datadog
80
83
  File.basename(File.expand_path(File.dirname($0)))
81
84
  end
82
85
 
86
+ # Sets the rails application name from other places in code
87
+ # @param name [String] the rails application name
88
+ # @return [void]
89
+ def self.rails_application_name=(name)
90
+ @rails_application_name = name
91
+ remove_instance_variable(:@tags) if instance_variable_defined?(:@tags)
92
+ remove_instance_variable(:@serialized) if instance_variable_defined?(:@serialized)
93
+ end
94
+
83
95
  private_class_method :entrypoint_workdir, :entrypoint_type, :entrypoint_name, :entrypoint_basedir
84
96
  end
85
97
  end
@@ -101,7 +101,7 @@ module Datadog
101
101
  statsd.count(stat, value, metric_options(options))
102
102
  rescue => e
103
103
  logger.error(
104
- "Failed to send count stat. Cause: #{e.class.name} #{e.message} Source: #{Array(e.backtrace).first}"
104
+ "Failed to send count stat. Cause: #{e.class}: #{e} Source: #{Array(e.backtrace).first}"
105
105
  )
106
106
  telemetry.report(e, description: 'Failed to send count stat')
107
107
  end
@@ -115,7 +115,7 @@ module Datadog
115
115
  statsd.distribution(stat, value, metric_options(options))
116
116
  rescue => e
117
117
  logger.error(
118
- "Failed to send distribution stat. Cause: #{e.class.name} #{e.message} Source: #{Array(e.backtrace).first}"
118
+ "Failed to send distribution stat. Cause: #{e.class}: #{e} Source: #{Array(e.backtrace).first}"
119
119
  )
120
120
  telemetry.report(e, description: 'Failed to send distribution stat')
121
121
  end
@@ -128,7 +128,7 @@ module Datadog
128
128
  statsd.increment(stat, metric_options(options))
129
129
  rescue => e
130
130
  logger.error(
131
- "Failed to send increment stat. Cause: #{e.class.name} #{e.message} Source: #{Array(e.backtrace).first}"
131
+ "Failed to send increment stat. Cause: #{e.class}: #{e} Source: #{Array(e.backtrace).first}"
132
132
  )
133
133
  telemetry.report(e, description: 'Failed to send increment stat')
134
134
  end
@@ -142,7 +142,7 @@ module Datadog
142
142
  statsd.gauge(stat, value, metric_options(options))
143
143
  rescue => e
144
144
  logger.error(
145
- "Failed to send gauge stat. Cause: #{e.class.name} #{e.message} Source: #{Array(e.backtrace).first}"
145
+ "Failed to send gauge stat. Cause: #{e.class}: #{e} Source: #{Array(e.backtrace).first}"
146
146
  )
147
147
  telemetry.report(e, description: 'Failed to send gauge stat')
148
148
  end
@@ -162,7 +162,7 @@ module Datadog
162
162
  rescue => e
163
163
  # TODO: Likely to be redundant, since `distribution` handles its own errors.
164
164
  logger.error(
165
- "Failed to send time stat. Cause: #{e.class.name} #{e.message} Source: #{Array(e.backtrace).first}"
165
+ "Failed to send time stat. Cause: #{e.class}: #{e} Source: #{Array(e.backtrace).first}"
166
166
  )
167
167
  telemetry.report(e, description: 'Failed to send time stat')
168
168
  end
@@ -45,7 +45,7 @@ module Datadog
45
45
  rescue Client::SyncError => e
46
46
  # Transient errors due to network or agent. Logged the error but not via telemetry
47
47
  logger.error do
48
- "remote worker client sync error: #{e.message} location: #{Array(e.backtrace).first}. skipping sync"
48
+ "remote worker client sync error: #{e.class}: #{e} location: #{Array(e.backtrace).first}. skipping sync"
49
49
  end
50
50
  rescue => e
51
51
  # In case of unexpected errors, reset the negotiation object
@@ -55,7 +55,7 @@ module Datadog
55
55
 
56
56
  # Transient errors due to network or agent. Logged the error but not via telemetry
57
57
  logger.error do
58
- "remote worker error: #{e.class.name} #{e.message} location: #{Array(e.backtrace).first}. " \
58
+ "remote worker error: #{e.class}: #{e} location: #{Array(e.backtrace).first}. " \
59
59
  'resetting client state'
60
60
  end
61
61
 
@@ -105,6 +105,7 @@ module Datadog
105
105
  class Barrier
106
106
  def initialize(timeout = nil)
107
107
  @once = false
108
+ @waited = false
108
109
  @timeout = timeout
109
110
 
110
111
  @mutex = Mutex.new
@@ -112,37 +113,53 @@ module Datadog
112
113
  end
113
114
 
114
115
  # Wait for first lift to happen, otherwise don't wait
116
+ #
117
+ # Returns:
118
+ # - :lift if the barrier was lifted (worker completed a cycle)
119
+ # - :timeout if the wait timed out before the barrier was lifted
120
+ # - :pass if wait_once was already called previously
121
+ #
122
+ # Uses a separate @waited flag to distinguish "already waited" (:pass)
123
+ # from "worker lifted before we could wait" (:lift). Without this,
124
+ # a race between Worker#start and wait_once can cause the first call
125
+ # to return :pass if the worker completes before wait_once runs.
115
126
  def wait_once(timeout = nil)
116
- # TTAS (Test and Test-And-Set) optimisation
117
- # Since @once only ever goes from false to true, this is semantically valid
118
- return :pass if @once
119
-
120
- begin
121
- @mutex.lock
127
+ # TTAS (Test and Test-And-Set) optimisation for subsequent calls.
128
+ # @waited is only set inside the mutex and only transitions false -> true,
129
+ # so an unsynchronized read is safe: a stale `false` just falls through
130
+ # to the synchronized path which re-checks.
131
+ return :pass if @waited
122
132
 
123
- return :pass if @once
124
-
125
- timeout ||= @timeout
133
+ @mutex.synchronize do
134
+ return :pass if @waited
126
135
 
127
- # - starting with Ruby 3.2, ConditionVariable#wait returns nil on
128
- # timeout and an integer otherwise
129
- # - before Ruby 3.2, ConditionVariable returns itself
130
- # so we have to rely on @once having been set
131
- if RUBY_VERSION >= '3.2'
132
- lifted = @condition.wait(@mutex, timeout)
136
+ if @once
137
+ # Worker lifted the barrier before we could wait.
138
+ # This is still the first call, so return :lift not :pass.
139
+ lifted = true
133
140
  else
134
- @condition.wait(@mutex, timeout)
135
- lifted = @once
141
+ timeout ||= @timeout
142
+
143
+ # - starting with Ruby 3.2, ConditionVariable#wait returns nil on
144
+ # timeout and an integer otherwise
145
+ # - before Ruby 3.2, ConditionVariable returns itself
146
+ # so we have to rely on @once having been set
147
+ if RUBY_VERSION >= '3.2'
148
+ lifted = @condition.wait(@mutex, timeout)
149
+ else
150
+ @condition.wait(@mutex, timeout)
151
+ lifted = @once
152
+ end
136
153
  end
137
154
 
155
+ @waited = true
156
+
138
157
  if lifted
139
158
  :lift
140
159
  else
141
160
  @once = true
142
161
  :timeout
143
162
  end
144
- ensure
145
- @mutex.unlock
146
163
  end
147
164
  end
148
165
 
@@ -100,7 +100,7 @@ module Datadog
100
100
  def try_flush
101
101
  yield
102
102
  rescue => e
103
- Datadog.logger.warn("Error while sending runtime metric. Cause: #{e.class.name} #{e.message}")
103
+ Datadog.logger.warn("Error while sending runtime metric. Cause: #{e.class}: #{e}")
104
104
  end
105
105
 
106
106
  def default_metric_options
@@ -99,6 +99,7 @@ module Datadog
99
99
  @worker = Telemetry::Worker.new(
100
100
  enabled: @enabled,
101
101
  heartbeat_interval_seconds: settings.telemetry.heartbeat_interval_seconds,
102
+ extended_heartbeat_interval_seconds: settings.telemetry.extended_heartbeat_interval_seconds,
102
103
  metrics_aggregation_interval_seconds: settings.telemetry.metrics_aggregation_interval_seconds,
103
104
  emitter: Emitter.new(
104
105
  @transport,
@@ -108,6 +109,8 @@ module Datadog
108
109
  metrics_manager: @metrics_manager,
109
110
  dependency_collection: settings.telemetry.dependency_collection,
110
111
  logger: logger,
112
+ settings: settings,
113
+ agent_settings: agent_settings,
111
114
  shutdown_timeout: settings.telemetry.shutdown_timeout_seconds,
112
115
  )
113
116
 
@@ -26,14 +26,13 @@ module Datadog
26
26
 
27
27
  def configuration
28
28
  config = Datadog.configuration
29
- seq_id = Event.configuration_sequence.next
30
29
 
31
30
  res = @changes.map do |name, value|
32
31
  {
33
32
  name: name,
34
33
  value: value,
35
34
  origin: @origin,
36
- seq_id: seq_id,
35
+ seq_id: Configuration::Option::Precedence::REMOTE_CONFIGURATION.numeric.next,
37
36
  }
38
37
  end
39
38
 
@@ -43,7 +42,7 @@ module Datadog
43
42
  name: 'appsec.sca_enabled',
44
43
  value: config.appsec.sca_enabled,
45
44
  origin: 'code',
46
- seq_id: seq_id,
45
+ seq_id: Configuration::Option::Precedence::PROGRAMMATIC.numeric.next,
47
46
  }
48
47
  end
49
48
 
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'app_started'
4
+
5
+ module Datadog
6
+ module Core
7
+ module Telemetry
8
+ module Event
9
+ # Telemetry class for the 'app-extended-heartbeat' event
10
+ class AppExtendedHeartbeat < AppStarted
11
+ def initialize(settings:, agent_settings:)
12
+ @configuration = configuration(settings, agent_settings)
13
+ end
14
+
15
+ def type
16
+ 'app-extended-heartbeat'
17
+ end
18
+
19
+ def payload
20
+ {
21
+ configuration: @configuration,
22
+ }
23
+ end
24
+
25
+ def app_started?
26
+ false
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end