datadog 2.1.0 → 2.3.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 (183) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +101 -1
  3. data/ext/datadog_profiling_loader/extconf.rb +15 -15
  4. data/ext/datadog_profiling_native_extension/clock_id.h +1 -0
  5. data/ext/datadog_profiling_native_extension/clock_id_from_pthread.c +1 -2
  6. data/ext/datadog_profiling_native_extension/clock_id_noop.c +1 -2
  7. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +132 -44
  8. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +49 -26
  9. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.h +34 -4
  10. data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +4 -0
  11. data/ext/datadog_profiling_native_extension/collectors_stack.c +90 -37
  12. data/ext/datadog_profiling_native_extension/collectors_stack.h +2 -2
  13. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +81 -19
  14. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +1 -0
  15. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +110 -0
  16. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +57 -0
  17. data/ext/datadog_profiling_native_extension/extconf.rb +69 -62
  18. data/ext/datadog_profiling_native_extension/heap_recorder.c +34 -6
  19. data/ext/datadog_profiling_native_extension/heap_recorder.h +3 -1
  20. data/ext/datadog_profiling_native_extension/helpers.h +6 -17
  21. data/ext/datadog_profiling_native_extension/http_transport.c +3 -3
  22. data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +0 -86
  23. data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +2 -23
  24. data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +61 -126
  25. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +64 -138
  26. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +17 -11
  27. data/ext/datadog_profiling_native_extension/profiling.c +0 -2
  28. data/ext/datadog_profiling_native_extension/ruby_helpers.c +0 -33
  29. data/ext/datadog_profiling_native_extension/ruby_helpers.h +1 -26
  30. data/ext/datadog_profiling_native_extension/setup_signal_handler.c +1 -1
  31. data/ext/datadog_profiling_native_extension/setup_signal_handler.h +1 -0
  32. data/ext/datadog_profiling_native_extension/stack_recorder.c +27 -8
  33. data/ext/datadog_profiling_native_extension/stack_recorder.h +2 -0
  34. data/ext/datadog_profiling_native_extension/time_helpers.c +0 -15
  35. data/ext/datadog_profiling_native_extension/time_helpers.h +36 -6
  36. data/ext/{datadog_profiling_native_extension → libdatadog_api}/crashtracker.c +20 -7
  37. data/ext/libdatadog_api/datadog_ruby_common.c +110 -0
  38. data/ext/libdatadog_api/datadog_ruby_common.h +57 -0
  39. data/ext/libdatadog_api/extconf.rb +108 -0
  40. data/ext/libdatadog_api/macos_development.md +26 -0
  41. data/ext/libdatadog_extconf_helpers.rb +130 -0
  42. data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +49 -0
  43. data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +73 -0
  44. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +68 -0
  45. data/lib/datadog/appsec/contrib/graphql/integration.rb +41 -0
  46. data/lib/datadog/appsec/contrib/graphql/patcher.rb +37 -0
  47. data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +59 -0
  48. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +1 -1
  49. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +1 -1
  50. data/lib/datadog/appsec/extensions.rb +1 -0
  51. data/lib/datadog/appsec/processor/actions.rb +1 -1
  52. data/lib/datadog/appsec/response.rb +15 -1
  53. data/lib/datadog/appsec.rb +1 -0
  54. data/lib/datadog/core/configuration/components.rb +17 -12
  55. data/lib/datadog/core/configuration/settings.rb +93 -7
  56. data/lib/datadog/core/configuration.rb +3 -17
  57. data/lib/datadog/core/crashtracking/agent_base_url.rb +21 -0
  58. data/lib/datadog/core/crashtracking/component.rb +111 -0
  59. data/lib/datadog/core/crashtracking/tag_builder.rb +39 -0
  60. data/lib/datadog/core/deprecations.rb +58 -0
  61. data/lib/datadog/core/diagnostics/environment_logger.rb +8 -11
  62. data/lib/datadog/core/environment/yjit.rb +5 -0
  63. data/lib/datadog/core/runtime/ext.rb +1 -0
  64. data/lib/datadog/core/runtime/metrics.rb +6 -0
  65. data/lib/datadog/core/telemetry/component.rb +154 -0
  66. data/lib/datadog/core/telemetry/emitter.rb +9 -11
  67. data/lib/datadog/core/telemetry/event.rb +132 -26
  68. data/lib/datadog/core/telemetry/ext.rb +3 -0
  69. data/lib/datadog/core/telemetry/http/adapters/net.rb +11 -13
  70. data/lib/datadog/core/telemetry/http/ext.rb +3 -0
  71. data/lib/datadog/core/telemetry/http/transport.rb +38 -9
  72. data/lib/datadog/core/telemetry/logging.rb +35 -0
  73. data/lib/datadog/core/telemetry/metric.rb +167 -0
  74. data/lib/datadog/core/telemetry/metrics_collection.rb +81 -0
  75. data/lib/datadog/core/telemetry/metrics_manager.rb +81 -0
  76. data/lib/datadog/core/telemetry/request.rb +1 -1
  77. data/lib/datadog/core/telemetry/worker.rb +173 -0
  78. data/lib/datadog/core/utils/at_fork_monkey_patch.rb +102 -0
  79. data/lib/datadog/core/utils/only_once_successful.rb +76 -0
  80. data/lib/datadog/core.rb +2 -19
  81. data/lib/datadog/kit/appsec/events.rb +2 -4
  82. data/lib/datadog/opentelemetry/sdk/propagator.rb +5 -10
  83. data/lib/datadog/opentelemetry/sdk/span_processor.rb +15 -2
  84. data/lib/datadog/opentelemetry/sdk/trace/span.rb +23 -0
  85. data/lib/datadog/profiling/collectors/code_provenance.rb +24 -11
  86. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +17 -17
  87. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +11 -13
  88. data/lib/datadog/profiling/collectors/info.rb +3 -3
  89. data/lib/datadog/profiling/collectors/thread_context.rb +4 -2
  90. data/lib/datadog/profiling/component.rb +85 -90
  91. data/lib/datadog/profiling/exporter.rb +3 -3
  92. data/lib/datadog/profiling/ext/dir_monkey_patches.rb +410 -0
  93. data/lib/datadog/profiling/ext.rb +21 -21
  94. data/lib/datadog/profiling/flush.rb +1 -1
  95. data/lib/datadog/profiling/http_transport.rb +8 -6
  96. data/lib/datadog/profiling/load_native_extension.rb +5 -5
  97. data/lib/datadog/profiling/preload.rb +1 -1
  98. data/lib/datadog/profiling/profiler.rb +5 -8
  99. data/lib/datadog/profiling/scheduler.rb +31 -25
  100. data/lib/datadog/profiling/tag_builder.rb +2 -2
  101. data/lib/datadog/profiling/tasks/exec.rb +5 -5
  102. data/lib/datadog/profiling/tasks/setup.rb +16 -35
  103. data/lib/datadog/profiling.rb +5 -5
  104. data/lib/datadog/tracing/contrib/action_cable/event.rb +1 -1
  105. data/lib/datadog/tracing/contrib/action_cable/events/broadcast.rb +1 -1
  106. data/lib/datadog/tracing/contrib/action_cable/events/perform_action.rb +1 -1
  107. data/lib/datadog/tracing/contrib/action_cable/events/transmit.rb +1 -1
  108. data/lib/datadog/tracing/contrib/action_mailer/event.rb +4 -6
  109. data/lib/datadog/tracing/contrib/action_mailer/events/deliver.rb +9 -4
  110. data/lib/datadog/tracing/contrib/action_mailer/events/process.rb +3 -2
  111. data/lib/datadog/tracing/contrib/action_view/events/render_partial.rb +1 -5
  112. data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +1 -1
  113. data/lib/datadog/tracing/contrib/active_job/events/discard.rb +1 -1
  114. data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +1 -1
  115. data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +1 -1
  116. data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +1 -1
  117. data/lib/datadog/tracing/contrib/active_job/events/perform.rb +1 -1
  118. data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +1 -1
  119. data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +1 -1
  120. data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +1 -1
  121. data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +1 -1
  122. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +2 -1
  123. data/lib/datadog/tracing/contrib/active_support/cache/event.rb +32 -0
  124. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +156 -0
  125. data/lib/datadog/tracing/contrib/active_support/cache/events.rb +34 -0
  126. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +45 -41
  127. data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +17 -40
  128. data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +4 -1
  129. data/lib/datadog/tracing/contrib/active_support/notifications/event.rb +29 -6
  130. data/lib/datadog/tracing/contrib/active_support/notifications/subscriber.rb +16 -4
  131. data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +33 -29
  132. data/lib/datadog/tracing/contrib/analytics.rb +5 -0
  133. data/lib/datadog/tracing/contrib/ext.rb +14 -0
  134. data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +5 -0
  135. data/lib/datadog/tracing/contrib/graphql/patcher.rb +8 -2
  136. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +166 -0
  137. data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +28 -0
  138. data/lib/datadog/tracing/contrib/kafka/consumer_event.rb +1 -1
  139. data/lib/datadog/tracing/contrib/kafka/consumer_group_event.rb +1 -1
  140. data/lib/datadog/tracing/contrib/kafka/event.rb +1 -1
  141. data/lib/datadog/tracing/contrib/kafka/events/connection/request.rb +3 -3
  142. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_batch.rb +3 -3
  143. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_message.rb +3 -3
  144. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/heartbeat.rb +3 -3
  145. data/lib/datadog/tracing/contrib/kafka/events/produce_operation/send_messages.rb +3 -3
  146. data/lib/datadog/tracing/contrib/kafka/events/producer/deliver_messages.rb +3 -3
  147. data/lib/datadog/tracing/contrib/lograge/patcher.rb +16 -0
  148. data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +5 -0
  149. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +17 -13
  150. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +5 -0
  151. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +4 -1
  152. data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +28 -0
  153. data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +5 -1
  154. data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +22 -10
  155. data/lib/datadog/tracing/contrib/racecar/event.rb +2 -2
  156. data/lib/datadog/tracing/contrib/rails/ext.rb +9 -0
  157. data/lib/datadog/tracing/contrib/rails/patcher.rb +7 -0
  158. data/lib/datadog/tracing/contrib/rails/runner.rb +95 -0
  159. data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +5 -0
  160. data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +4 -1
  161. data/lib/datadog/tracing/diagnostics/environment_logger.rb +14 -16
  162. data/lib/datadog/tracing/distributed/b3_multi.rb +1 -1
  163. data/lib/datadog/tracing/distributed/b3_single.rb +3 -1
  164. data/lib/datadog/tracing/distributed/datadog.rb +2 -2
  165. data/lib/datadog/tracing/distributed/propagation.rb +9 -2
  166. data/lib/datadog/tracing/distributed/trace_context.rb +3 -2
  167. data/lib/datadog/tracing/metadata/errors.rb +9 -1
  168. data/lib/datadog/tracing/metadata/ext.rb +4 -0
  169. data/lib/datadog/tracing/pipeline/span_filter.rb +2 -2
  170. data/lib/datadog/tracing/span.rb +9 -2
  171. data/lib/datadog/tracing/span_event.rb +41 -0
  172. data/lib/datadog/tracing/span_operation.rb +9 -4
  173. data/lib/datadog/tracing/trace_operation.rb +7 -3
  174. data/lib/datadog/tracing/trace_segment.rb +4 -1
  175. data/lib/datadog/tracing/tracer.rb +9 -2
  176. data/lib/datadog/tracing/transport/serializable_trace.rb +3 -0
  177. data/lib/datadog/tracing.rb +5 -1
  178. data/lib/datadog/version.rb +2 -2
  179. metadata +43 -12
  180. data/lib/datadog/core/telemetry/client.rb +0 -95
  181. data/lib/datadog/core/telemetry/heartbeat.rb +0 -33
  182. data/lib/datadog/profiling/crashtracker.rb +0 -91
  183. data/lib/datadog/profiling/ext/forking.rb +0 -98
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'event'
4
+ require_relative 'metric'
5
+
6
+ module Datadog
7
+ module Core
8
+ module Telemetry
9
+ # MetricsCollection is a thread-safe collection of metrics per namespace
10
+ class MetricsCollection
11
+ attr_reader :namespace, :interval
12
+
13
+ def initialize(namespace, aggregation_interval:)
14
+ @namespace = namespace
15
+ @interval = aggregation_interval
16
+
17
+ @mutex = Mutex.new
18
+
19
+ @metrics = {}
20
+ @distributions = {}
21
+ end
22
+
23
+ def inc(metric_name, value, tags: {}, common: true)
24
+ metric = Metric::Count.new(metric_name, tags: tags, common: common)
25
+ fetch_or_add_metric(metric, value)
26
+ end
27
+
28
+ def dec(metric_name, value, tags: {}, common: true)
29
+ metric = Metric::Count.new(metric_name, tags: tags, common: common)
30
+ fetch_or_add_metric(metric, -value)
31
+ end
32
+
33
+ def gauge(metric_name, value, tags: {}, common: true)
34
+ metric = Metric::Gauge.new(metric_name, tags: tags, common: common, interval: @interval)
35
+ fetch_or_add_metric(metric, value)
36
+ end
37
+
38
+ def rate(metric_name, value, tags: {}, common: true)
39
+ metric = Metric::Rate.new(metric_name, tags: tags, common: common, interval: @interval)
40
+ fetch_or_add_metric(metric, value)
41
+ end
42
+
43
+ def distribution(metric_name, value, tags: {}, common: true)
44
+ metric = Metric::Distribution.new(metric_name, tags: tags, common: common)
45
+ fetch_or_add_distribution(metric, value)
46
+ end
47
+
48
+ def flush!
49
+ @mutex.synchronize do
50
+ events = []
51
+ events << Event::GenerateMetrics.new(@namespace, @metrics.values) if @metrics.any?
52
+ events << Event::Distributions.new(@namespace, @distributions.values) if @distributions.any?
53
+
54
+ @metrics = {}
55
+ @distributions = {}
56
+
57
+ events
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def fetch_or_add_metric(metric, value)
64
+ @mutex.synchronize do
65
+ m = (@metrics[metric.id] ||= metric)
66
+ m.track(value)
67
+ end
68
+ nil
69
+ end
70
+
71
+ def fetch_or_add_distribution(metric, value)
72
+ @mutex.synchronize do
73
+ m = (@distributions[metric.id] ||= metric)
74
+ m.track(value)
75
+ end
76
+ nil
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'metrics_collection'
4
+
5
+ module Datadog
6
+ module Core
7
+ module Telemetry
8
+ # MetricsManager aggregates and flushes metrics and distributions
9
+ class MetricsManager
10
+ attr_reader :enabled
11
+
12
+ def initialize(aggregation_interval:, enabled:)
13
+ @interval = aggregation_interval
14
+ @enabled = enabled
15
+ @mutex = Mutex.new
16
+
17
+ @collections = {}
18
+ end
19
+
20
+ def inc(namespace, metric_name, value, tags: {}, common: true)
21
+ return unless @enabled
22
+
23
+ # collection is thread-safe internally
24
+ collection = fetch_or_create_collection(namespace)
25
+ collection.inc(metric_name, value, tags: tags, common: common)
26
+ end
27
+
28
+ def dec(namespace, metric_name, value, tags: {}, common: true)
29
+ return unless @enabled
30
+
31
+ # collection is thread-safe internally
32
+ collection = fetch_or_create_collection(namespace)
33
+ collection.dec(metric_name, value, tags: tags, common: common)
34
+ end
35
+
36
+ def gauge(namespace, metric_name, value, tags: {}, common: true)
37
+ return unless @enabled
38
+
39
+ # collection is thread-safe internally
40
+ collection = fetch_or_create_collection(namespace)
41
+ collection.gauge(metric_name, value, tags: tags, common: common)
42
+ end
43
+
44
+ def rate(namespace, metric_name, value, tags: {}, common: true)
45
+ return unless @enabled
46
+
47
+ # collection is thread-safe internally
48
+ collection = fetch_or_create_collection(namespace)
49
+ collection.rate(metric_name, value, tags: tags, common: common)
50
+ end
51
+
52
+ def distribution(namespace, metric_name, value, tags: {}, common: true)
53
+ return unless @enabled
54
+
55
+ # collection is thread-safe internally
56
+ collection = fetch_or_create_collection(namespace)
57
+ collection.distribution(metric_name, value, tags: tags, common: common)
58
+ end
59
+
60
+ def flush!
61
+ return [] unless @enabled
62
+
63
+ collections = @mutex.synchronize { @collections.values }
64
+ collections.reduce([]) { |events, collection| events + collection.flush! }
65
+ end
66
+
67
+ def disable!
68
+ @enabled = false
69
+ end
70
+
71
+ private
72
+
73
+ def fetch_or_create_collection(namespace)
74
+ @mutex.synchronize do
75
+ @collections[namespace] ||= MetricsCollection.new(namespace, aggregation_interval: @interval)
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -17,7 +17,7 @@ module Datadog
17
17
  application: application,
