datadog 2.0.0 → 2.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +66 -2
- data/README.md +1 -1
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +19 -1
- data/ext/datadog_profiling_native_extension/collectors_stack.c +41 -0
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +1 -1
- data/ext/datadog_profiling_native_extension/crashtracker.c +1 -1
- data/ext/datadog_profiling_native_extension/extconf.rb +6 -4
- data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +47 -1
- data/ext/datadog_profiling_native_extension/setup_signal_handler.c +1 -1
- data/ext/datadog_profiling_native_extension/stack_recorder.c +13 -6
- data/ext/datadog_profiling_native_extension/stack_recorder.h +1 -0
- data/lib/datadog/appsec/configuration/settings.rb +5 -0
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +0 -1
- data/lib/datadog/appsec/contrib/sinatra/patcher.rb +1 -1
- data/lib/datadog/appsec/extensions.rb +1 -0
- data/lib/datadog/core/configuration/components.rb +6 -3
- data/lib/datadog/core/configuration/ext.rb +1 -0
- data/lib/datadog/core/configuration/option.rb +21 -14
- data/lib/datadog/core/configuration/options.rb +5 -1
- data/lib/datadog/core/configuration/settings.rb +68 -5
- data/lib/datadog/core/configuration.rb +3 -17
- data/lib/datadog/core/deprecations.rb +58 -0
- data/lib/datadog/core/environment/ext.rb +2 -0
- data/lib/datadog/core/environment/yjit.rb +5 -0
- data/lib/datadog/core/runtime/ext.rb +2 -0
- data/lib/datadog/core/runtime/metrics.rb +6 -0
- data/lib/datadog/core/telemetry/component.rb +107 -0
- data/lib/datadog/core/telemetry/event.rb +124 -31
- data/lib/datadog/core/telemetry/ext.rb +2 -0
- data/lib/datadog/core/telemetry/http/adapters/net.rb +1 -1
- data/lib/datadog/core/telemetry/metric.rb +167 -0
- data/lib/datadog/core/telemetry/metrics_collection.rb +81 -0
- data/lib/datadog/core/telemetry/metrics_manager.rb +81 -0
- data/lib/datadog/core/telemetry/request.rb +1 -1
- data/lib/datadog/core/telemetry/worker.rb +173 -0
- data/lib/datadog/core/utils/only_once_successful.rb +76 -0
- data/lib/datadog/core.rb +2 -19
- data/lib/datadog/opentelemetry/sdk/propagator.rb +5 -10
- data/lib/datadog/opentelemetry/sdk/span_processor.rb +5 -2
- data/lib/datadog/profiling/collectors/code_provenance.rb +18 -5
- data/lib/datadog/profiling/component.rb +18 -1
- data/lib/datadog/profiling/ext/dir_monkey_patches.rb +410 -0
- data/lib/datadog/profiling.rb +1 -0
- data/lib/datadog/tracing/configuration/ext.rb +7 -0
- data/lib/datadog/tracing/configuration/settings.rb +52 -3
- data/lib/datadog/tracing/contrib/action_cable/event.rb +1 -1
- data/lib/datadog/tracing/contrib/action_cable/events/broadcast.rb +1 -1
- data/lib/datadog/tracing/contrib/action_cable/events/perform_action.rb +1 -1
- data/lib/datadog/tracing/contrib/action_cable/events/transmit.rb +1 -1
- data/lib/datadog/tracing/contrib/action_mailer/event.rb +4 -6
- data/lib/datadog/tracing/contrib/action_mailer/events/deliver.rb +9 -4
- data/lib/datadog/tracing/contrib/action_mailer/events/process.rb +3 -2
- data/lib/datadog/tracing/contrib/action_view/events/render_partial.rb +1 -5
- data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/discard.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/perform.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +1 -1
- data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +1 -1
- data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +1 -1
- data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +1 -1
- data/lib/datadog/tracing/contrib/active_record/events/sql.rb +1 -1
- data/lib/datadog/tracing/contrib/active_support/cache/event.rb +32 -0
- data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +156 -0
- data/lib/datadog/tracing/contrib/active_support/cache/events.rb +34 -0
- data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +45 -41
- data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +17 -40
- data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +4 -1
- data/lib/datadog/tracing/contrib/active_support/notifications/event.rb +29 -6
- data/lib/datadog/tracing/contrib/active_support/notifications/subscriber.rb +16 -4
- data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +33 -29
- data/lib/datadog/tracing/contrib/analytics.rb +5 -0
- data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +5 -0
- data/lib/datadog/tracing/contrib/graphql/patcher.rb +8 -2
- data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +166 -0
- data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +25 -0
- data/lib/datadog/tracing/contrib/kafka/consumer_event.rb +1 -1
- data/lib/datadog/tracing/contrib/kafka/consumer_group_event.rb +1 -1
- data/lib/datadog/tracing/contrib/kafka/event.rb +1 -1
- data/lib/datadog/tracing/contrib/kafka/events/connection/request.rb +3 -3
- data/lib/datadog/tracing/contrib/kafka/events/consumer/process_batch.rb +3 -3
- data/lib/datadog/tracing/contrib/kafka/events/consumer/process_message.rb +3 -3
- data/lib/datadog/tracing/contrib/kafka/events/consumer_group/heartbeat.rb +3 -3
- data/lib/datadog/tracing/contrib/kafka/events/produce_operation/send_messages.rb +3 -3
- data/lib/datadog/tracing/contrib/kafka/events/producer/deliver_messages.rb +3 -3
- data/lib/datadog/tracing/contrib/racecar/event.rb +2 -2
- data/lib/datadog/tracing/contrib/rails/ext.rb +9 -0
- data/lib/datadog/tracing/contrib/rails/patcher.rb +7 -0
- data/lib/datadog/tracing/contrib/rails/runner.rb +95 -0
- data/lib/datadog/tracing/distributed/b3_multi.rb +1 -1
- data/lib/datadog/tracing/distributed/b3_single.rb +3 -1
- data/lib/datadog/tracing/distributed/datadog.rb +2 -2
- data/lib/datadog/tracing/distributed/propagation.rb +39 -4
- data/lib/datadog/tracing/distributed/trace_context.rb +5 -3
- data/lib/datadog/tracing/metadata/ext.rb +1 -0
- data/lib/datadog/tracing/span_operation.rb +3 -2
- data/lib/datadog/tracing/trace_operation.rb +7 -3
- data/lib/datadog/tracing/trace_segment.rb +4 -1
- data/lib/datadog/tracing/tracer.rb +9 -2
- data/lib/datadog/tracing.rb +5 -1
- data/lib/datadog/version.rb +2 -2
- metadata +21 -8
- data/lib/datadog/core/telemetry/client.rb +0 -95
- data/lib/datadog/core/telemetry/heartbeat.rb +0 -33
@@ -0,0 +1,167 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
module Core
|
5
|
+
module Telemetry
|
6
|
+
# Telemetry metrics data model (internal Datadog metrics for client libraries)
|
7
|
+
module Metric
|
8
|
+
# Base class for all metric types
|
9
|
+
class Base
|
10
|
+
attr_reader :name, :tags, :values, :common
|
11
|
+
|
12
|
+
# @param name [String] metric name
|
13
|
+
# @param tags [Array<String>|Hash{String=>String}] metric tags as hash or array of "tag:val" strings
|
14
|
+
# @param common [Boolean] true if the metric is common for all languages, false for Ruby-specific metric
|
15
|
+
def initialize(name, tags: {}, common: true)
|
16
|
+
@name = name
|
17
|
+
@values = []
|
18
|
+
@tags = tags_to_array(tags)
|
19
|
+
@common = common
|
20
|
+
end
|
21
|
+
|
22
|
+
def id
|
23
|
+
@id ||= "#{type}::#{name}::#{tags.join(',')}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def track(value)
|
27
|
+
raise NotImplementedError, 'method must be implemented in subclasses'
|
28
|
+
end
|
29
|
+
|
30
|
+
def type
|
31
|
+
raise NotImplementedError, 'method must be implemented in subclasses'
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_h
|
35
|
+
{
|
36
|
+
metric: name,
|
37
|
+
points: values,
|
38
|
+
type: type,
|
39
|
+
tags: tags,
|
40
|
+
common: common
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def tags_to_array(tags)
|
47
|
+
return tags if tags.is_a?(Array)
|
48
|
+
|
49
|
+
tags.map { |k, v| "#{k}:#{v}" }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Base class for metrics that require aggregation interval
|
54
|
+
class IntervalMetric < Base
|
55
|
+
attr_reader :interval
|
56
|
+
|
57
|
+
# @param name [String] metric name
|
58
|
+
# @param tags [Array<String>|Hash{String=>String}] metric tags as hash of array of "tag:val" strings
|
59
|
+
# @param common [Boolean] true if the metric is common for all languages, false for Ruby-specific metric
|
60
|
+
# @param interval [Integer] metrics aggregation interval in seconds
|
61
|
+
def initialize(name, interval:, tags: {}, common: true)
|
62
|
+
raise ArgumentError, 'interval must be a positive number' if interval.nil? || interval <= 0
|
63
|
+
|
64
|
+
super(name, tags: tags, common: common)
|
65
|
+
|
66
|
+
@interval = interval
|
67
|
+
end
|
68
|
+
|
69
|
+
def to_h
|
70
|
+
res = super
|
71
|
+
res[:interval] = interval
|
72
|
+
res
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Count metric adds up all the submitted values in a time interval. This would be suitable for a
|
77
|
+
# metric tracking the number of website hits, for instance.
|
78
|
+
class Count < Base
|
79
|
+
TYPE = 'count'
|
80
|
+
|
81
|
+
def type
|
82
|
+
TYPE
|
83
|
+
end
|
84
|
+
|
85
|
+
def track(value)
|
86
|
+
value = value.to_i
|
87
|
+
|
88
|
+
if values.empty?
|
89
|
+
values << [Time.now.to_i, value]
|
90
|
+
else
|
91
|
+
values[0][0] = Time.now.to_i
|
92
|
+
values[0][1] += value
|
93
|
+
end
|
94
|
+
nil
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# A gauge type takes the last value reported during the interval. This type would make sense for tracking RAM or
|
99
|
+
# CPU usage, where taking the last value provides a representative picture of the host’s behavior during the time
|
100
|
+
# interval.
|
101
|
+
class Gauge < IntervalMetric
|
102
|
+
TYPE = 'gauge'
|
103
|
+
|
104
|
+
def type
|
105
|
+
TYPE
|
106
|
+
end
|
107
|
+
|
108
|
+
def track(value)
|
109
|
+
if values.empty?
|
110
|
+
values << [Time.now.to_i, value]
|
111
|
+
else
|
112
|
+
values[0][0] = Time.now.to_i
|
113
|
+
values[0][1] = value
|
114
|
+
end
|
115
|
+
nil
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# The rate type takes the count and divides it by the length of the time interval. This is useful if you’re
|
120
|
+
# interested in the number of hits per second.
|
121
|
+
class Rate < IntervalMetric
|
122
|
+
TYPE = 'rate'
|
123
|
+
|
124
|
+
def initialize(name, interval:, tags: {}, common: true)
|
125
|
+
super
|
126
|
+
|
127
|
+
@value = 0.0
|
128
|
+
end
|
129
|
+
|
130
|
+
def type
|
131
|
+
TYPE
|
132
|
+
end
|
133
|
+
|
134
|
+
def track(value = 1.0)
|
135
|
+
@value += value
|
136
|
+
@values = [[Time.now.to_i, @value / interval]]
|
137
|
+
nil
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Distribution metric represents the global statistical distribution of a set of values.
|
142
|
+
class Distribution < Base
|
143
|
+
TYPE = 'distributions'
|
144
|
+
|
145
|
+
def type
|
146
|
+
TYPE
|
147
|
+
end
|
148
|
+
|
149
|
+
def track(value)
|
150
|
+
values << value
|
151
|
+
nil
|
152
|
+
end
|
153
|
+
|
154
|
+
# distribution metric data does not have type field
|
155
|
+
def to_h
|
156
|
+
{
|
157
|
+
metric: name,
|
158
|
+
points: values,
|
159
|
+
tags: tags,
|
160
|
+
common: common
|
161
|
+
}
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -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
|
@@ -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,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
|
-
|
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
|
@@ -41,8 +41,11 @@ module Datadog
|
|
41
41
|
digest = @datadog_propagator.extract(carrier)
|
42
42
|
return context unless digest
|
43
43
|
|
44
|
-
|
45
|
-
|
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
|
@@ -70,7 +70,8 @@ module Datadog
|
|
70
70
|
if parent_context.trace
|
71
71
|
Tracing.send(:tracer).send(:call_context).activate!(parent_context.ensure_trace)
|
72
72
|
else
|
73
|
-
|
73
|
+
otel_trace_id = span.context.hex_trace_id.to_i(16)
|
74
|
+
Tracing.continue_trace!(Datadog::Tracing::TraceDigest.new(trace_id: otel_trace_id, span_remote: false))
|
74
75
|
end
|
75
76
|
|
76
77
|
datadog_span = start_datadog_span(span)
|
@@ -85,7 +86,6 @@ module Datadog
|
|
85
86
|
name, kwargs = span_arguments(span, attributes)
|
86
87
|
|
87
88
|
datadog_span = Tracing.trace(name, **kwargs)
|
88
|
-
|
89
89
|
datadog_span.set_error([nil, span.status.description]) unless span.status.ok?
|
90
90
|
datadog_span.set_tags(span.attributes)
|
91
91
|
|
@@ -143,6 +143,9 @@ module Datadog
|
|
143
143
|
|
144
144
|
kwargs[:tags] = attributes.to_h
|
145
145
|
|
146
|
+
# DEV: The datadog span must have the same ID as the OpenTelemetry span
|
147
|
+
kwargs[:id] = span.context.hex_span_id.to_i(16)
|
148
|
+
|
146
149
|
[name, kwargs]
|
147
150
|
end
|
148
151
|
|