datadog 2.23.0 → 2.24.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 +44 -2
- data/ext/datadog_profiling_native_extension/collectors_stack.c +17 -5
- data/ext/datadog_profiling_native_extension/crashtracking_runtime_stacks.c +239 -0
- data/ext/datadog_profiling_native_extension/extconf.rb +4 -1
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +12 -0
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +4 -0
- data/ext/datadog_profiling_native_extension/profiling.c +2 -0
- data/lib/datadog/appsec/context.rb +2 -1
- data/lib/datadog/appsec/remote.rb +1 -9
- data/lib/datadog/appsec/security_engine/result.rb +2 -1
- data/lib/datadog/core/configuration/config_helper.rb +1 -1
- data/lib/datadog/core/configuration/deprecations.rb +2 -2
- data/lib/datadog/core/configuration/option_definition.rb +4 -2
- data/lib/datadog/core/configuration/options.rb +8 -5
- data/lib/datadog/core/configuration/settings.rb +14 -3
- data/lib/datadog/core/configuration/supported_configurations.rb +2 -1
- data/lib/datadog/core/environment/cgroup.rb +52 -25
- data/lib/datadog/core/environment/container.rb +140 -46
- data/lib/datadog/core/environment/ext.rb +1 -0
- data/lib/datadog/core/environment/process.rb +9 -1
- data/lib/datadog/core/rate_limiter.rb +9 -1
- data/lib/datadog/core/remote/client.rb +14 -6
- data/lib/datadog/core/remote/component.rb +6 -4
- data/lib/datadog/core/remote/configuration/content.rb +15 -2
- data/lib/datadog/core/remote/configuration/digest.rb +14 -7
- data/lib/datadog/core/remote/configuration/repository.rb +1 -1
- data/lib/datadog/core/remote/configuration/target.rb +13 -6
- data/lib/datadog/core/remote/transport/config.rb +3 -16
- data/lib/datadog/core/remote/transport/http/config.rb +4 -44
- data/lib/datadog/core/remote/transport/http/negotiation.rb +0 -39
- data/lib/datadog/core/remote/transport/http.rb +13 -24
- data/lib/datadog/core/remote/transport/negotiation.rb +7 -16
- data/lib/datadog/core/telemetry/component.rb +52 -13
- data/lib/datadog/core/telemetry/event/app_started.rb +34 -0
- data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +27 -4
- data/lib/datadog/core/telemetry/metrics_manager.rb +9 -0
- data/lib/datadog/core/telemetry/request.rb +17 -3
- data/lib/datadog/core/telemetry/transport/http/telemetry.rb +2 -32
- data/lib/datadog/core/telemetry/transport/http.rb +21 -16
- data/lib/datadog/core/telemetry/transport/telemetry.rb +3 -10
- data/lib/datadog/core/telemetry/worker.rb +88 -32
- data/lib/datadog/core/transport/ext.rb +2 -0
- data/lib/datadog/core/transport/http/api/endpoint.rb +9 -4
- data/lib/datadog/core/transport/http/api/instance.rb +4 -21
- data/lib/datadog/core/transport/http/builder.rb +9 -5
- data/lib/datadog/core/transport/http/client.rb +19 -8
- data/lib/datadog/core/transport/http.rb +22 -19
- data/lib/datadog/core/transport/response.rb +9 -0
- data/lib/datadog/core/transport/transport.rb +90 -0
- data/lib/datadog/core/utils/only_once_successful.rb +2 -0
- data/lib/datadog/core/utils/time.rb +1 -1
- data/lib/datadog/core/workers/async.rb +10 -1
- data/lib/datadog/core/workers/interval_loop.rb +44 -3
- data/lib/datadog/core/workers/polling.rb +2 -0
- data/lib/datadog/core/workers/queue.rb +100 -1
- data/lib/datadog/data_streams/processor.rb +1 -1
- data/lib/datadog/data_streams/transport/http/stats.rb +1 -36
- data/lib/datadog/data_streams/transport/http.rb +5 -6
- data/lib/datadog/data_streams/transport/stats.rb +3 -17
- data/lib/datadog/di/contrib/active_record.rb +31 -5
- data/lib/datadog/di/el/compiler.rb +8 -4
- data/lib/datadog/di/error.rb +5 -0
- data/lib/datadog/di/instrumenter.rb +17 -4
- data/lib/datadog/di/probe_builder.rb +2 -1
- data/lib/datadog/di/probe_manager.rb +37 -31
- data/lib/datadog/di/probe_notification_builder.rb +15 -2
- data/lib/datadog/di/remote.rb +89 -84
- data/lib/datadog/di/transport/diagnostics.rb +7 -35
- data/lib/datadog/di/transport/http/diagnostics.rb +1 -31
- data/lib/datadog/di/transport/http/input.rb +1 -31
- data/lib/datadog/di/transport/http.rb +28 -17
- data/lib/datadog/di/transport/input.rb +7 -34
- data/lib/datadog/di.rb +61 -5
- data/lib/datadog/open_feature/evaluation_engine.rb +2 -1
- data/lib/datadog/open_feature/remote.rb +3 -10
- data/lib/datadog/open_feature/transport.rb +9 -11
- data/lib/datadog/opentelemetry/api/baggage.rb +1 -1
- data/lib/datadog/opentelemetry/configuration/settings.rb +2 -2
- data/lib/datadog/opentelemetry/metrics.rb +21 -14
- data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +5 -8
- data/lib/datadog/profiling/collectors/code_provenance.rb +27 -2
- data/lib/datadog/profiling/collectors/info.rb +2 -1
- data/lib/datadog/profiling/component.rb +12 -11
- data/lib/datadog/profiling/http_transport.rb +4 -1
- data/lib/datadog/tracing/contrib/extensions.rb +10 -2
- data/lib/datadog/tracing/contrib/karafka/patcher.rb +31 -32
- data/lib/datadog/tracing/contrib/status_range_matcher.rb +2 -1
- data/lib/datadog/tracing/contrib/utils/quantization/hash.rb +3 -1
- data/lib/datadog/tracing/contrib/waterdrop/patcher.rb +6 -3
- data/lib/datadog/tracing/diagnostics/environment_logger.rb +1 -1
- data/lib/datadog/tracing/remote.rb +1 -9
- data/lib/datadog/tracing/span_event.rb +2 -2
- data/lib/datadog/tracing/span_operation.rb +9 -4
- data/lib/datadog/tracing/trace_operation.rb +44 -6
- data/lib/datadog/tracing/tracer.rb +42 -16
- data/lib/datadog/tracing/transport/http/traces.rb +2 -50
- data/lib/datadog/tracing/transport/http.rb +15 -9
- data/lib/datadog/tracing/transport/io/client.rb +1 -1
- data/lib/datadog/tracing/transport/traces.rb +6 -66
- data/lib/datadog/tracing/workers/trace_writer.rb +5 -0
- data/lib/datadog/tracing/writer.rb +1 -0
- data/lib/datadog/version.rb +2 -2
- metadata +7 -13
- data/lib/datadog/core/remote/transport/http/api.rb +0 -53
- data/lib/datadog/core/telemetry/transport/http/api.rb +0 -43
- data/lib/datadog/core/transport/http/api/spec.rb +0 -36
- data/lib/datadog/data_streams/transport/http/api.rb +0 -33
- data/lib/datadog/data_streams/transport/http/client.rb +0 -21
- data/lib/datadog/di/transport/http/api.rb +0 -42
- data/lib/datadog/opentelemetry/api/baggage.rbs +0 -26
- data/lib/datadog/tracing/transport/http/api.rb +0 -44
|
@@ -46,7 +46,7 @@ module Datadog
|
|
|
46
46
|
build_snapshot(context)
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
NANOSECONDS =
|
|
49
|
+
NANOSECONDS = 1_000_000_000
|
|
50
50
|
MILLISECONDS = 1000
|
|
51
51
|
|
|
52
52
|
def build_snapshot(context)
|
|
@@ -134,7 +134,7 @@ module Datadog
|
|
|
134
134
|
format_caller_locations(caller_locations)
|
|
135
135
|
end
|
|
136
136
|
|
|
137
|
-
{
|
|
137
|
+
payload = {
|
|
138
138
|
service: settings.service,
|
|
139
139
|
debugger: {
|
|
140
140
|
type: 'snapshot',
|
|
@@ -189,6 +189,10 @@ module Datadog
|
|
|
189
189
|
message: message,
|
|
190
190
|
timestamp: timestamp,
|
|
191
191
|
}
|
|
192
|
+
|
|
193
|
+
tag_process_tags!(payload, settings)
|
|
194
|
+
|
|
195
|
+
payload
|
|
192
196
|
end
|
|
193
197
|
|
|
194
198
|
def build_status(probe, message:, status:)
|
|
@@ -236,6 +240,15 @@ module Datadog
|
|
|
236
240
|
[message, evaluation_errors]
|
|
237
241
|
end
|
|
238
242
|
|
|
243
|
+
def tag_process_tags!(payload, settings)
|
|
244
|
+
return unless settings.experimental_propagate_process_tags_enabled
|
|
245
|
+
|
|
246
|
+
process_tags = Core::Environment::Process.serialized
|
|
247
|
+
return if process_tags.empty?
|
|
248
|
+
|
|
249
|
+
payload[:process_tags] = process_tags
|
|
250
|
+
end
|
|
251
|
+
|
|
239
252
|
def timestamp_now
|
|
240
253
|
(Core::Utils::Time.now.to_f * MILLISECONDS).to_i
|
|
241
254
|
end
|
data/lib/datadog/di/remote.rb
CHANGED
|
@@ -12,8 +12,6 @@ module Datadog
|
|
|
12
12
|
#
|
|
13
13
|
# @api private
|
|
14
14
|
module Remote
|
|
15
|
-
class ReadError < StandardError; end
|
|
16
|
-
|
|
17
15
|
class << self
|
|
18
16
|
PRODUCT = 'LIVE_DEBUGGING'
|
|
19
17
|
|
|
@@ -28,7 +26,7 @@ module Datadog
|
|
|
28
26
|
end
|
|
29
27
|
|
|
30
28
|
def receivers(telemetry)
|
|
31
|
-
receiver do |repository,
|
|
29
|
+
receiver do |repository, changes|
|
|
32
30
|
# DEV: Filter our by product. Given it will be very common
|
|
33
31
|
# DEV: we can filter this out before we receive the data in this method.
|
|
34
32
|
# DEV: Apply this refactor to AppSec as well if implemented.
|
|
@@ -41,84 +39,23 @@ module Datadog
|
|
|
41
39
|
# If the component is nil for some reason, we also don't have a
|
|
42
40
|
# logger instance to report the issue.
|
|
43
41
|
if component
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
# TODO test exception capture
|
|
60
|
-
probe_manager.add_probe(probe)
|
|
61
|
-
content.applied
|
|
62
|
-
rescue DI::Error::DITargetNotInRegistry => exc
|
|
63
|
-
component.telemetry&.report(exc, description: "Line probe is targeting a loaded file that is not in code tracker")
|
|
64
|
-
|
|
65
|
-
payload = probe_notification_builder.build_errored(probe, exc)
|
|
66
|
-
probe_notifier_worker.add_status(payload)
|
|
67
|
-
|
|
68
|
-
# If a probe fails to install, we will mark the content
|
|
69
|
-
# as errored. On subsequent remote configuration application
|
|
70
|
-
# attemps, probe manager will raise the "previously errored"
|
|
71
|
-
# exception and we'll rescue it here, again marking the
|
|
72
|
-
# content as errored but with a somewhat different exception
|
|
73
|
-
# message.
|
|
74
|
-
# TODO assert content state (errored for this example)
|
|
75
|
-
content.errored("Error applying dynamic instrumentation configuration: #{exc.class.name} #{exc.message}")
|
|
76
|
-
rescue => exc
|
|
77
|
-
raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
|
78
|
-
|
|
79
|
-
component.logger.debug { "di: unhandled exception adding #{probe.type} probe at #{probe.location} (#{probe.id}) in DI remote receiver: #{exc.class}: #{exc}" }
|
|
80
|
-
component.telemetry&.report(exc, description: "Unhandled exception adding probe in DI remote receiver")
|
|
81
|
-
|
|
82
|
-
# TODO test this path
|
|
83
|
-
payload = probe_notification_builder.build_errored(probe, exc)
|
|
84
|
-
probe_notifier_worker.add_status(payload)
|
|
85
|
-
|
|
86
|
-
# If a probe fails to install, we will mark the content
|
|
87
|
-
# as errored. On subsequent remote configuration application
|
|
88
|
-
# attemps, probe manager will raise the "previously errored"
|
|
89
|
-
# exception and we'll rescue it here, again marking the
|
|
90
|
-
# content as errored but with a somewhat different exception
|
|
91
|
-
# message.
|
|
92
|
-
# TODO assert content state (errored for this example)
|
|
93
|
-
content.errored("Error applying dynamic instrumentation configuration: #{exc.class.name} #{exc.message}")
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
# Important: even if processing fails for this probe config,
|
|
97
|
-
# we need to note it as being current so that we do not
|
|
98
|
-
# try to remove instrumentation that is still supposed to be
|
|
99
|
-
# active.
|
|
100
|
-
current_probe_ids[probe_spec.fetch('id')] = true
|
|
101
|
-
rescue => exc
|
|
102
|
-
raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
|
103
|
-
|
|
104
|
-
component.logger.debug { "di: unhandled exception handling a probe in DI remote receiver: #{exc.class}: #{exc}" }
|
|
105
|
-
component.telemetry&.report(exc, description: "Unhandled exception handling probe in DI remote receiver")
|
|
106
|
-
|
|
107
|
-
# TODO assert content state (errored for this example)
|
|
108
|
-
content.errored("Error applying dynamic instrumentation configuration: #{exc.class.name} #{exc.message}")
|
|
109
|
-
end
|
|
42
|
+
changes.each do |change|
|
|
43
|
+
case change.type
|
|
44
|
+
when :insert
|
|
45
|
+
add_probe(change.content, component)
|
|
46
|
+
when :update
|
|
47
|
+
# We do not implement updates at the moment, remove the
|
|
48
|
+
# probe and reinstall.
|
|
49
|
+
remove_probe(change.content, component)
|
|
50
|
+
add_probe(change.content, component)
|
|
51
|
+
when :delete
|
|
52
|
+
remove_probe(change.previous, component)
|
|
53
|
+
else
|
|
54
|
+
# This really should never happen since we generate the
|
|
55
|
+
# change types in the library.
|
|
56
|
+
component.logger.debug { "di: unrecognized change type: #{change.type}" }
|
|
110
57
|
end
|
|
111
58
|
end
|
|
112
|
-
|
|
113
|
-
begin
|
|
114
|
-
# TODO test exception capture
|
|
115
|
-
probe_manager.remove_other_probes(current_probe_ids.keys)
|
|
116
|
-
rescue => exc
|
|
117
|
-
raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
|
118
|
-
|
|
119
|
-
component.logger.debug { "di: unhandled exception removing probes in DI remote receiver: #{exc.class}: #{exc}" }
|
|
120
|
-
component.telemetry&.report(exc, description: "Unhandled exception removing probes in DI remote receiver")
|
|
121
|
-
end
|
|
122
59
|
end
|
|
123
60
|
end
|
|
124
61
|
end
|
|
@@ -130,14 +67,82 @@ module Datadog
|
|
|
130
67
|
|
|
131
68
|
private
|
|
132
69
|
|
|
133
|
-
def
|
|
134
|
-
|
|
70
|
+
def add_probe(content, component)
|
|
71
|
+
probe_spec = parse_content(content)
|
|
72
|
+
probe = component.parse_probe_spec_and_notify(probe_spec)
|
|
73
|
+
component.logger.debug { "di: received #{probe.type} probe at #{probe.location} (#{probe.id}) via RC" }
|
|
74
|
+
|
|
75
|
+
begin
|
|
76
|
+
# TODO test exception capture
|
|
77
|
+
component.probe_manager.add_probe(probe)
|
|
78
|
+
content.applied
|
|
79
|
+
rescue DI::Error::DITargetNotInRegistry => exc
|
|
80
|
+
component.telemetry&.report(exc, description: "Line probe is targeting a loaded file that is not in code tracker")
|
|
81
|
+
|
|
82
|
+
payload = component.probe_notification_builder.build_errored(probe, exc)
|
|
83
|
+
component.probe_notifier_worker.add_status(payload)
|
|
84
|
+
|
|
85
|
+
# If a probe fails to install, we will mark the content
|
|
86
|
+
# as errored. On subsequent remote configuration application
|
|
87
|
+
# attemps, probe manager will raise the "previously errored"
|
|
88
|
+
# exception and we'll rescue it here, again marking the
|
|
89
|
+
# content as errored but with a somewhat different exception
|
|
90
|
+
# message.
|
|
91
|
+
# TODO assert content state (errored for this example)
|
|
92
|
+
content.errored("Error applying dynamic instrumentation configuration: #{exc.class.name} #{exc.message}")
|
|
93
|
+
rescue => exc
|
|
94
|
+
raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
|
95
|
+
|
|
96
|
+
component.logger.debug { "di: unhandled exception adding #{probe.type} probe at #{probe.location} (#{probe.id}) in DI remote receiver: #{exc.class}: #{exc}" }
|
|
97
|
+
component.telemetry&.report(exc, description: "Unhandled exception adding probe in DI remote receiver")
|
|
98
|
+
|
|
99
|
+
# TODO test this path
|
|
100
|
+
payload = component.probe_notification_builder.build_errored(probe, exc)
|
|
101
|
+
component.probe_notifier_worker.add_status(payload)
|
|
102
|
+
|
|
103
|
+
# If a probe fails to install, we will mark the content
|
|
104
|
+
# as errored. On subsequent remote configuration application
|
|
105
|
+
# attemps, probe manager will raise the "previously errored"
|
|
106
|
+
# exception and we'll rescue it here, again marking the
|
|
107
|
+
# content as errored but with a somewhat different exception
|
|
108
|
+
# message.
|
|
109
|
+
# TODO assert content state (errored for this example)
|
|
110
|
+
content.errored("Error applying dynamic instrumentation configuration: #{exc.class.name} #{exc.message}")
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Important: even if processing fails for this probe config,
|
|
114
|
+
# we need to note it as being current so that we do not
|
|
115
|
+
# try to remove instrumentation that is still supposed to be
|
|
116
|
+
# active.
|
|
117
|
+
#current_probe_ids[probe_spec.fetch('id')] = true
|
|
118
|
+
rescue => exc
|
|
119
|
+
raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
|
120
|
+
|
|
121
|
+
component.logger.debug { "di: unhandled exception handling a probe in DI remote receiver: #{exc.class}: #{exc}" }
|
|
122
|
+
component.telemetry&.report(exc, description: "Unhandled exception handling probe in DI remote receiver")
|
|
135
123
|
|
|
136
|
-
content
|
|
124
|
+
# TODO assert content state (errored for this example)
|
|
125
|
+
content.errored("Error applying dynamic instrumentation configuration: #{exc.class.name} #{exc.message}")
|
|
126
|
+
end
|
|
137
127
|
|
|
138
|
-
|
|
128
|
+
# This method does not mark +previous_content+ as succeeded or errored,
|
|
129
|
+
# because that content is from a previous RC response and has already
|
|
130
|
+
# been marked. Removal of probes happens when an RC entry disappears,
|
|
131
|
+
# as such there is nothing to mark.
|
|
132
|
+
def remove_probe(previous_content, component)
|
|
133
|
+
# TODO test exception capture
|
|
134
|
+
probe_spec = parse_content(previous_content)
|
|
135
|
+
probe_id = probe_spec.fetch('id')
|
|
136
|
+
component.probe_manager.remove_probe(probe_id)
|
|
137
|
+
rescue => exc
|
|
138
|
+
raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
|
139
|
+
|
|
140
|
+
component.logger.debug { "di: unhandled exception removing probes in DI remote receiver: #{exc.class}: #{exc}" }
|
|
141
|
+
component.telemetry&.report(exc, description: "Unhandled exception removing probes in DI remote receiver")
|
|
142
|
+
end
|
|
139
143
|
|
|
140
|
-
|
|
144
|
+
def parse_content(content)
|
|
145
|
+
JSON.parse(content.data)
|
|
141
146
|
end
|
|
142
147
|
end
|
|
143
148
|
end
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'json'
|
|
4
|
+
|
|
3
5
|
require_relative '../../core/transport/parcel'
|
|
6
|
+
require_relative '../../core/transport/request'
|
|
7
|
+
require_relative '../../core/transport/transport'
|
|
4
8
|
require_relative 'http/diagnostics'
|
|
5
9
|
|
|
6
10
|
module Datadog
|
|
@@ -14,46 +18,14 @@ module Datadog
|
|
|
14
18
|
class Request < Datadog::Core::Transport::Request
|
|
15
19
|
end
|
|
16
20
|
|
|
17
|
-
class Transport
|
|
18
|
-
attr_reader :client, :apis, :default_api, :current_api_id, :logger
|
|
19
|
-
|
|
20
|
-
def initialize(apis, default_api, logger:)
|
|
21
|
-
@apis = apis
|
|
22
|
-
@logger = logger
|
|
23
|
-
|
|
24
|
-
@client = DI::Transport::HTTP::Diagnostics::Client.new(current_api, logger: logger)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def current_api
|
|
28
|
-
@apis[HTTP::API::DIAGNOSTICS]
|
|
29
|
-
end
|
|
30
|
-
|
|
21
|
+
class Transport < Core::Transport::Transport
|
|
31
22
|
def send_diagnostics(payload)
|
|
23
|
+
# TODO use transport encoder functionality?
|
|
32
24
|
json = JSON.dump(payload)
|
|
33
25
|
parcel = EncodedParcel.new(json)
|
|
34
26
|
request = Request.new(parcel)
|
|
35
27
|
|
|
36
|
-
|
|
37
|
-
unless response.ok?
|
|
38
|
-
# TODO Datadog::Core::Transport::InternalErrorResponse
|
|
39
|
-
# does not have +code+ method, what is the actual API of
|
|
40
|
-
# these response objects?
|
|
41
|
-
raise Error::AgentCommunicationError, "send_diagnostics failed: #{begin
|
|
42
|
-
response.code
|
|
43
|
-
rescue
|
|
44
|
-
"???"
|
|
45
|
-
end}: #{response.payload}"
|
|
46
|
-
end
|
|
47
|
-
rescue Error::AgentCommunicationError
|
|
48
|
-
raise
|
|
49
|
-
# Datadog::Core::Transport does not perform any exception mapping,
|
|
50
|
-
# therefore we could have any exception here from failure to parse
|
|
51
|
-
# agent URI for example.
|
|
52
|
-
# If we ever implement retries for network errors, we should distinguish
|
|
53
|
-
# actual network errors from non-network errors that are raised by
|
|
54
|
-
# transport code.
|
|
55
|
-
rescue => exc
|
|
56
|
-
raise Error::AgentCommunicationError, "send_diagnostics failed: #{exc.class}: #{exc}"
|
|
28
|
+
client.send_request(:diagnostics, request)
|
|
57
29
|
end
|
|
58
30
|
end
|
|
59
31
|
end
|
|
@@ -1,43 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative '../../../core/transport/http/api/
|
|
4
|
-
require_relative '../../../core/transport/http/api/spec'
|
|
5
|
-
require_relative '../../../core/transport/http/client'
|
|
3
|
+
require_relative '../../../core/transport/http/api/endpoint'
|
|
6
4
|
|
|
7
5
|
module Datadog
|
|
8
6
|
module DI
|
|
9
7
|
module Transport
|
|
10
8
|
module HTTP
|
|
11
9
|
module Diagnostics
|
|
12
|
-
class Client < Core::Transport::HTTP::Client
|
|
13
|
-
def send_diagnostics_payload(request)
|
|
14
|
-
send_request(request) do |api, env|
|
|
15
|
-
api.send_diagnostics(env)
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
|
|
20
10
|
module API
|
|
21
|
-
class Instance < Core::Transport::HTTP::API::Instance
|
|
22
|
-
def send_diagnostics(env)
|
|
23
|
-
raise Core::Transport::HTTP::API::Instance::EndpointNotSupportedError.new('diagnostics', self) unless spec.is_a?(Diagnostics::API::Spec)
|
|
24
|
-
|
|
25
|
-
spec.send_diagnostics(env) do |request_env|
|
|
26
|
-
call(request_env)
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
class Spec < Core::Transport::HTTP::API::Spec
|
|
32
|
-
attr_accessor :diagnostics
|
|
33
|
-
|
|
34
|
-
def send_diagnostics(env, &block)
|
|
35
|
-
raise Core::Transport::HTTP::API::Spec::EndpointNotDefinedError.new('diagnostics', self) if diagnostics.nil?
|
|
36
|
-
|
|
37
|
-
diagnostics.call(env, &block)
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
11
|
class Endpoint < Datadog::Core::Transport::HTTP::API::Endpoint
|
|
42
12
|
attr_reader :encoder
|
|
43
13
|
|
|
@@ -1,43 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative '../../../core/transport/http/api/
|
|
4
|
-
require_relative '../../../core/transport/http/api/spec'
|
|
5
|
-
require_relative '../../../core/transport/http/client'
|
|
3
|
+
require_relative '../../../core/transport/http/api/endpoint'
|
|
6
4
|
|
|
7
5
|
module Datadog
|
|
8
6
|
module DI
|
|
9
7
|
module Transport
|
|
10
8
|
module HTTP
|
|
11
9
|
module Input
|
|
12
|
-
class Client < Core::Transport::HTTP::Client
|
|
13
|
-
def send_input_payload(request)
|
|
14
|
-
send_request(request) do |api, env|
|
|
15
|
-
api.send_input(env)
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
|
|
20
10
|
module API
|
|
21
|
-
class Instance < Core::Transport::HTTP::API::Instance
|
|
22
|
-
def send_input(env)
|
|
23
|
-
raise Core::Transport::HTTP::API::Instance::EndpointNotSupportedError.new('input', self) unless spec.is_a?(Input::API::Spec)
|
|
24
|
-
|
|
25
|
-
spec.send_input(env) do |request_env|
|
|
26
|
-
call(request_env)
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
class Spec < Core::Transport::HTTP::API::Spec
|
|
32
|
-
attr_accessor :input
|
|
33
|
-
|
|
34
|
-
def send_input(env, &block)
|
|
35
|
-
raise Core::Transport::HTTP::API::Spec::EndpointNotDefinedError.new('input', self) if input.nil?
|
|
36
|
-
|
|
37
|
-
input.call(env, &block)
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
11
|
class Endpoint < Datadog::Core::Transport::HTTP::API::Endpoint
|
|
42
12
|
HEADER_CONTENT_TYPE = 'Content-Type'
|
|
43
13
|
|
|
@@ -1,31 +1,46 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative '../../core/encoding'
|
|
4
|
+
require_relative '../../core/transport/http'
|
|
3
5
|
require_relative 'diagnostics'
|
|
4
6
|
require_relative 'input'
|
|
5
|
-
require_relative 'http/api'
|
|
6
|
-
require_relative '../../core/transport/http'
|
|
7
7
|
|
|
8
8
|
module Datadog
|
|
9
9
|
module DI
|
|
10
10
|
module Transport
|
|
11
11
|
# Namespace for HTTP transport components
|
|
12
12
|
module HTTP
|
|
13
|
-
|
|
13
|
+
DIAGNOSTICS = Diagnostics::API::Endpoint.new(
|
|
14
|
+
'/debugger/v1/diagnostics',
|
|
15
|
+
Core::Encoding::JSONEncoder,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
INPUT = Input::API::Endpoint.new(
|
|
19
|
+
'/debugger/v2/input',
|
|
20
|
+
Core::Encoding::JSONEncoder,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
LEGACY_INPUT = Input::API::Endpoint.new(
|
|
24
|
+
# We used to use /debugger/v1/input, but now input
|
|
25
|
+
# payloads should be going to the diagnostics endpoint
|
|
26
|
+
# which I gather performs data redaction.
|
|
27
|
+
'/debugger/v1/diagnostics',
|
|
28
|
+
Core::Encoding::JSONEncoder,
|
|
29
|
+
)
|
|
14
30
|
|
|
15
31
|
# Builds a new Transport::HTTP::Client with default settings
|
|
16
32
|
# Pass a block to override any settings.
|
|
17
|
-
def diagnostics(
|
|
33
|
+
def self.diagnostics(
|
|
18
34
|
agent_settings:,
|
|
19
35
|
logger:,
|
|
20
|
-
api_version: nil,
|
|
21
36
|
headers: nil
|
|
22
37
|
)
|
|
23
|
-
Core::Transport::HTTP.build(
|
|
38
|
+
Core::Transport::HTTP.build(
|
|
24
39
|
logger: logger,
|
|
25
|
-
agent_settings: agent_settings,
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
transport.api
|
|
40
|
+
agent_settings: agent_settings,
|
|
41
|
+
headers: headers,
|
|
42
|
+
) do |transport|
|
|
43
|
+
transport.api 'diagnostics', DIAGNOSTICS
|
|
29
44
|
|
|
30
45
|
# Call block to apply any customization, if provided
|
|
31
46
|
yield(transport) if block_given?
|
|
@@ -34,22 +49,18 @@ module Datadog
|
|
|
34
49
|
|
|
35
50
|
# Builds a new Transport::HTTP::Client with default settings
|
|
36
51
|
# Pass a block to override any settings.
|
|
37
|
-
def input(
|
|
52
|
+
def self.input(
|
|
38
53
|
agent_settings:,
|
|
39
54
|
logger:,
|
|
40
|
-
api_version: nil,
|
|
41
55
|
headers: nil
|
|
42
56
|
)
|
|
43
57
|
Core::Transport::HTTP.build(
|
|
44
|
-
api_instance_class: Input::API::Instance,
|
|
45
58
|
logger: logger,
|
|
46
59
|
agent_settings: agent_settings,
|
|
47
|
-
api_version: api_version,
|
|
48
60
|
headers: headers,
|
|
49
61
|
) do |transport|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
transport.api API::INPUT, apis[API::INPUT]
|
|
62
|
+
transport.api 'input', INPUT, fallback: 'legacy_input', default: true
|
|
63
|
+
transport.api 'legacy_input', LEGACY_INPUT
|
|
53
64
|
|
|
54
65
|
# Call block to apply any customization, if provided
|
|
55
66
|
yield(transport) if block_given?
|
|
@@ -5,6 +5,7 @@ require_relative '../../core/encoding'
|
|
|
5
5
|
require_relative '../../core/tag_builder'
|
|
6
6
|
require_relative '../../core/transport/parcel'
|
|
7
7
|
require_relative '../../core/transport/request'
|
|
8
|
+
require_relative '../../core/transport/transport'
|
|
8
9
|
require_relative '../error'
|
|
9
10
|
require_relative 'http/input'
|
|
10
11
|
|
|
@@ -26,9 +27,7 @@ module Datadog
|
|
|
26
27
|
end
|
|
27
28
|
end
|
|
28
29
|
|
|
29
|
-
class Transport
|
|
30
|
-
attr_reader :client, :apis, :default_api, :current_api_id, :logger
|
|
31
|
-
|
|
30
|
+
class Transport < Core::Transport::Transport
|
|
32
31
|
# The limit on an individual snapshot payload, aka "log line",
|
|
33
32
|
# is 1 MB.
|
|
34
33
|
#
|
|
@@ -48,17 +47,6 @@ module Datadog
|
|
|
48
47
|
# max chunk size, it will still get sent out.
|
|
49
48
|
DEFAULT_CHUNK_SIZE = 2 * 1024 * 1024
|
|
50
49
|
|
|
51
|
-
def initialize(apis, default_api, logger:)
|
|
52
|
-
@apis = apis
|
|
53
|
-
@logger = logger
|
|
54
|
-
|
|
55
|
-
@client = DI::Transport::HTTP::Input::Client.new(current_api, logger: logger)
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def current_api
|
|
59
|
-
@apis[HTTP::API::INPUT]
|
|
60
|
-
end
|
|
61
|
-
|
|
62
50
|
def send_input(payload, tags)
|
|
63
51
|
# Tags are the same for all chunks, serialize them one time.
|
|
64
52
|
serialized_tags = Core::TagBuilder.serialize_tags(tags)
|
|
@@ -101,27 +89,12 @@ module Datadog
|
|
|
101
89
|
parcel = EncodedParcel.new(chunked_payload)
|
|
102
90
|
request = Request.new(parcel, serialized_tags)
|
|
103
91
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
raise Error::AgentCommunicationError, "send_input failed: #{begin
|
|
110
|
-
response.code
|
|
111
|
-
rescue
|
|
112
|
-
"???"
|
|
113
|
-
end}: #{response.payload}"
|
|
92
|
+
client.send_request(:input, request).tap do |response|
|
|
93
|
+
if downgrade?(response)
|
|
94
|
+
downgrade!
|
|
95
|
+
return send_input_chunk(chunked_payload, serialized_tags)
|
|
96
|
+
end
|
|
114
97
|
end
|
|
115
|
-
rescue Error::AgentCommunicationError
|
|
116
|
-
raise
|
|
117
|
-
# Datadog::Core::Transport does not perform any exception mapping,
|
|
118
|
-
# therefore we could have any exception here from failure to parse
|
|
119
|
-
# agent URI for example.
|
|
120
|
-
# If we ever implement retries for network errors, we should distinguish
|
|
121
|
-
# actual network errors from non-network errors that are raised by
|
|
122
|
-
# transport code.
|
|
123
|
-
rescue => exc
|
|
124
|
-
raise Error::AgentCommunicationError, "send_input failed: #{exc.class}: #{exc}"
|
|
125
98
|
end
|
|
126
99
|
end
|
|
127
100
|
end
|
data/lib/datadog/di.rb
CHANGED
|
@@ -9,16 +9,13 @@ module Datadog
|
|
|
9
9
|
#
|
|
10
10
|
# @api private
|
|
11
11
|
module DI
|
|
12
|
+
INSTRUMENTED_COUNTERS_LOCK = Mutex.new
|
|
13
|
+
|
|
12
14
|
class << self
|
|
13
15
|
def enabled?
|
|
14
16
|
Datadog.configuration.dynamic_instrumentation.enabled
|
|
15
17
|
end
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
# Expose DI to global shared objects
|
|
19
|
-
Extensions.activate!
|
|
20
18
|
|
|
21
|
-
class << self
|
|
22
19
|
# This method is called from DI Remote handler to issue DI operations
|
|
23
20
|
# to the probe manager (add or remove probes).
|
|
24
21
|
#
|
|
@@ -31,6 +28,65 @@ module Datadog
|
|
|
31
28
|
def component
|
|
32
29
|
Datadog.send(:components).dynamic_instrumentation
|
|
33
30
|
end
|
|
31
|
+
|
|
32
|
+
# Track how many outstanding instrumentations are in DI.
|
|
33
|
+
#
|
|
34
|
+
# It is hard to find the actual instrumentations - there is no
|
|
35
|
+
# method provided by Ruby to list all trace points, and we would
|
|
36
|
+
# need to manually track our instrumentation modules for method probes.
|
|
37
|
+
# Plus, tracking the modules could create active references to
|
|
38
|
+
# instrumentation, which is not desired.
|
|
39
|
+
#
|
|
40
|
+
# A simpler solution is to maintain a counter which is increased
|
|
41
|
+
# whenever a probe is installed and decreased when a probe is removed.
|
|
42
|
+
#
|
|
43
|
+
# This counter does not include pending probes - being not installed,
|
|
44
|
+
# those pose no concerns to customer applications.
|
|
45
|
+
def instrumented_count(kind = nil)
|
|
46
|
+
INSTRUMENTED_COUNTERS_LOCK.synchronize do
|
|
47
|
+
if defined?(@instrumented_count)
|
|
48
|
+
if kind
|
|
49
|
+
validate_kind!(kind)
|
|
50
|
+
@instrumented_count[kind] || 0
|
|
51
|
+
else
|
|
52
|
+
@instrumented_count.inject(0) do |sum, (_kind, count)|
|
|
53
|
+
sum + count
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
else
|
|
57
|
+
0
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def instrumented_count_inc(kind)
|
|
63
|
+
validate_kind!(kind)
|
|
64
|
+
INSTRUMENTED_COUNTERS_LOCK.synchronize do
|
|
65
|
+
@instrumented_count = Hash.new(0) unless defined?(@instrumented_count)
|
|
66
|
+
@instrumented_count[kind] += 1
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def instrumented_count_dec(kind)
|
|
71
|
+
validate_kind!(kind)
|
|
72
|
+
INSTRUMENTED_COUNTERS_LOCK.synchronize do
|
|
73
|
+
@instrumented_count = Hash.new(0) unless defined?(@instrumented_count)
|
|
74
|
+
if @instrumented_count[kind] <= 0
|
|
75
|
+
Datadog.logger.debug { "di: attempting to decrease instrumented count below zero for #{kind}" }
|
|
76
|
+
return
|
|
77
|
+
end
|
|
78
|
+
@instrumented_count[kind] -= 1
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
private def validate_kind!(kind)
|
|
83
|
+
unless %i[line method].include?(kind)
|
|
84
|
+
raise ArgumentError, "Invalid kind: #{kind}"
|
|
85
|
+
end
|
|
86
|
+
end
|
|
34
87
|
end
|
|
88
|
+
|
|
89
|
+
# Expose DI to global shared objects
|
|
90
|
+
Extensions.activate!
|
|
35
91
|
end
|
|
36
92
|
end
|
|
@@ -9,7 +9,8 @@ module Datadog
|
|
|
9
9
|
module OpenFeature
|
|
10
10
|
# This class performs the evaluation of the feature flag
|
|
11
11
|
class EvaluationEngine
|
|
12
|
-
|
|
12
|
+
# Steep: https://github.com/soutaro/steep/issues/1880
|
|
13
|
+
ReconfigurationError = Class.new(StandardError) # steep:ignore IncompatibleAssignment
|
|
13
14
|
|
|
14
15
|
ALLOWED_TYPES = %i[boolean string number float integer object].freeze
|
|
15
16
|
|