18
18
  debug: false,
19
19
  host: host,
20
- payload: event.payload(seq_id),
20
+ payload: event.payload,
21
21
  request_type: event.type,
22
22
  runtime_id: Core::Environment::Identity.id,
23
23
  seq_id: seq_id,
@@ -0,0 +1,173 @@
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
+ metrics_aggregation_interval_seconds:,
25
+ emitter:,
26
+ metrics_manager:,
27
+ dependency_collection:,
28
+ enabled: true,
29
+ shutdown_timeout: Workers::Polling::DEFAULT_SHUTDOWN_TIMEOUT,
30
+ buffer_size: DEFAULT_BUFFER_MAX_SIZE
31
+ )
32
+ @emitter = emitter
33
+ @metrics_manager = metrics_manager
34
+ @dependency_collection = dependency_collection
35
+
36
+ @ticks_per_heartbeat = (heartbeat_interval_seconds / metrics_aggregation_interval_seconds).to_i
37
+ @current_ticks = 0
38
+
39
+ # Workers::Polling settings
40
+ self.enabled = enabled
41
+ # Workers::IntervalLoop settings
42
+ self.loop_base_interval = metrics_aggregation_interval_seconds
43
+ self.fork_policy = Core::Workers::Async::Thread::FORK_POLICY_STOP
44
+
45
+ @shutdown_timeout = shutdown_timeout
46
+ @buffer_size = buffer_size
47
+
48
+ self.buffer = buffer_klass.new(@buffer_size)
49
+ end
50
+
51
+ def start
52
+ return if !enabled? || forked?
53
+
54
+ # starts async worker
55
+ perform
56
+ end
57
+
58
+ def stop(force_stop = false, timeout = @shutdown_timeout)
59
+ buffer.close if running?
60
+
61
+ super
62
+ end
63
+
64
+ def enqueue(event)
65
+ return if !enabled? || forked?
66
+
67
+ buffer.push(event)
68
+ end
69
+
70
+ def sent_started_event?
71
+ TELEMETRY_STARTED_ONCE.success?
72
+ end
73
+
74
+ def failed_to_start?
75
+ TELEMETRY_STARTED_ONCE.failed?
76
+ end
77
+
78
+ private
79
+
80
+ def perform(*events)
81
+ return if !enabled? || forked?
82
+
83
+ started! unless sent_started_event?
84
+
85
+ metric_events = @metrics_manager.flush!
86
+ events = [] if events.nil?
87
+ flush_events(events + metric_events)
88
+
89
+ @current_ticks += 1
90
+ return if @current_ticks < @ticks_per_heartbeat
91
+
92
+ @current_ticks = 0
93
+ heartbeat!
94
+ end
95
+
96
+ def flush_events(events)
97
+ return if events.empty?
98
+ return if !enabled? || !sent_started_event?
99
+
100
+ Datadog.logger.debug { "Sending #{events&.count} telemetry events" }
101
+ send_event(Event::MessageBatch.new(events))
102
+ end
103
+
104
+ def heartbeat!
105
+ return if !enabled? || !sent_started_event?
106
+
107
+ send_event(Event::AppHeartbeat.new)
108
+ end
109
+
110
+ def started!
111
+ return unless enabled?
112
+
113
+ if failed_to_start?
114
+ Datadog.logger.debug('Telemetry app-started event exhausted retries, disabling telemetry worker')
115
+ disable!
116
+ return
117
+ end
118
+
119
+ TELEMETRY_STARTED_ONCE.run do
120
+ res = send_event(Event::AppStarted.new)
121
+
122
+ if res.ok?
123
+ Datadog.logger.debug('Telemetry app-started event is successfully sent')
124
+
125
+ send_event(Event::AppDependenciesLoaded.new) if @dependency_collection
126
+
127
+ true
128
+ else
129
+ Datadog.logger.debug('Error sending telemetry app-started event, retry after heartbeat interval...')
130
+ false
131
+ end
132
+ end
133
+ end
134
+
135
+ def send_event(event)
136
+ res = @emitter.request(event)
137
+
138
+ disable_on_not_found!(res)
139
+
140
+ res
141
+ end
142
+
143
+ def dequeue
144
+ buffer.pop
145
+ end
146
+
147
+ def work_pending?
148
+ run_loop? || !buffer.empty?
149
+ end
150
+
151
+ def buffer_klass
152
+ if Core::Environment::Ext::RUBY_ENGINE == 'ruby'
153
+ Core::Buffer::CRuby
154
+ else
155
+ Core::Buffer::ThreadSafe
156
+ end
157
+ end
158
+
159
+ def disable!
160
+ self.enabled = false
161
+ @metrics_manager.disable!
162
+ end
163
+
164
+ def disable_on_not_found!(response)
165
+ return unless response.not_found?
166
+
167
+ Datadog.logger.debug('Agent does not support telemetry; disabling future telemetry events.')
168
+ disable!
169
+ end
170
+ end
171
+ end
172
+ end
173
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module Core
5
+ module Utils
6
+ # Monkey patches `Kernel#fork` and similar functions, adding an `at_fork` callback mechanism which
7
+ # is used to restart observability after the VM forks (e.g. in multiprocess Ruby apps).
8
+ module AtForkMonkeyPatch
9
+ AT_FORK_CHILD_BLOCKS = [] # rubocop:disable Style/MutableConstant Used to store blocks to run, mutable by design.
10
+ private_constant :AT_FORK_CHILD_BLOCKS
11
+
12
+ def self.supported?
13
+ Process.respond_to?(:fork)
14
+ end
15
+
16
+ def self.apply!
17
+ return false unless supported?
18
+
19
+ if RUBY_VERSION < '3.1'
20
+ [
21
+ ::Process.singleton_class, # Process.fork
22
+ ::Kernel.singleton_class, # Kernel.fork
23
+ ::Object, # fork without explicit receiver (it's defined as a method in ::Kernel)
24
+ # Note: Modifying Object as we do here is irreversible. During tests, this
25
+ # change will stick around even if we otherwise stub `Process` and `Kernel`
26
+ ].each { |target| target.prepend(KernelMonkeyPatch) }
27
+ end
28
+
29
+ ::Process.singleton_class.prepend(ProcessMonkeyPatch)
30
+
31
+ true
32
+ end
33
+
34
+ def self.run_at_fork_blocks(stage)
35
+ raise(ArgumentError, "Unsupported stage #{stage}") unless stage == :child
36
+
37
+ AT_FORK_CHILD_BLOCKS.each(&:call)
38
+ end
39
+
40
+ def self.at_fork(stage, &block)
41
+ raise(ArgumentError, "Unsupported stage #{stage}") unless stage == :child
42
+ raise(ArgumentError, 'Missing block argument') unless block
43
+
44
+ AT_FORK_CHILD_BLOCKS << block
45
+
46
+ true
47
+ end
48
+
49
+ # Adds `at_fork` behavior; see parent module for details.
50
+ module KernelMonkeyPatch
51
+ def fork
52
+ # If a block is provided, it must be wrapped to trigger callbacks.
53
+ child_block = if block_given?
54
+ proc do
55
+ AtForkMonkeyPatch.run_at_fork_blocks(:child)
56
+
57
+ # Invoke original block
58
+ yield
59
+ end
60
+ end
61
+
62
+ # Start fork
63
+ # If a block is provided, use the wrapped version.
64
+ result = child_block.nil? ? super : super(&child_block)
65
+
66
+ # When fork gets called without a block, it returns twice:
67
+ # If we're in the fork, result = nil: trigger child callbacks.
68
+ # If we're in the parent, result = pid: we do nothing.
69
+ # (If it gets called with a block, it only returns on the parent)
70
+ AtForkMonkeyPatch.run_at_fork_blocks(:child) if result.nil?
71
+
72
+ result
73
+ end
74
+ end
75
+
76
+ # Adds `at_fork` behavior; see parent module for details.
77
+ module ProcessMonkeyPatch
78
+ # Hook provided by Ruby 3.1+ for observability libraries that want to know about fork, see
79
+ # https://github.com/ruby/ruby/pull/5017 and https://bugs.ruby-lang.org/issues/17795
80
+ def _fork
81
+ pid = super
82
+
83
+ AtForkMonkeyPatch.run_at_fork_blocks(:child) if pid == 0
84
+
85
+ pid
86
+ end
87
+
88
+ # A call to Process.daemon ( https://rubyapi.org/3.1/o/process#method-c-daemon ) forks the current process and
89
+ # keeps executing code in the child process, killing off the parent, thus effectively replacing it.
90
+ # This is not covered by `_fork` and thus we have some extra code for it.
91
+ def daemon(*args)
92
+ result = super
93
+
94
+ AtForkMonkeyPatch.run_at_fork_blocks(:child)
95
+
96
+ result
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ 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.positive?
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
data/lib/datadog/core.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'core/deprecations'
3
4
  require_relative 'core/extensions'
