datadog 2.1.0 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|