datadog 2.7.1 → 2.9.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 +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
|