4
5
 
5
6
  # We must load core extensions to make certain global APIs
@@ -9,25 +10,7 @@ module Datadog
9
10
  # products. It is a dependency of each product. Contrast with Datadog::Kit
10
11
  # for higher-level features.
11
12
  module Core
12
- class << self
13
- # Records the occurrence of a deprecated operation in this library.
14
- #
15
- # Currently, these operations are logged to `Datadog.logger` at `warn` level.
16
- #
17
- # `disallowed_next_major` adds a message informing that the deprecated operation
18
- # won't be allowed in the next major release.
19
- #
20
- # @yieldreturn [String] a String with the lazily evaluated deprecation message.
21
- # @param [Boolean] disallowed_next_major whether this deprecation will be enforced in the next major release.
22
- def log_deprecation(disallowed_next_major: true)
23
- Datadog.logger.warn do
24
- message = yield
25
- message += ' This will be enforced in the next major release.' if disallowed_next_major
26
- message
27
- end
28
- nil
29
- end
30
- end
13
+ extend Core::Deprecations
31
14
  end
32
15
 
33
16
  extend Core::Extensions
@@ -53,13 +53,11 @@ module Datadog
53
53
  # @param user_exists [bool] Whether the user id that did a login attempt exists.
54
54
  # @param others [Hash<String || Symbol, String>] Additional free-form
