datadog 2.1.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +53 -2
  3. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +19 -1
  4. data/ext/datadog_profiling_native_extension/collectors_stack.c +41 -0
  5. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +1 -1
  6. data/ext/datadog_profiling_native_extension/crashtracker.c +1 -1
  7. data/ext/datadog_profiling_native_extension/extconf.rb +6 -4
  8. data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +47 -1
  9. data/ext/datadog_profiling_native_extension/setup_signal_handler.c +1 -1
  10. data/ext/datadog_profiling_native_extension/stack_recorder.c +13 -6
  11. data/ext/datadog_profiling_native_extension/stack_recorder.h +1 -0
  12. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +1 -1
  13. data/lib/datadog/appsec/extensions.rb +1 -0
  14. data/lib/datadog/core/configuration/components.rb +6 -3
  15. data/lib/datadog/core/configuration/settings.rb +39 -0
  16. data/lib/datadog/core/configuration.rb +3 -17
  17. data/lib/datadog/core/deprecations.rb +58 -0
  18. data/lib/datadog/core/environment/yjit.rb +5 -0
  19. data/lib/datadog/core/runtime/ext.rb +1 -0
  20. data/lib/datadog/core/runtime/metrics.rb +6 -0
  21. data/lib/datadog/core/telemetry/component.rb +107 -0
  22. data/lib/datadog/core/telemetry/event.rb +100 -25
  23. data/lib/datadog/core/telemetry/ext.rb +2 -0
  24. data/lib/datadog/core/telemetry/http/adapters/net.rb +1 -1
  25. data/lib/datadog/core/telemetry/metric.rb +167 -0
  26. data/lib/datadog/core/telemetry/metrics_collection.rb +81 -0
  27. data/lib/datadog/core/telemetry/metrics_manager.rb +81 -0
  28. data/lib/datadog/core/telemetry/request.rb +1 -1
  29. data/lib/datadog/core/telemetry/worker.rb +173 -0
  30. data/lib/datadog/core/utils/only_once_successful.rb +76 -0
  31. data/lib/datadog/core.rb +2 -19
  32. data/lib/datadog/opentelemetry/sdk/propagator.rb +5 -10
  33. data/lib/datadog/opentelemetry/sdk/span_processor.rb +5 -2
  34. data/lib/datadog/profiling/collectors/code_provenance.rb +18 -5
  35. data/lib/datadog/profiling/component.rb +18 -1
  36. data/lib/datadog/profiling/ext/dir_monkey_patches.rb +410 -0
  37. data/lib/datadog/profiling.rb +1 -0
  38. data/lib/datadog/tracing/contrib/action_cable/event.rb +1 -1
  39. data/lib/datadog/tracing/contrib/action_cable/events/broadcast.rb +1 -1
  40. data/lib/datadog/tracing/contrib/action_cable/events/perform_action.rb +1 -1
  41. data/lib/datadog/tracing/contrib/action_cable/events/transmit.rb +1 -1
  42. data/lib/datadog/tracing/contrib/action_mailer/event.rb +4 -6
  43. data/lib/datadog/tracing/contrib/action_mailer/events/deliver.rb +9 -4
  44. data/lib/datadog/tracing/contrib/action_mailer/events/process.rb +3 -2
  45. data/lib/datadog/tracing/contrib/action_view/events/render_partial.rb +1 -5
  46. data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +1 -1
  47. data/lib/datadog/tracing/contrib/active_job/events/discard.rb +1 -1
  48. data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +1 -1
  49. data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +1 -1
  50. data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +1 -1
  51. data/lib/datadog/tracing/contrib/active_job/events/perform.rb +1 -1
  52. data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +1 -1
  53. data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +1 -1
  54. data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +1 -1
  55. data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +1 -1
  56. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +1 -1
  57. data/lib/datadog/tracing/contrib/active_support/cache/event.rb +32 -0
  58. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +156 -0
  59. data/lib/datadog/tracing/contrib/active_support/cache/events.rb +34 -0
  60. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +45 -41
  61. data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +17 -40
  62. data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +4 -1
  63. data/lib/datadog/tracing/contrib/active_support/notifications/event.rb +29 -6
  64. data/lib/datadog/tracing/contrib/active_support/notifications/subscriber.rb +16 -4
  65. data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +33 -29
  66. data/lib/datadog/tracing/contrib/analytics.rb +5 -0
  67. data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +5 -0
  68. data/lib/datadog/tracing/contrib/graphql/patcher.rb +8 -2
  69. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +166 -0
  70. data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +25 -0
  71. data/lib/datadog/tracing/contrib/kafka/consumer_event.rb +1 -1
  72. data/lib/datadog/tracing/contrib/kafka/consumer_group_event.rb +1 -1
  73. data/lib/datadog/tracing/contrib/kafka/event.rb +1 -1
  74. data/lib/datadog/tracing/contrib/kafka/events/connection/request.rb +3 -3
  75. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_batch.rb +3 -3
  76. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_message.rb +3 -3
  77. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/heartbeat.rb +3 -3
  78. data/lib/datadog/tracing/contrib/kafka/events/produce_operation/send_messages.rb +3 -3
  79. data/lib/datadog/tracing/contrib/kafka/events/producer/deliver_messages.rb +3 -3
  80. data/lib/datadog/tracing/contrib/racecar/event.rb +2 -2
  81. data/lib/datadog/tracing/contrib/rails/ext.rb +9 -0
  82. data/lib/datadog/tracing/contrib/rails/patcher.rb +7 -0
  83. data/lib/datadog/tracing/contrib/rails/runner.rb +95 -0
  84. data/lib/datadog/tracing/distributed/b3_multi.rb +1 -1
  85. data/lib/datadog/tracing/distributed/b3_single.rb +3 -1
  86. data/lib/datadog/tracing/distributed/datadog.rb +2 -2
  87. data/lib/datadog/tracing/distributed/propagation.rb +9 -2
  88. data/lib/datadog/tracing/distributed/trace_context.rb +3 -2
  89. data/lib/datadog/tracing/span_operation.rb +3 -2
  90. data/lib/datadog/tracing/trace_operation.rb +7 -3
  91. data/lib/datadog/tracing/trace_segment.rb +4 -1
  92. data/lib/datadog/tracing/tracer.rb +9 -2
  93. data/lib/datadog/tracing.rb +5 -1
  94. data/lib/datadog/version.rb +2 -2
  95. metadata +22 -9
  96. data/lib/datadog/core/telemetry/client.rb +0 -95
  97. 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
- # @param seq_id [Integer] The sequence ID for the event.
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(seq_id)
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('tracing.writer_options.buffer_size', to_value(config.tracing.writer_options[:buffer_size])),
112
- conf_value('tracing.writer_options.flush_interval', to_value(config.tracing.writer_options[:flush_interval])),
113
- conf_value('tracing.opentelemetry.enabled', !defined?(Datadog::OpenTelemetry::LOADED).nil?),
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: @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(seq_id)
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(seq_id)
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(seq_id)
248
- { configuration: configuration(seq_id) }
271
+ def payload
272
+ { configuration: configuration }
249
273
  end
250
274
 
251
- def configuration(seq_id)
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'
@@ -15,7 +15,7 @@ module Datadog
15
15
  :timeout,
16
16
  :ssl
17
17
 
18
- DEFAULT_TIMEOUT = 30
18
+ DEFAULT_TIMEOUT = 2
19
19
 
20
20
  def initialize(hostname:, port: nil, timeout: DEFAULT_TIMEOUT, ssl: true)
21
21
  @hostname = hostname
@@ -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
@@ -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,