datadog 2.7.0 → 2.8.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 +43 -1
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +47 -17
- data/ext/datadog_profiling_native_extension/extconf.rb +3 -8
- data/ext/datadog_profiling_native_extension/heap_recorder.c +11 -89
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +3 -9
- data/ext/datadog_profiling_native_extension/profiling.c +6 -0
- data/ext/datadog_profiling_native_extension/ruby_helpers.c +14 -4
- data/ext/datadog_profiling_native_extension/ruby_helpers.h +4 -0
- data/ext/datadog_profiling_native_extension/stack_recorder.c +0 -34
- data/ext/libdatadog_extconf_helpers.rb +1 -1
- data/lib/datadog/appsec/component.rb +1 -8
- 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/event.rb +1 -1
- data/lib/datadog/appsec/processor/context.rb +2 -2
- data/lib/datadog/appsec/remote.rb +1 -3
- data/lib/datadog/appsec/response.rb +7 -11
- data/lib/datadog/appsec.rb +3 -2
- data/lib/datadog/core/configuration/components.rb +17 -1
- data/lib/datadog/core/configuration/settings.rb +10 -0
- data/lib/datadog/core/configuration.rb +9 -1
- 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/ext.rb +1 -0
- data/lib/datadog/di/code_tracker.rb +5 -4
- data/lib/datadog/di/component.rb +5 -1
- data/lib/datadog/di/contrib/active_record.rb +1 -0
- data/lib/datadog/di/init.rb +20 -0
- data/lib/datadog/di/instrumenter.rb +81 -11
- data/lib/datadog/di/probe.rb +11 -1
- data/lib/datadog/di/probe_builder.rb +1 -0
- data/lib/datadog/di/probe_manager.rb +4 -1
- data/lib/datadog/di/probe_notification_builder.rb +13 -7
- data/lib/datadog/di/remote.rb +124 -0
- data/lib/datadog/di/serializer.rb +14 -7
- data/lib/datadog/di/transport.rb +2 -1
- data/lib/datadog/di/utils.rb +7 -0
- data/lib/datadog/di.rb +84 -20
- data/lib/datadog/profiling/component.rb +4 -16
- data/lib/datadog/tracing/configuration/settings.rb +4 -8
- data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +16 -4
- 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/tracer.rb +1 -1
- data/lib/datadog/version.rb +1 -1
- data/lib/datadog.rb +3 -0
- metadata +17 -13
- data/lib/datadog/appsec/processor/actions.rb +0 -49
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
module AppSec
|
5
|
+
module Contrib
|
6
|
+
module ActiveRecord
|
7
|
+
# AppSec module that will be prepended to ActiveRecord adapter
|
8
|
+
module Instrumentation
|
9
|
+
module_function
|
10
|
+
|
11
|
+
def detect_sql_injection(sql, adapter_name)
|
12
|
+
scope = AppSec.active_scope
|
13
|
+
return unless scope
|
14
|
+
|
15
|
+
# libddwaf expects db system to be lowercase,
|
16
|
+
# in case of sqlite adapter, libddwaf expects 'sqlite' as db system
|
17
|
+
db_system = adapter_name.downcase
|
18
|
+
db_system = 'sqlite' if db_system == 'sqlite3'
|
19
|
+
|
20
|
+
ephemeral_data = {
|
21
|
+
'server.db.statement' => sql,
|
22
|
+
'server.db.system' => db_system
|
23
|
+
}
|
24
|
+
|
25
|
+
waf_timeout = Datadog.configuration.appsec.waf_timeout
|
26
|
+
result = scope.processor_context.run({}, ephemeral_data, waf_timeout)
|
27
|
+
|
28
|
+
if result.status == :match
|
29
|
+
Datadog::AppSec::Event.tag_and_keep!(scope, result)
|
30
|
+
|
31
|
+
event = {
|
32
|
+
waf_result: result,
|
33
|
+
trace: scope.trace,
|
34
|
+
span: scope.service_entry_span,
|
35
|
+
sql: sql,
|
36
|
+
actions: result.actions
|
37
|
+
}
|
38
|
+
scope.processor_context.events << event
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# patch for all adapters in ActiveRecord >= 7.1
|
43
|
+
module InternalExecQueryAdapterPatch
|
44
|
+
def internal_exec_query(sql, *args, **rest)
|
45
|
+
Instrumentation.detect_sql_injection(sql, adapter_name)
|
46
|
+
|
47
|
+
super
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# patch for postgres adapter in ActiveRecord < 7.1
|
52
|
+
module ExecuteAndClearAdapterPatch
|
53
|
+
def execute_and_clear(sql, *args, **rest)
|
54
|
+
Instrumentation.detect_sql_injection(sql, adapter_name)
|
55
|
+
|
56
|
+
super
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# patch for mysql2 and sqlite3 adapters in ActiveRecord < 7.1
|
61
|
+
# this patch is also used when using JDBC adapter
|
62
|
+
module ExecQueryAdapterPatch
|
63
|
+
def exec_query(sql, *args, **rest)
|
64
|
+
Instrumentation.detect_sql_injection(sql, adapter_name)
|
65
|
+
|
66
|
+
super
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../integration'
|
4
|
+
require_relative 'patcher'
|
5
|
+
|
6
|
+
module Datadog
|
7
|
+
module AppSec
|
8
|
+
module Contrib
|
9
|
+
module ActiveRecord
|
10
|
+
# This class provides helper methods that are used when patching ActiveRecord
|
11
|
+
class Integration
|
12
|
+
include Datadog::AppSec::Contrib::Integration
|
13
|
+
|
14
|
+
MINIMUM_VERSION = Gem::Version.new('4')
|
15
|
+
|
16
|
+
register_as :active_record, auto_patch: false
|
17
|
+
|
18
|
+
def self.version
|
19
|
+
Gem.loaded_specs['activerecord'] && Gem.loaded_specs['activerecord'].version
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.loaded?
|
23
|
+
!defined?(::ActiveRecord).nil?
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.compatible?
|
27
|
+
super && version >= MINIMUM_VERSION
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.auto_instrument?
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
def patcher
|
35
|
+
Patcher
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../patcher'
|
4
|
+
require_relative 'instrumentation'
|
5
|
+
|
6
|
+
module Datadog
|
7
|
+
module AppSec
|
8
|
+
module Contrib
|
9
|
+
module ActiveRecord
|
10
|
+
# AppSec patcher module for ActiveRecord
|
11
|
+
module Patcher
|
12
|
+
include Datadog::AppSec::Contrib::Patcher
|
13
|
+
|
14
|
+
module_function
|
15
|
+
|
16
|
+
def patched?
|
17
|
+
Patcher.instance_variable_get(:@patched)
|
18
|
+
end
|
19
|
+
|
20
|
+
def target_version
|
21
|
+
Integration.version
|
22
|
+
end
|
23
|
+
|
24
|
+
def patch
|
25
|
+
ActiveSupport.on_load :active_record do
|
26
|
+
instrumentation_module = if ::ActiveRecord.gem_version >= Gem::Version.new('7.1')
|
27
|
+
Instrumentation::InternalExecQueryAdapterPatch
|
28
|
+
else
|
29
|
+
Instrumentation::ExecQueryAdapterPatch
|
30
|
+
end
|
31
|
+
|
32
|
+
if defined?(::ActiveRecord::ConnectionAdapters::SQLite3Adapter)
|
33
|
+
::ActiveRecord::ConnectionAdapters::SQLite3Adapter.prepend(instrumentation_module)
|
34
|
+
end
|
35
|
+
|
36
|
+
if defined?(::ActiveRecord::ConnectionAdapters::Mysql2Adapter)
|
37
|
+
::ActiveRecord::ConnectionAdapters::Mysql2Adapter.prepend(instrumentation_module)
|
38
|
+
end
|
39
|
+
|
40
|
+
if defined?(::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)
|
41
|
+
unless defined?(::ActiveRecord::ConnectionAdapters::JdbcAdapter)
|
42
|
+
instrumentation_module = Instrumentation::ExecuteAndClearAdapterPatch
|
43
|
+
end
|
44
|
+
|
45
|
+
::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(instrumentation_module)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/datadog/appsec/event.rb
CHANGED
@@ -142,7 +142,7 @@ module Datadog
|
|
142
142
|
scope.trace.keep! if scope.trace
|
143
143
|
|
144
144
|
if scope.service_entry_span
|
145
|
-
scope.service_entry_span.set_tag('appsec.blocked', 'true') if waf_result.actions.
|
145
|
+
scope.service_entry_span.set_tag('appsec.blocked', 'true') if waf_result.actions.key?('block_request')
|
146
146
|
scope.service_entry_span.set_tag('appsec.event', 'true')
|
147
147
|
end
|
148
148
|
|
@@ -19,7 +19,7 @@ module Datadog
|
|
19
19
|
@events = []
|
20
20
|
@run_mutex = Mutex.new
|
21
21
|
|
22
|
-
@libddwaf_debug_tag = "libddwaf:#{WAF::VERSION::STRING}"
|
22
|
+
@libddwaf_debug_tag = "libddwaf:#{WAF::VERSION::STRING} method:ddwaf_run"
|
23
23
|
end
|
24
24
|
|
25
25
|
def run(persistent_data, ephemeral_data, timeout = WAF::LibDDWAF::DDWAF_RUN_TIMEOUT)
|
@@ -79,7 +79,7 @@ module Datadog
|
|
79
79
|
@context.run(persistent_data, ephemeral_data, timeout)
|
80
80
|
rescue WAF::LibDDWAF::Error => e
|
81
81
|
Datadog.logger.debug { "#{@libddwaf_debug_tag} execution error: #{e} backtrace: #{e.backtrace&.first(3)}" }
|
82
|
-
@telemetry.report(e, description: 'libddwaf internal low-level error')
|
82
|
+
@telemetry.report(e, description: 'libddwaf-rb internal low-level error')
|
83
83
|
|
84
84
|
[:err_internal, WAF::Result.new(:err_internal, [], 0.0, false, [], [])]
|
85
85
|
end
|
@@ -67,7 +67,6 @@ module Datadog
|
|
67
67
|
data = []
|
68
68
|
overrides = []
|
69
69
|
exclusions = []
|
70
|
-
actions = []
|
71
70
|
|
72
71
|
repository.contents.each do |content|
|
73
72
|
parsed_content = parse_content(content)
|
@@ -81,7 +80,6 @@ module Datadog
|
|
81
80
|
overrides << parsed_content['rules_override'] if parsed_content['rules_override']
|
82
81
|
exclusions << parsed_content['exclusions'] if parsed_content['exclusions']
|
83
82
|
custom_rules << parsed_content['custom_rules'] if parsed_content['custom_rules']
|
84
|
-
actions.concat(parsed_content['actions']) if parsed_content['actions']
|
85
83
|
end
|
86
84
|
end
|
87
85
|
|
@@ -105,7 +103,7 @@ module Datadog
|
|
105
103
|
telemetry: telemetry
|
106
104
|
)
|
107
105
|
|
108
|
-
Datadog::AppSec.reconfigure(ruleset: ruleset,
|
106
|
+
Datadog::AppSec.reconfigure(ruleset: ruleset, telemetry: telemetry)
|
109
107
|
end
|
110
108
|
|
111
109
|
[receiver]
|
@@ -31,19 +31,16 @@ module Datadog
|
|
31
31
|
def negotiate(env, actions)
|
32
32
|
# @type var configured_response: Response?
|
33
33
|
configured_response = nil
|
34
|
-
actions.each do |
|
34
|
+
actions.each do |type, parameters|
|
35
35
|
# Need to use next to make steep happy :(
|
36
36
|
# I rather use break to stop the execution
|
37
37
|
next if configured_response
|
38
38
|
|
39
|
-
|
40
|
-
next unless action_configuration
|
41
|
-
|
42
|
-
configured_response = case action_configuration['type']
|
39
|
+
configured_response = case type
|
43
40
|
when 'block_request'
|
44
|
-
block_response(env,
|
41
|
+
block_response(env, parameters)
|
45
42
|
when 'redirect_request'
|
46
|
-
redirect_response(env,
|
43
|
+
redirect_response(env, parameters)
|
47
44
|
end
|
48
45
|
end
|
49
46
|
|
@@ -90,7 +87,7 @@ module Datadog
|
|
90
87
|
body << content(content_type)
|
91
88
|
|
92
89
|
Response.new(
|
93
|
-
status: options['status_code'] || 403,
|
90
|
+
status: options['status_code']&.to_i || 403,
|
94
91
|
headers: { 'Content-Type' => content_type },
|
95
92
|
body: body,
|
96
93
|
)
|
@@ -100,15 +97,14 @@ module Datadog
|
|
100
97
|
if options['location'] && !options['location'].empty?
|
101
98
|
content_type = content_type(env)
|
102
99
|
|
103
|
-
status = options['status_code'] >= 300 && options['status_code'] < 400 ? options['status_code'] : 303
|
104
|
-
|
105
100
|
headers = {
|
106
101
|
'Content-Type' => content_type,
|
107
102
|
'Location' => options['location']
|
108
103
|
}
|
109
104
|
|
105
|
+
status_code = options['status_code'].to_i
|
110
106
|
Response.new(
|
111
|
-
status:
|
107
|
+
status: (status_code >= 300 && status_code < 400 ? status_code : 303),
|
112
108
|
headers: headers,
|
113
109
|
body: [],
|
114
110
|
)
|
data/lib/datadog/appsec.rb
CHANGED
@@ -24,12 +24,12 @@ module Datadog
|
|
24
24
|
appsec_component.processor if appsec_component
|
25
25
|
end
|
26
26
|
|
27
|
-
def reconfigure(ruleset:,
|
27
|
+
def reconfigure(ruleset:, telemetry:)
|
28
28
|
appsec_component = components.appsec
|
29
29
|
|
30
30
|
return unless appsec_component
|
31
31
|
|
32
|
-
appsec_component.reconfigure(ruleset: ruleset,
|
32
|
+
appsec_component.reconfigure(ruleset: ruleset, telemetry: telemetry)
|
33
33
|
end
|
34
34
|
|
35
35
|
def reconfigure_lock(&block)
|
@@ -56,6 +56,7 @@ end
|
|
56
56
|
require_relative 'appsec/contrib/rack/integration'
|
57
57
|
require_relative 'appsec/contrib/sinatra/integration'
|
58
58
|
require_relative 'appsec/contrib/rails/integration'
|
59
|
+
require_relative 'appsec/contrib/active_record/integration'
|
59
60
|
require_relative 'appsec/contrib/devise/integration'
|
60
61
|
require_relative 'appsec/contrib/graphql/integration'
|
61
62
|
|
@@ -13,6 +13,7 @@ require_relative '../remote/component'
|
|
13
13
|
require_relative '../../tracing/component'
|
14
14
|
require_relative '../../profiling/component'
|
15
15
|
require_relative '../../appsec/component'
|
16
|
+
require_relative '../../di/component'
|
16
17
|
require_relative '../crashtracking/component'
|
17
18
|
|
18
19
|
module Datadog
|
@@ -83,6 +84,7 @@ module Datadog
|
|
83
84
|
:telemetry,
|
84
85
|
:tracer,
|
85
86
|
:crashtracker,
|
87
|
+
:dynamic_instrumentation,
|
86
88
|
:appsec
|
87
89
|
|
88
90
|
def initialize(settings)
|
@@ -110,12 +112,13 @@ module Datadog
|
|
110
112
|
@runtime_metrics = self.class.build_runtime_metrics_worker(settings)
|
111
113
|
@health_metrics = self.class.build_health_metrics(settings)
|
112
114
|
@appsec = Datadog::AppSec::Component.build_appsec_component(settings, telemetry: telemetry)
|
115
|
+
@dynamic_instrumentation = Datadog::DI::Component.build(settings, agent_settings, telemetry: telemetry)
|
113
116
|
|
114
117
|
self.class.configure_tracing(settings)
|
115
118
|
end
|
116
119
|
|
117
120
|
# Starts up components
|
118
|
-
def startup!(settings)
|
121
|
+
def startup!(settings, old_state: nil)
|
119
122
|
if settings.profiling.enabled
|
120
123
|
if profiler
|
121
124
|
profiler.start
|
@@ -126,6 +129,16 @@ module Datadog
|
|
126
129
|
end
|
127
130
|
end
|
128
131
|
|
132
|
+
if settings.remote.enabled && old_state&.[](:remote_started)
|
133
|
+
# The library was reconfigured and previously it already started
|
134
|
+
# the remote component (i.e., it received at least one request
|
135
|
+
# through the installed Rack middleware which started the remote).
|
136
|
+
# If the new configuration also has remote enabled, start the
|
137
|
+
# new remote right away.
|
138
|
+
# remote should always be not nil here but steep doesn't know this.
|
139
|
+
remote&.start
|
140
|
+
end
|
141
|
+
|
129
142
|
Core::Diagnostics::EnvironmentLogger.collect_and_log!(@environment_logger_extra)
|
130
143
|
end
|
131
144
|
|
@@ -136,6 +149,9 @@ module Datadog
|
|
136
149
|
# Shutdown remote configuration
|
137
150
|
remote.shutdown! if remote
|
138
151
|
|
152
|
+
# Shutdown DI after remote, since remote config triggers DI operations.
|
153
|
+
dynamic_instrumentation&.shutdown!
|
154
|
+
|
139
155
|
# Decommission AppSec
|
140
156
|
appsec.shutdown! if appsec
|
141
157
|
|
@@ -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
|
@@ -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
|
|
@@ -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
|
@@ -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
|
@@ -78,17 +78,18 @@ module Datadog
|
|
78
78
|
registry_lock.synchronize do
|
79
79
|
registry[path] = tp.instruction_sequence
|
80
80
|
end
|
81
|
-
end
|
82
|
-
|
83
|
-
DI.component&.probe_manager&.install_pending_line_probes(path)
|
84
81
|
|
82
|
+
# Also, pending line probes should only be installed for
|
83
|
+
# non-eval'd code.
|
84
|
+
DI.current_component&.probe_manager&.install_pending_line_probes(path)
|
85
|
+
end
|
85
86
|
# Since this method normally is called from customer applications,
|
86
87
|
# rescue any exceptions that might not be handled to not break said
|
87
88
|
# customer applications.
|
88
89
|
rescue => exc
|
89
90
|
# TODO we do not have DI.component defined yet, remove steep:ignore
|
90
91
|
# before release.
|
91
|
-
if component = DI.
|
92
|
+
if component = DI.current_component # steep:ignore
|
92
93
|
raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
93
94
|
component.logger.warn("Unhandled exception in script_compiled trace point: #{exc.class}: #{exc}")
|
94
95
|
component.telemetry&.report(exc, description: "Unhandled exception in script_compiled trace point")
|