55
55
  # event information to attach to the trace.
56
- def track_login_failure(trace = nil, span = nil, user_id:, user_exists:, **others)
56
+ def track_login_failure(trace = nil, span = nil, user_exists:, user_id: nil, **others)
57
57
  set_trace_and_span_context('track_login_failure', trace, span) do |active_trace, active_span|
58
- raise ArgumentError, 'user_id cannot be nil' if user_id.nil?
59
-
60
58
  track(LOGIN_FAILURE_EVENT, active_trace, active_span, **others)
61
59
 
62
- active_span.set_tag('appsec.events.users.login.failure.usr.id', user_id)
60
+ active_span.set_tag('appsec.events.users.login.failure.usr.id', user_id) if user_id
63
61
  active_span.set_tag('appsec.events.users.login.failure.usr.exists', user_exists)
64
62
  end
65
63
  end
@@ -41,8 +41,11 @@ module Datadog
41
41
  digest = @datadog_propagator.extract(carrier)
42
42
  return context unless digest
43
43
 
44
- trace_id = to_otel_id(digest.trace_id)
45
- span_id = to_otel_id(digest.span_id)
44
+ # Converts the {Numeric} Datadog id object to OpenTelemetry's byte array format.
45
+ # 128-bit unsigned, big-endian integer
46
+ trace_id = [digest.trace_id >> 64, digest.trace_id & 0xFFFFFFFFFFFFFFFF].pack('Q>Q>')
47
+ # 64-bit unsigned, big-endian integer
48
+ span_id = [digest.span_id].pack('Q>')
46
49
 
