datadog 2.7.1 → 2.9.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 +69 -1
- data/ext/datadog_profiling_native_extension/clock_id.h +2 -2
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +64 -54
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +1 -1
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.h +1 -1
- data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +16 -16
- data/ext/datadog_profiling_native_extension/collectors_stack.c +7 -7
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +259 -132
- data/ext/datadog_profiling_native_extension/extconf.rb +0 -8
- data/ext/datadog_profiling_native_extension/heap_recorder.c +11 -89
- data/ext/datadog_profiling_native_extension/heap_recorder.h +1 -1
- data/ext/datadog_profiling_native_extension/http_transport.c +4 -4
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +4 -1
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +3 -1
- data/ext/datadog_profiling_native_extension/profiling.c +10 -8
- data/ext/datadog_profiling_native_extension/ruby_helpers.c +8 -8
- data/ext/datadog_profiling_native_extension/stack_recorder.c +54 -88
- data/ext/datadog_profiling_native_extension/stack_recorder.h +1 -1
- data/ext/datadog_profiling_native_extension/time_helpers.h +1 -1
- data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.c +47 -0
- data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.h +31 -0
- data/ext/libdatadog_api/crashtracker.c +3 -0
- data/ext/libdatadog_extconf_helpers.rb +1 -1
- data/lib/datadog/appsec/assets/waf_rules/recommended.json +355 -157
- data/lib/datadog/appsec/assets/waf_rules/strict.json +62 -32
- data/lib/datadog/appsec/component.rb +1 -8
- data/lib/datadog/appsec/context.rb +54 -0
- data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +73 -0
- data/lib/datadog/appsec/contrib/active_record/integration.rb +41 -0
- data/lib/datadog/appsec/contrib/active_record/patcher.rb +53 -0
- data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +6 -6
- data/lib/datadog/appsec/contrib/devise/patcher/registration_controller_patch.rb +4 -4
- data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +19 -28
- data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +5 -5
- data/lib/datadog/appsec/contrib/rack/gateway/response.rb +3 -3
- data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +64 -96
- data/lib/datadog/appsec/contrib/rack/reactive/request.rb +10 -10
- data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +5 -5
- data/lib/datadog/appsec/contrib/rack/reactive/response.rb +6 -6
- data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +10 -11
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +43 -49
- data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +21 -32
- data/lib/datadog/appsec/contrib/rails/patcher.rb +1 -1
- data/lib/datadog/appsec/contrib/rails/reactive/action.rb +6 -6
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +41 -63
- data/lib/datadog/appsec/contrib/sinatra/patcher.rb +2 -2
- data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +5 -5
- data/lib/datadog/appsec/event.rb +6 -6
- data/lib/datadog/appsec/ext.rb +3 -1
- data/lib/datadog/appsec/monitor/gateway/watcher.rb +22 -32
- data/lib/datadog/appsec/monitor/reactive/set_user.rb +5 -5
- data/lib/datadog/appsec/processor/context.rb +2 -2
- data/lib/datadog/appsec/processor/rule_loader.rb +0 -3
- data/lib/datadog/appsec/remote.rb +1 -3
- data/lib/datadog/appsec/response.rb +7 -11
- data/lib/datadog/appsec.rb +6 -5
- data/lib/datadog/auto_instrument.rb +3 -0
- data/lib/datadog/core/configuration/agent_settings_resolver.rb +39 -11
- data/lib/datadog/core/configuration/components.rb +20 -2
- data/lib/datadog/core/configuration/settings.rb +10 -0
- data/lib/datadog/core/configuration.rb +10 -2
- data/lib/datadog/{tracing → core}/contrib/rails/utils.rb +1 -3
- data/lib/datadog/core/crashtracking/component.rb +1 -3
- data/lib/datadog/core/remote/client/capabilities.rb +6 -0
- data/lib/datadog/core/remote/client.rb +65 -59
- data/lib/datadog/core/telemetry/component.rb +9 -3
- data/lib/datadog/core/telemetry/event.rb +87 -3
- data/lib/datadog/core/telemetry/ext.rb +1 -0
- data/lib/datadog/core/telemetry/logging.rb +2 -2
- data/lib/datadog/core/telemetry/metric.rb +22 -0
- data/lib/datadog/core/telemetry/worker.rb +33 -0
- data/lib/datadog/di/base.rb +115 -0
- data/lib/datadog/di/code_tracker.rb +11 -7
- data/lib/datadog/di/component.rb +21 -11
- data/lib/datadog/di/configuration/settings.rb +11 -1
- data/lib/datadog/di/contrib/active_record.rb +1 -0
- data/lib/datadog/di/contrib/railtie.rb +15 -0
- data/lib/datadog/di/contrib.rb +26 -0
- data/lib/datadog/di/error.rb +5 -0
- data/lib/datadog/di/instrumenter.rb +111 -20
- data/lib/datadog/di/preload.rb +18 -0
- data/lib/datadog/di/probe.rb +11 -1
- data/lib/datadog/di/probe_builder.rb +1 -0
- data/lib/datadog/di/probe_manager.rb +8 -5
- data/lib/datadog/di/probe_notification_builder.rb +27 -7
- data/lib/datadog/di/probe_notifier_worker.rb +5 -6
- data/lib/datadog/di/remote.rb +124 -0
- data/lib/datadog/di/serializer.rb +14 -7
- data/lib/datadog/di/transport.rb +3 -5
- data/lib/datadog/di/utils.rb +7 -0
- data/lib/datadog/di.rb +23 -62
- data/lib/datadog/kit/appsec/events.rb +3 -3
- data/lib/datadog/kit/identity.rb +4 -4
- data/lib/datadog/profiling/component.rb +59 -69
- data/lib/datadog/profiling/http_transport.rb +1 -26
- data/lib/datadog/tracing/configuration/settings.rb +4 -8
- data/lib/datadog/tracing/contrib/action_cable/integration.rb +5 -2
- data/lib/datadog/tracing/contrib/action_mailer/integration.rb +6 -2
- data/lib/datadog/tracing/contrib/action_pack/integration.rb +5 -2
- data/lib/datadog/tracing/contrib/action_view/integration.rb +5 -2
- data/lib/datadog/tracing/contrib/active_job/integration.rb +5 -2
- data/lib/datadog/tracing/contrib/active_record/integration.rb +6 -2
- data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +3 -1
- data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +3 -1
- data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +16 -4
- data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +10 -0
- data/lib/datadog/tracing/contrib/active_support/integration.rb +5 -2
- data/lib/datadog/tracing/contrib/auto_instrument.rb +2 -2
- data/lib/datadog/tracing/contrib/aws/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/concurrent_ruby/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/elasticsearch/configuration/settings.rb +4 -0
- data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +6 -1
- data/lib/datadog/tracing/contrib/httprb/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/kafka/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/mongodb/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/opensearch/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/presto/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/rack/integration.rb +2 -2
- data/lib/datadog/tracing/contrib/rails/framework.rb +2 -2
- data/lib/datadog/tracing/contrib/rails/patcher.rb +1 -1
- data/lib/datadog/tracing/contrib/rest_client/integration.rb +3 -0
- data/lib/datadog/tracing/span.rb +12 -4
- data/lib/datadog/tracing/span_event.rb +123 -3
- data/lib/datadog/tracing/span_operation.rb +6 -0
- data/lib/datadog/tracing/transport/serializable_trace.rb +24 -6
- data/lib/datadog/version.rb +2 -2
- data/lib/datadog.rb +3 -0
- metadata +30 -17
- data/lib/datadog/appsec/processor/actions.rb +0 -49
- data/lib/datadog/appsec/reactive/operation.rb +0 -68
- data/lib/datadog/appsec/scope.rb +0 -58
- data/lib/datadog/core/crashtracking/agent_base_url.rb +0 -21
@@ -863,6 +863,16 @@ module Datadog
|
|
863
863
|
o.type :float
|
864
864
|
o.default 1.0
|
865
865
|
end
|
866
|
+
|
867
|
+
# Enable log collection for telemetry. Log collection only works when telemetry is enabled and
|
868
|
+
# logs are enabled.
|
869
|
+
# @default `DD_TELEMETRY_LOG_COLLECTION_ENABLED` environment variable, otherwise `true`.
|
870
|
+
# @return [Boolean]
|
871
|
+
option :log_collection_enabled do |o|
|
872
|
+
o.type :bool
|
873
|
+
o.env Core::Telemetry::Ext::ENV_LOG_COLLECTION
|
874
|
+
o.default true
|
875
|
+
end
|
866
876
|
end
|
867
877
|
|
868
878
|
# Remote configuration
|
@@ -236,7 +236,7 @@ module Datadog
|
|
236
236
|
rescue ThreadError => e
|
237
237
|
logger_without_components.error(
|
238
238
|
'Detected deadlock during datadog initialization. ' \
|
239
|
-
'Please report this at https://github.com/
|
239
|
+
'Please report this at https://github.com/datadog/dd-trace-rb/blob/master/CONTRIBUTING.md#found-a-bug' \
|
240
240
|
"\n\tSource:\n\t#{Array(e.backtrace).join("\n\t")}"
|
241
241
|
)
|
242
242
|
nil
|
@@ -258,8 +258,16 @@ module Datadog
|
|
258
258
|
def replace_components!(settings, old)
|
259
259
|
components = Components.new(settings)
|
260
260
|
|
261
|
+
# Carry over state from existing components to the new ones.
|
262
|
+
# Currently, if we already started the remote component (which
|
263
|
+
# happens after a request goes through installed Rack middleware),
|
264
|
+
# we will start the new remote component as well.
|
265
|
+
old_state = {
|
266
|
+
remote_started: old.remote&.started?,
|
267
|
+
}
|
268
|
+
|
261
269
|
old.shutdown!(components)
|
262
|
-
components.startup!(settings)
|
270
|
+
components.startup!(settings, old_state: old_state)
|
263
271
|
components
|
264
272
|
end
|
265
273
|
|
@@ -3,7 +3,6 @@
|
|
3
3
|
require 'libdatadog'
|
4
4
|
|
5
5
|
require_relative 'tag_builder'
|
6
|
-
require_relative 'agent_base_url'
|
7
6
|
require_relative '../utils/only_once'
|
8
7
|
require_relative '../utils/at_fork_monkey_patch'
|
9
8
|
|
@@ -31,8 +30,7 @@ module Datadog
|
|
31
30
|
|
32
31
|
def self.build(settings, agent_settings, logger:)
|
33
32
|
tags = TagBuilder.call(settings)
|
34
|
-
agent_base_url =
|
35
|
-
logger.warn('Missing agent base URL; cannot enable crash tracking') unless agent_base_url
|
33
|
+
agent_base_url = agent_settings.url
|
36
34
|
|
37
35
|
ld_library_path = ::Libdatadog.ld_library_path
|
38
36
|
logger.warn('Missing ld_library_path; cannot enable crash tracking') unless ld_library_path
|
@@ -32,6 +32,12 @@ module Datadog
|
|
32
32
|
register_receivers(Datadog::AppSec::Remote.receivers(@telemetry))
|
33
33
|
end
|
34
34
|
|
35
|
+
if settings.respond_to?(:dynamic_instrumentation) && settings.dynamic_instrumentation.enabled
|
36
|
+
register_capabilities(Datadog::DI::Remote.capabilities)
|
37
|
+
register_products(Datadog::DI::Remote.products)
|
38
|
+
register_receivers(Datadog::DI::Remote.receivers(@telemetry))
|
39
|
+
end
|
40
|
+
|
35
41
|
register_capabilities(Datadog::Tracing::Remote.capabilities)
|
36
42
|
register_products(Datadog::Tracing::Remote.products)
|
37
43
|
register_receivers(Datadog::Tracing::Remote.receivers(@telemetry))
|
@@ -24,93 +24,99 @@ module Datadog
|
|
24
24
|
@dispatcher = Dispatcher.new(@capabilities.receivers)
|
25
25
|
end
|
26
26
|
|
27
|
-
# rubocop:disable Metrics/AbcSize,Metrics/PerceivedComplexity,Metrics/MethodLength,Metrics/CyclomaticComplexity
|
28
27
|
def sync
|
29
28
|
# TODO: Skip sync if no capabilities are registered
|
30
29
|
response = transport.send_config(payload)
|
31
30
|
|
32
31
|
if response.ok?
|
33
|
-
|
34
|
-
|
35
|
-
|
32
|
+
process_response(response)
|
33
|
+
elsif response.internal_error?
|
34
|
+
raise TransportError, response.to_s
|
35
|
+
end
|
36
|
+
end
|
36
37
|
|
37
|
-
|
38
|
-
end
|
38
|
+
private
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
40
|
+
def process_response(response)
|
41
|
+
# when response is completely empty, do nothing as in: leave as is
|
42
|
+
if response.empty?
|
43
|
+
Datadog.logger.debug { 'remote: empty response => NOOP' }
|
44
44
|
|
45
|
-
|
45
|
+
return
|
46
|
+
end
|
46
47
|
|
47
|
-
|
48
|
-
|
49
|
-
|
48
|
+
begin
|
49
|
+
paths = response.client_configs.map do |path|
|
50
|
+
Configuration::Path.parse(path)
|
50
51
|
end
|
51
52
|
|
52
|
-
|
53
|
-
|
53
|
+
targets = Configuration::TargetMap.parse(response.targets)
|
54
|
+
|
55
|
+
contents = Configuration::ContentList.parse(response.target_files)
|
56
|
+
rescue Remote::Configuration::Path::ParseError => e
|
57
|
+
raise SyncError, e.message
|
58
|
+
end
|
54
59
|
|
55
|
-
|
56
|
-
|
60
|
+
# To make sure steep does not complain
|
61
|
+
return unless paths && targets && contents
|
57
62
|
|
58
|
-
|
59
|
-
|
60
|
-
(current.paths - paths).each { |p| transaction.delete(p) }
|
63
|
+
# TODO: sometimes it can strangely be so that paths.empty?
|
64
|
+
# TODO: sometimes it can strangely be so that targets.empty?
|
61
65
|
|
62
|
-
|
63
|
-
|
64
|
-
# match target with path
|
65
|
-
target = targets[path]
|
66
|
+
apply_config(paths, targets, contents)
|
67
|
+
end
|
66
68
|
|
67
|
-
|
68
|
-
|
69
|
+
def apply_config(paths, targets, contents)
|
70
|
+
changes = repository.transaction do |current, transaction|
|
71
|
+
# paths to be removed: previously applied paths minus ingress paths
|
72
|
+
(current.paths - paths).each { |p| transaction.delete(p) }
|
69
73
|
|
70
|
-
|
71
|
-
|
74
|
+
# go through each ingress path
|
75
|
+
paths.each do |path|
|
76
|
+
# match target with path
|
77
|
+
target = targets[path]
|
72
78
|
|
73
|
-
|
74
|
-
|
75
|
-
changed = current.paths.include?(path) && !current.contents.find_content(path, target)
|
79
|
+
# abort entirely if matching target not found
|
80
|
+
raise SyncError, "no target for path '#{path}'" if target.nil?
|
76
81
|
|
77
|
-
|
78
|
-
|
82
|
+
# new paths are not in previously applied paths
|
83
|
+
new = !current.paths.include?(path)
|
79
84
|
|
80
|
-
|
85
|
+
# updated paths are in previously applied paths
|
86
|
+
# but the content hash changed
|
87
|
+
changed = current.paths.include?(path) && !current.contents.find_content(path, target)
|
81
88
|
|
82
|
-
|
83
|
-
|
89
|
+
# skip if unchanged
|
90
|
+
same = !new && !changed
|
84
91
|
|
85
|
-
|
86
|
-
raise SyncError, "no valid content for target at path '#{path}'" if content.nil?
|
92
|
+
next if same
|
87
93
|
|
88
|
-
|
89
|
-
|
90
|
-
transaction.insert(path, target, content) if new
|
91
|
-
transaction.update(path, target, content) if changed
|
92
|
-
end
|
94
|
+
# match content with path and target
|
95
|
+
content = contents.find_content(path, target)
|
93
96
|
|
94
|
-
#
|
95
|
-
|
96
|
-
transaction.set(targets_version: targets.version)
|
97
|
+
# abort entirely if matching content not found
|
98
|
+
raise SyncError, "no valid content for target at path '#{path}'" if content.nil?
|
97
99
|
|
98
|
-
#
|
99
|
-
# TODO:
|
100
|
+
# to be added or updated << config
|
101
|
+
# TODO: metadata (hash, version, etc...)
|
102
|
+
transaction.insert(path, target, content) if new
|
103
|
+
transaction.update(path, target, content) if changed
|
100
104
|
end
|
101
105
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
end
|
107
|
-
|
108
|
-
raise TransportError, response.to_s
|
106
|
+
# save backend opaque backend state
|
107
|
+
transaction.set(opaque_backend_state: targets.opaque_backend_state)
|
108
|
+
transaction.set(targets_version: targets.version)
|
109
|
+
|
110
|
+
# upon transaction end, new list of applied config + metadata (add, change, remove) will be saved
|
111
|
+
# TODO: also remove stale config (matching removed) from cache (client configs is exhaustive list of paths)
|
109
112
|
end
|
110
|
-
end
|
111
|
-
# rubocop:enable Metrics/AbcSize,Metrics/PerceivedComplexity,Metrics/MethodLength,Metrics/CyclomaticComplexity
|
112
113
|
|
113
|
-
|
114
|
+
if changes.empty?
|
115
|
+
Datadog.logger.debug { 'remote: no changes' }
|
116
|
+
else
|
117
|
+
dispatcher.dispatch(changes, repository)
|
118
|
+
end
|
119
|
+
end
|
114
120
|
|
115
121
|
def payload # rubocop:disable Metrics/MethodLength
|
116
122
|
state = repository.state
|
@@ -14,6 +14,7 @@ module Datadog
|
|
14
14
|
module Core
|
15
15
|
module Telemetry
|
16
16
|
# Telemetry entrypoint, coordinates sending telemetry events at various points in app lifecycle.
|
17
|
+
# Note: Telemetry does not spawn its worker thread in fork processes, thus no telemetry is sent in forked processes.
|
17
18
|
class Component
|
18
19
|
attr_reader :enabled
|
19
20
|
|
@@ -52,6 +53,7 @@ module Datadog
|
|
52
53
|
metrics_aggregation_interval_seconds: settings.telemetry.metrics_aggregation_interval_seconds,
|
53
54
|
dependency_collection: settings.telemetry.dependency_collection,
|
54
55
|
shutdown_timeout_seconds: settings.telemetry.shutdown_timeout_seconds,
|
56
|
+
log_collection_enabled: settings.telemetry.log_collection_enabled
|
55
57
|
)
|
56
58
|
end
|
57
59
|
|
@@ -67,10 +69,11 @@ module Datadog
|
|
67
69
|
http_transport:,
|
68
70
|
shutdown_timeout_seconds:,
|
69
71
|
enabled: true,
|
70
|
-
metrics_enabled: true
|
72
|
+
metrics_enabled: true,
|
73
|
+
log_collection_enabled: true
|
71
74
|
)
|
72
75
|
@enabled = enabled
|
73
|
-
@
|
76
|
+
@log_collection_enabled = log_collection_enabled
|
74
77
|
|
75
78
|
@metrics_manager = MetricsManager.new(
|
76
79
|
enabled: enabled && metrics_enabled,
|
@@ -86,6 +89,9 @@ module Datadog
|
|
86
89
|
dependency_collection: dependency_collection,
|
87
90
|
shutdown_timeout: shutdown_timeout_seconds
|
88
91
|
)
|
92
|
+
|
93
|
+
@stopped = false
|
94
|
+
|
89
95
|
@worker.start
|
90
96
|
end
|
91
97
|
|
@@ -114,7 +120,7 @@ module Datadog
|
|
114
120
|
end
|
115
121
|
|
116
122
|
def log!(event)
|
117
|
-
return
|
123
|
+
return if !@enabled || forked? || !@log_collection_enabled
|
118
124
|
|
119
125
|
@worker.enqueue(event)
|
120
126
|
end
|
@@ -29,6 +29,22 @@ module Datadog
|
|
29
29
|
def payload
|
30
30
|
{}
|
31
31
|
end
|
32
|
+
|
33
|
+
# Override equality to allow for deduplication
|
34
|
+
# The basic implementation is to check if the other object is an instance of the same class.
|
35
|
+
# This works for events that have no attributes.
|
36
|
+
# For events with attributes, you should override this method to compare the attributes.
|
37
|
+
def ==(other)
|
38
|
+
other.is_a?(self.class)
|
39
|
+
end
|
40
|
+
|
41
|
+
# @see #==
|
42
|
+
alias eql? ==
|
43
|
+
|
44
|
+
# @see #==
|
45
|
+
def hash
|
46
|
+
self.class.hash
|
47
|
+
end
|
32
48
|
end
|
33
49
|
|
34
50
|
# Telemetry class for the 'app-started' event
|
@@ -263,6 +279,8 @@ module Datadog
|
|
263
279
|
|
264
280
|
# Telemetry class for the 'app-client-configuration-change' event
|
265
281
|
class AppClientConfigurationChange < Base
|
282
|
+
attr_reader :changes, :origin
|
283
|
+
|
266
284
|
def type
|
267
285
|
'app-client-configuration-change'
|
268
286
|
end
|
@@ -301,6 +319,16 @@ module Datadog
|
|
301
319
|
|
302
320
|
res
|
303
321
|
end
|
322
|
+
|
323
|
+
def ==(other)
|
324
|
+
other.is_a?(AppClientConfigurationChange) && other.changes == @changes && other.origin == @origin
|
325
|
+
end
|
326
|
+
|
327
|
+
alias eql? ==
|
328
|
+
|
329
|
+
def hash
|
330
|
+
[self.class, @changes, @origin].hash
|
331
|
+
end
|
304
332
|
end
|
305
333
|
|
306
334
|
# Telemetry class for the 'app-heartbeat' event
|
@@ -319,6 +347,8 @@ module Datadog
|
|
319
347
|
|
320
348
|
# Telemetry class for the 'generate-metrics' event
|
321
349
|
class GenerateMetrics < Base
|
350
|
+
attr_reader :namespace, :metric_series
|
351
|
+
|
322
352
|
def type
|
323
353
|
'generate-metrics'
|
324
354
|
end
|
@@ -335,24 +365,54 @@ module Datadog
|
|
335
365
|
series: @metric_series.map(&:to_h)
|
336
366
|
}
|
337
367
|
end
|
368
|
+
|
369
|
+
def ==(other)
|
370
|
+
other.is_a?(GenerateMetrics) && other.namespace == @namespace && other.metric_series == @metric_series
|
371
|
+
end
|
372
|
+
|
373
|
+
alias eql? ==
|
374
|
+
|
375
|
+
def hash
|
376
|
+
[self.class, @namespace, @metric_series].hash
|
377
|
+
end
|
338
378
|
end
|
339
379
|
|
340
|
-
# Telemetry class for the 'logs' event
|
380
|
+
# Telemetry class for the 'logs' event.
|
381
|
+
# Logs with the same content are deduplicated at flush time.
|
341
382
|
class Log < Base
|
342
383
|
LEVELS = {
|
343
384
|
error: 'ERROR',
|
344
385
|
warn: 'WARN',
|
345
386
|
}.freeze
|
346
387
|
|
388
|
+
LEVELS_STRING = LEVELS.values.freeze
|
389
|
+
|
390
|
+
attr_reader :message, :level, :stack_trace, :count
|
391
|
+
|
347
392
|
def type
|
348
393
|
'logs'
|
349
394
|
end
|
350
395
|
|
351
|
-
|
396
|
+
# @param message [String] the log message
|
397
|
+
# @param level [Symbol, String] the log level. Either :error, :warn, 'ERROR', or 'WARN'.
|
398
|
+
# @param stack_trace [String, nil] the stack trace
|
399
|
+
# @param count [Integer] the number of times the log was emitted. Used for deduplication.
|
400
|
+
def initialize(message:, level:, stack_trace: nil, count: 1)
|
352
401
|
super()
|
353
402
|
@message = message
|
354
403
|
@stack_trace = stack_trace
|
355
|
-
|
404
|
+
|
405
|
+
if level.is_a?(String) && LEVELS_STRING.include?(level)
|
406
|
+
# String level is used during object copy for deduplication
|
407
|
+
@level = level
|
408
|
+
elsif level.is_a?(Symbol)
|
409
|
+
# Symbol level is used by the regular log emitter user
|
410
|
+
@level = LEVELS.fetch(level) { |k| raise ArgumentError, "Invalid log level :#{k}" }
|
411
|
+
else
|
412
|
+
raise ArgumentError, "Invalid log level #{level}"
|
413
|
+
end
|
414
|
+
|
415
|
+
@count = count
|
356
416
|
end
|
357
417
|
|
358
418
|
def payload
|
@@ -362,10 +422,24 @@ module Datadog
|
|
362
422
|
message: @message,
|
363
423
|
level: @level,
|
364
424
|
stack_trace: @stack_trace,
|
425
|
+
count: @count,
|
365
426
|
}.compact
|
366
427
|
]
|
367
428
|
}
|
368
429
|
end
|
430
|
+
|
431
|
+
# override equality to allow for deduplication
|
432
|
+
def ==(other)
|
433
|
+
other.is_a?(Log) &&
|
434
|
+
other.message == @message &&
|
435
|
+
other.level == @level && other.stack_trace == @stack_trace && other.count == @count
|
436
|
+
end
|
437
|
+
|
438
|
+
alias eql? ==
|
439
|
+
|
440
|
+
def hash
|
441
|
+
[self.class, @message, @level, @stack_trace, @count].hash
|
442
|
+
end
|
369
443
|
end
|
370
444
|
|
371
445
|
# Telemetry class for the 'distributions' event
|
@@ -395,6 +469,16 @@ module Datadog
|
|
395
469
|
}
|
396
470
|
end
|
397
471
|
end
|
472
|
+
|
473
|
+
def ==(other)
|
474
|
+
other.is_a?(MessageBatch) && other.events == @events
|
475
|
+
end
|
476
|
+
|
477
|
+
alias eql? ==
|
478
|
+
|
479
|
+
def hash
|
480
|
+
[self.class, @events].hash
|
481
|
+
end
|
398
482
|
end
|
399
483
|
end
|
400
484
|
end
|
@@ -13,6 +13,7 @@ module Datadog
|
|
13
13
|
ENV_INSTALL_TYPE = 'DD_INSTRUMENTATION_INSTALL_TYPE'
|
14
14
|
ENV_INSTALL_TIME = 'DD_INSTRUMENTATION_INSTALL_TIME'
|
15
15
|
ENV_AGENTLESS_URL_OVERRIDE = 'DD_TELEMETRY_AGENTLESS_URL'
|
16
|
+
ENV_LOG_COLLECTION = 'DD_TELEMETRY_LOG_COLLECTION_ENABLED'
|
16
17
|
end
|
17
18
|
end
|
18
19
|
end
|
@@ -41,7 +41,7 @@ module Datadog
|
|
41
41
|
else
|
42
42
|
'REDACTED'
|
43
43
|
end
|
44
|
-
end.join(
|
44
|
+
end.join("\n")
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
@@ -49,7 +49,7 @@ module Datadog
|
|
49
49
|
# Annoymous exceptions to be logged as <Class:0x00007f8b1c0b3b40>
|
50
50
|
message = +''
|
51
51
|
message << (exception.class.name || exception.class.inspect)
|
52
|
-
message << ':' << description if description
|
52
|
+
message << ': ' << description if description
|
53
53
|
|
54
54
|
event = Event::Log.new(
|
55
55
|
message: message,
|
@@ -41,6 +41,18 @@ module Datadog
|
|
41
41
|
}
|
42
42
|
end
|
43
43
|
|
44
|
+
def ==(other)
|
45
|
+
other.is_a?(self.class) &&
|
46
|
+
name == other.name &&
|
47
|
+
values == other.values && tags == other.tags && common == other.common && type == other.type
|
48
|
+
end
|
49
|
+
|
50
|
+
alias eql? ==
|
51
|
+
|
52
|
+
def hash
|
53
|
+
[self.class, name, values, tags, common, type].hash
|
54
|
+
end
|
55
|
+
|
44
56
|
private
|
45
57
|
|
46
58
|
def tags_to_array(tags)
|
@@ -71,6 +83,16 @@ module Datadog
|
|
71
83
|
res[:interval] = interval
|
72
84
|
res
|
73
85
|
end
|
86
|
+
|
87
|
+
def ==(other)
|
88
|
+
super && interval == other.interval
|
89
|
+
end
|
90
|
+
|
91
|
+
alias eql? ==
|
92
|
+
|
93
|
+
def hash
|
94
|
+
[super, interval].hash
|
95
|
+
end
|
74
96
|
end
|
75
97
|
|
76
98
|
# Count metric adds up all the submitted values in a time interval. This would be suitable for a
|
@@ -97,6 +97,8 @@ module Datadog
|
|
97
97
|
return if events.empty?
|
98
98
|
return if !enabled? || !sent_started_event?
|
99
99
|
|
100
|
+
events = deduplicate_logs(events)
|
101
|
+
|
100
102
|
Datadog.logger.debug { "Sending #{events&.count} telemetry events" }
|
101
103
|
send_event(Event::MessageBatch.new(events))
|
102
104
|
end
|
@@ -167,6 +169,37 @@ module Datadog
|
|
167
169
|
Datadog.logger.debug('Agent does not support telemetry; disabling future telemetry events.')
|
168
170
|
disable!
|
169
171
|
end
|
172
|
+
|
173
|
+
# Deduplicate logs by counting the number of repeated occurrences of the same log
|
174
|
+
# entry and replacing them with a single entry with the calculated `count` value.
|
175
|
+
# Non-log events are unchanged.
|
176
|
+
def deduplicate_logs(events)
|
177
|
+
return events if events.empty?
|
178
|
+
|
179
|
+
all_logs = []
|
180
|
+
other_events = events.reject do |event|
|
181
|
+
if event.is_a?(Event::Log)
|
182
|
+
all_logs << event
|
183
|
+
true
|
184
|
+
else
|
185
|
+
false
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
return events if all_logs.empty?
|
190
|
+
|
191
|
+
uniq_logs = all_logs.group_by(&:itself).map do |_, logs|
|
192
|
+
log = logs.first
|
193
|
+
if logs.size > 1
|
194
|
+
# New log event with a count of repeated occurrences
|
195
|
+
Event::Log.new(message: log.message, level: log.level, stack_trace: log.stack_trace, count: logs.size)
|
196
|
+
else
|
197
|
+
log
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
other_events + uniq_logs
|
202
|
+
end
|
170
203
|
end
|
171
204
|
end
|
172
205
|
end
|