datadog 2.1.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 +53 -2
- 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/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/settings.rb +39 -0
- data/lib/datadog/core/configuration.rb +3 -17
- data/lib/datadog/core/deprecations.rb +58 -0
- data/lib/datadog/core/environment/yjit.rb +5 -0
- data/lib/datadog/core/runtime/ext.rb +1 -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 +100 -25
- 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/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 +9 -2
- data/lib/datadog/tracing/distributed/trace_context.rb +3 -2
- 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 +22 -9
- data/lib/datadog/core/telemetry/client.rb +0 -95
- data/lib/datadog/core/telemetry/heartbeat.rb +0 -33
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'emitter'
|
4
|
+
require_relative 'event'
|
5
|
+
require_relative 'metrics_manager'
|
6
|
+
require_relative 'worker'
|
7
|
+
require_relative '../utils/forking'
|
8
|
+
|
9
|
+
module Datadog
|
10
|
+
module Core
|
11
|
+
module Telemetry
|
12
|
+
# Telemetry entrypoint, coordinates sending telemetry events at various points in app lifecycle.
|
13
|
+
class Component
|
14
|
+
attr_reader :enabled
|
15
|
+
|
16
|
+
include Core::Utils::Forking
|
17
|
+
|
18
|
+
# @param enabled [Boolean] Determines whether telemetry events should be sent to the API
|
19
|
+
# @param metrics_enabled [Boolean] Determines whether telemetry metrics should be sent to the API
|
20
|
+
# @param heartbeat_interval_seconds [Float] How frequently heartbeats will be reported, in seconds.
|
21
|
+
# @param metrics_aggregation_interval_seconds [Float] How frequently metrics will be aggregated, in seconds.
|
22
|
+
# @param [Boolean] dependency_collection Whether to send the `app-dependencies-loaded` event
|
23
|
+
def initialize(
|
24
|
+
heartbeat_interval_seconds:,
|
25
|
+
metrics_aggregation_interval_seconds:,
|
26
|
+
dependency_collection:,
|
27
|
+
enabled: true,
|
28
|
+
metrics_enabled: true
|
29
|
+
)
|
30
|
+
@enabled = enabled
|
31
|
+
@stopped = false
|
32
|
+
|
33
|
+
@metrics_manager = MetricsManager.new(
|
34
|
+
enabled: enabled && metrics_enabled,
|
35
|
+
aggregation_interval: metrics_aggregation_interval_seconds
|
36
|
+
)
|
37
|
+
|
38
|
+
@worker = Telemetry::Worker.new(
|
39
|
+
enabled: @enabled,
|
40
|
+
heartbeat_interval_seconds: heartbeat_interval_seconds,
|
41
|
+
metrics_aggregation_interval_seconds: metrics_aggregation_interval_seconds,
|
42
|
+
emitter: Emitter.new,
|
43
|
+
metrics_manager: @metrics_manager,
|
44
|
+
dependency_collection: dependency_collection
|
45
|
+
)
|
46
|
+
@worker.start
|
47
|
+
end
|
48
|
+
|
49
|
+
def disable!
|
50
|
+
@enabled = false
|
51
|
+
@worker.enabled = false
|
52
|
+
end
|
53
|
+
|
54
|
+
def stop!
|
55
|
+
return if @stopped
|
56
|
+
|
57
|
+
@worker.stop(true)
|
58
|
+
@stopped = true
|
59
|
+
end
|
60
|
+
|
61
|
+
def emit_closing!
|
62
|
+
return if !@enabled || forked?
|
63
|
+
|
64
|
+
@worker.enqueue(Event::AppClosing.new)
|
65
|
+
end
|
66
|
+
|
67
|
+
def integrations_change!
|
68
|
+
return if !@enabled || forked?
|
69
|
+
|
70
|
+
@worker.enqueue(Event::AppIntegrationsChange.new)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Report configuration changes caused by Remote Configuration.
|
74
|
+
def client_configuration_change!(changes)
|
75
|
+
return if !@enabled || forked?
|
76
|
+
|
77
|
+
@worker.enqueue(Event::AppClientConfigurationChange.new(changes, 'remote_config'))
|
78
|
+
end
|
79
|
+
|
80
|
+
# Increments a count metric.
|
81
|
+
def inc(namespace, metric_name, value, tags: {}, common: true)
|
82
|
+
@metrics_manager.inc(namespace, metric_name, value, tags: tags, common: common)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Decremenets a count metric.
|
86
|
+
def dec(namespace, metric_name, value, tags: {}, common: true)
|
87
|
+
@metrics_manager.dec(namespace, metric_name, value, tags: tags, common: common)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Tracks gauge metric.
|
91
|
+
def gauge(namespace, metric_name, value, tags: {}, common: true)
|
92
|
+
@metrics_manager.gauge(namespace, metric_name, value, tags: tags, common: common)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Tracks rate metric.
|
96
|
+
def rate(namespace, metric_name, value, tags: {}, common: true)
|
97
|
+
@metrics_manager.rate(namespace, metric_name, value, tags: tags, common: common)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Tracks distribution metric.
|
101
|
+
def distribution(namespace, metric_name, value, tags: {}, common: true)
|
102
|
+
@metrics_manager.distribution(namespace, metric_name, value, tags: tags, common: common)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -3,7 +3,16 @@
|
|
3
3
|
module Datadog
|
4
4
|
module Core
|
5
5
|
module Telemetry
|
6
|
+
# Collection of telemetry events
|
6
7
|
class Event
|
8
|
+
extend Core::Utils::Forking
|
9
|
+
|
10
|
+
# returns sequence that increments every time the configuration changes
|
11
|
+
def self.configuration_sequence
|
12
|
+
after_fork! { @sequence = Datadog::Core::Utils::Sequence.new(1) }
|
13
|
+
@sequence ||= Datadog::Core::Utils::Sequence.new(1)
|
14
|
+
end
|
15
|
+
|
7
16
|
# Base class for all Telemetry V2 events.
|
8
17
|
class Base
|
9
18
|
# The type of the event.
|
@@ -12,8 +21,7 @@ module Datadog
|
|
12
21
|
def type; end
|
13
22
|
|
14
23
|
# The JSON payload for the event.
|
15
|
-
|
16
|
-
def payload(seq_id)
|
24
|
+
def payload
|
17
25
|
{}
|
18
26
|
end
|
19
27
|
end
|
@@ -24,8 +32,7 @@ module Datadog
|
|
24
32
|
'app-started'
|
25
33
|
end
|
26
34
|
|
27
|
-
def payload
|
28
|
-
@seq_id = seq_id
|
35
|
+
def payload
|
29
36
|
{
|
30
37
|
products: products,
|
31
38
|
configuration: configuration,
|
@@ -38,6 +45,7 @@ module Datadog
|
|
38
45
|
private
|
39
46
|
|
40
47
|
def products
|
48
|
+
# @type var products: Hash[Symbol, Hash[Symbol, Object]]
|
41
49
|
products = {
|
42
50
|
appsec: {
|
43
51
|
enabled: Datadog::AppSec.enabled?,
|
@@ -79,16 +87,19 @@ module Datadog
|
|
79
87
|
].freeze
|
80
88
|
|
81
89
|
# rubocop:disable Metrics/AbcSize
|
90
|
+
# rubocop:disable Metrics/MethodLength
|
82
91
|
def configuration
|
83
92
|
config = Datadog.configuration
|
93
|
+
seq_id = Event.configuration_sequence.next
|
84
94
|
|
85
95
|
list = [
|
86
|
-
conf_value('DD_AGENT_HOST', config.agent.host),
|
87
|
-
conf_value('DD_AGENT_TRANSPORT', agent_transport(config)),
|
88
|
-
conf_value('DD_TRACE_SAMPLE_RATE', to_value(config.tracing.sampling.default_rate)),
|
96
|
+
conf_value('DD_AGENT_HOST', config.agent.host, seq_id),
|
97
|
+
conf_value('DD_AGENT_TRANSPORT', agent_transport(config), seq_id),
|
98
|
+
conf_value('DD_TRACE_SAMPLE_RATE', to_value(config.tracing.sampling.default_rate), seq_id),
|
89
99
|
conf_value(
|
90
100
|
'DD_TRACE_REMOVE_INTEGRATION_SERVICE_NAMES_ENABLED',
|
91
|
-
config.tracing.contrib.global_default_service_name.enabled
|
101
|
+
config.tracing.contrib.global_default_service_name.enabled,
|
102
|
+
seq_id
|
92
103
|
),
|
93
104
|
]
|
94
105
|
|
@@ -97,32 +108,45 @@ module Datadog
|
|
97
108
|
peer_service_mapping = config.tracing.contrib.peer_service_mapping
|
98
109
|
peer_service_mapping_str = peer_service_mapping.map { |key, value| "#{key}:#{value}" }.join(',')
|
99
110
|
end
|
100
|
-
list << conf_value('DD_TRACE_PEER_SERVICE_MAPPING', peer_service_mapping_str)
|
111
|
+
list << conf_value('DD_TRACE_PEER_SERVICE_MAPPING', peer_service_mapping_str, seq_id)
|
101
112
|
|
102
113
|
# Whitelist of configuration options to send in additional payload object
|
103
114
|
TARGET_OPTIONS.each do |option|
|
104
115
|
split_option = option.split('.')
|
105
|
-
list << conf_value(option, to_value(config.dig(*split_option)))
|
116
|
+
list << conf_value(option, to_value(config.dig(*split_option)), seq_id)
|
106
117
|
end
|
107
118
|
|
108
119
|
# Add some more custom additional payload values here
|
109
120
|
list.push(
|
110
|
-
conf_value('tracing.auto_instrument.enabled', !defined?(Datadog::AutoInstrument::LOADED).nil
|
111
|
-
conf_value(
|
112
|
-
|
113
|
-
|
121
|
+
conf_value('tracing.auto_instrument.enabled', !defined?(Datadog::AutoInstrument::LOADED).nil?, seq_id),
|
122
|
+
conf_value(
|
123
|
+
'tracing.writer_options.buffer_size',
|
124
|
+
to_value(config.tracing.writer_options[:buffer_size]),
|
125
|
+
seq_id
|
126
|
+
),
|
127
|
+
conf_value(
|
128
|
+
'tracing.writer_options.flush_interval',
|
129
|
+
to_value(config.tracing.writer_options[:flush_interval]),
|
130
|
+
seq_id
|
131
|
+
),
|
132
|
+
conf_value(
|
133
|
+
'tracing.opentelemetry.enabled',
|
134
|
+
!defined?(Datadog::OpenTelemetry::LOADED).nil?,
|
135
|
+
seq_id
|
136
|
+
),
|
114
137
|
)
|
115
|
-
list << conf_value('logger.instance', config.logger.instance.class.to_s) if config.logger.instance
|
138
|
+
list << conf_value('logger.instance', config.logger.instance.class.to_s, seq_id) if config.logger.instance
|
116
139
|
if config.respond_to?('appsec')
|
117
|
-
list << conf_value('appsec.enabled', config.dig('appsec', 'enabled'))
|
118
|
-
list << conf_value('appsec.sca_enabled', config.dig('appsec', 'sca_enabled'))
|
140
|
+
list << conf_value('appsec.enabled', config.dig('appsec', 'enabled'), seq_id)
|
141
|
+
list << conf_value('appsec.sca_enabled', config.dig('appsec', 'sca_enabled'), seq_id)
|
119
142
|
end
|
120
|
-
list << conf_value('ci.enabled', config.dig('ci', 'enabled')) if config.respond_to?('ci')
|
143
|
+
list << conf_value('ci.enabled', config.dig('ci', 'enabled'), seq_id) if config.respond_to?('ci')
|
121
144
|
|
122
145
|
list.reject! { |entry| entry[:value].nil? }
|
123
146
|
list
|
124
147
|
end
|
125
148
|
# rubocop:enable Metrics/AbcSize
|
149
|
+
# rubocop:enable Metrics/MethodLength
|
126
150
|
|
127
151
|
def agent_transport(config)
|
128
152
|
adapter = Core::Configuration::AgentSettingsResolver.call(config).adapter
|
@@ -133,12 +157,12 @@ module Datadog
|
|
133
157
|
end
|
134
158
|
end
|
135
159
|
|
136
|
-
def conf_value(name, value, origin = 'code')
|
160
|
+
def conf_value(name, value, seq_id, origin = 'code')
|
137
161
|
{
|
138
162
|
name: name,
|
139
163
|
value: value,
|
140
164
|
origin: origin,
|
141
|
-
seq_id:
|
165
|
+
seq_id: seq_id,
|
142
166
|
}
|
143
167
|
end
|
144
168
|
|
@@ -168,7 +192,7 @@ module Datadog
|
|
168
192
|
'app-dependencies-loaded'
|
169
193
|
end
|
170
194
|
|
171
|
-
def payload
|
195
|
+
def payload
|
172
196
|
{ dependencies: dependencies }
|
173
197
|
end
|
174
198
|
|
@@ -191,7 +215,7 @@ module Datadog
|
|
191
215
|
'app-integrations-change'
|
192
216
|
end
|
193
217
|
|
194
|
-
def payload
|
218
|
+
def payload
|
195
219
|
{ integrations: integrations }
|
196
220
|
end
|
197
221
|
|
@@ -244,18 +268,20 @@ module Datadog
|
|
244
268
|
@origin = origin
|
245
269
|
end
|
246
270
|
|
247
|
-
def payload
|
248
|
-
{ configuration: configuration
|
271
|
+
def payload
|
272
|
+
{ configuration: configuration }
|
249
273
|
end
|
250
274
|
|
251
|
-
def configuration
|
275
|
+
def configuration
|
252
276
|
config = Datadog.configuration
|
277
|
+
seq_id = Event.configuration_sequence.next
|
253
278
|
|
254
279
|
res = @changes.map do |name, value|
|
255
280
|
{
|
256
281
|
name: name,
|
257
282
|
value: value,
|
258
283
|
origin: @origin,
|
284
|
+
seq_id: seq_id,
|
259
285
|
}
|
260
286
|
end
|
261
287
|
|
@@ -285,6 +311,55 @@ module Datadog
|
|
285
311
|
'app-closing'
|
286
312
|
end
|
287
313
|
end
|
314
|
+
|
315
|
+
# Telemetry class for the 'generate-metrics' event
|
316
|
+
class GenerateMetrics < Base
|
317
|
+
def type
|
318
|
+
'generate-metrics'
|
319
|
+
end
|
320
|
+
|
321
|
+
def initialize(namespace, metric_series)
|
322
|
+
super()
|
323
|
+
@namespace = namespace
|
324
|
+
@metric_series = metric_series
|
325
|
+
end
|
326
|
+
|
327
|
+
def payload
|
328
|
+
{
|
329
|
+
namespace: @namespace,
|
330
|
+
series: @metric_series.map(&:to_h)
|
331
|
+
}
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
# Telemetry class for the 'distributions' event
|
336
|
+
class Distributions < GenerateMetrics
|
337
|
+
def type
|
338
|
+
'distributions'
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
# Telemetry class for the 'message-batch' event
|
343
|
+
class MessageBatch
|
344
|
+
attr_reader :events
|
345
|
+
|
346
|
+
def type
|
347
|
+
'message-batch'
|
348
|
+
end
|
349
|
+
|
350
|
+
def initialize(events)
|
351
|
+
@events = events
|
352
|
+
end
|
353
|
+
|
354
|
+
def payload
|
355
|
+
@events.map do |event|
|
356
|
+
{
|
357
|
+
request_type: event.type,
|
358
|
+
payload: event.payload,
|
359
|
+
}
|
360
|
+
end
|
361
|
+
end
|
362
|
+
end
|
288
363
|
end
|
289
364
|
end
|
290
365
|
end
|
@@ -5,7 +5,9 @@ module Datadog
|
|
5
5
|
module Telemetry
|
6
6
|
module Ext
|
7
7
|
ENV_ENABLED = 'DD_INSTRUMENTATION_TELEMETRY_ENABLED'
|
8
|
+
ENV_METRICS_ENABLED = 'DD_TELEMETRY_METRICS_ENABLED'
|
8
9
|
ENV_HEARTBEAT_INTERVAL = 'DD_TELEMETRY_HEARTBEAT_INTERVAL'
|
10
|
+
ENV_METRICS_AGGREGATION_INTERVAL = 'DD_TELEMETRY_METRICS_AGGREGATION_INTERVAL'
|
9
11
|
ENV_DEPENDENCY_COLLECTION = 'DD_TELEMETRY_DEPENDENCY_COLLECTION_ENABLED'
|
10
12
|
ENV_INSTALL_ID = 'DD_INSTRUMENTATION_INSTALL_ID'
|
11
13
|
ENV_INSTALL_TYPE = 'DD_INSTRUMENTATION_INSTALL_TYPE'
|
@@ -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
|