47
50
  if digest.trace_state || digest.trace_flags
48
51
  trace_flags = ::OpenTelemetry::Trace::TraceFlags.from_byte(digest.trace_flags)
@@ -78,14 +81,6 @@ module Datadog
78
81
  def fields
79
82
  []
80
83
  end
81
-
82
- private
83
-
84
- # Converts the {Numeric} Datadog id object to OpenTelemetry's byte array format.
85
- # This method currently converts an unsigned 64-bit Integer to a binary String.
86
- def to_otel_id(dd_id)
87
- Array(dd_id).pack('Q')
88
- end
89
84
  end
90
85
  end
91
86
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative 'trace/span'
4
4
  require_relative '../../tracing/span_link'
5
+ require_relative '../../tracing/span_event'
5
6
  require_relative '../../tracing/trace_digest'
6
7
 
7
8
  module Datadog
@@ -31,6 +32,15 @@ module Datadog
31
32
  #
32
33
  # @param [Span] span the {Span} that just ended.
33
34
  def on_finish(span)
35
+ unless span.events.nil?
36
+ span.datadog_span.span_events = span.events.map do |event|
37
+ Datadog::Tracing::SpanEvent.new(
38
+ event.name,
39
+ attributes: event.attributes,
40
+ time_unix_nano: event.timestamp
41
+ )
42
+ end
43
+ end
34
44
  span.datadog_span.finish(ns_to_time(span.end_timestamp))
35
45
  end
36
46
 
@@ -70,7 +80,8 @@ module Datadog
70
80
  if parent_context.trace
71
81
  Tracing.send(:tracer).send(:call_context).activate!(parent_context.ensure_trace)
72
82
  else
73
- Tracing.continue_trace!(nil)
83
+ otel_trace_id = span.context.hex_trace_id.to_i(16)
84
+ Tracing.continue_trace!(Datadog::Tracing::TraceDigest.new(trace_id: otel_trace_id, span_remote: false))
74
85
  end
75
86
 
76
87
  datadog_span = start_datadog_span(span)
@@ -85,7 +96,6 @@ module Datadog
85
96
  name, kwargs = span_arguments(span, attributes)
86
97
 
87
98
  datadog_span = Tracing.trace(name, **kwargs)
88
-
89
99
  datadog_span.set_error([nil, span.status.description]) unless span.status.ok?
90
100
  datadog_span.set_tags(span.attributes)
91
101
 
@@ -143,6 +153,9 @@ module Datadog
143
153
 
144
154
  kwargs[:tags] = attributes.to_h
145
155
 
156
+ # DEV: The datadog span must have the same ID as the OpenTelemetry span
157
+ kwargs[:id] = span.context.hex_span_id.to_i(16)
158
+
146
159
  [name, kwargs]
147
160
  end
148
161
 
@@ -15,6 +15,29 @@ module Datadog
15
15
  res
16
16
  end
17
17
 
18
+ # Record an exception during the execution of this span. Multiple exceptions
19
+ # can be recorded on a span.
20
+ #
21
+ # @param [Exception] exception The exception to recorded
22
+ # @param [optional Hash{String => String, Numeric, Boolean, Array<String, Numeric, Boolean>}]
23
+ # attributes One or more key:value pairs, where the keys must be
24
+ # strings and the values may be (array of) string, boolean or numeric
25
+ # type.
26
+ #
27
+ # @return [void]
28
+ def record_exception(exception, attributes: nil)
29
+ res = super
30
+ if (span = datadog_span)
31
+ # Sets the exception attributes as span error tags. The values in the attribute hash MUST
32
+ # take precedence over the type, message and stacktrace inferred from the exception object
33
+ type = attributes&.[]('exception.type') || exception.class.to_s
34
+ message = attributes&.[]('exception.message') || exception.message
35
+ stacktrace = attributes&.[]('exception.stacktrace') || exception.full_message(highlight: false, order: :top)
36
+ span.set_error_tags([type, message, stacktrace])
37
+ end
38
+ res
39
+ end
40
+
18
41
  # `alias` performed to match {OpenTelemetry::SDK::Trace::Span} aliasing upstream
19
42
  alias []= set_attribute
20
43