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
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative '
|
|
4
|
-
require_relative 'http/api'
|
|
3
|
+
require_relative '../../encoding'
|
|
5
4
|
require_relative '../../transport/http'
|
|
5
|
+
require_relative 'telemetry'
|
|
6
6
|
|
|
7
7
|
module Datadog
|
|
8
8
|
module Core
|
|
@@ -10,6 +10,16 @@ module Datadog
|
|
|
10
10
|
module Transport
|
|
11
11
|
# Namespace for HTTP transport components
|
|
12
12
|
module HTTP
|
|
13
|
+
AGENT_TELEMETRY = Telemetry::API::Endpoint.new(
|
|
14
|
+
'/telemetry/proxy/api/v2/apmtelemetry',
|
|
15
|
+
Core::Encoding::JSONEncoder,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
AGENTLESS_TELEMETRY = Telemetry::API::Endpoint.new(
|
|
19
|
+
'/api/v2/apmtelemetry',
|
|
20
|
+
Core::Encoding::JSONEncoder,
|
|
21
|
+
)
|
|
22
|
+
|
|
13
23
|
module_function
|
|
14
24
|
|
|
15
25
|
# Builds a new Transport::HTTP::Client with default settings
|
|
@@ -18,18 +28,14 @@ module Datadog
|
|
|
18
28
|
agent_settings:,
|
|
19
29
|
logger:,
|
|
20
30
|
api_key: nil,
|
|
21
|
-
api_version: nil,
|
|
22
31
|
headers: nil
|
|
23
32
|
)
|
|
24
|
-
Core::Transport::HTTP.build(
|
|
33
|
+
Core::Transport::HTTP.build(
|
|
25
34
|
logger: logger,
|
|
26
35
|
agent_settings: agent_settings,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
transport.api API::AGENTLESS_TELEMETRY, apis[API::AGENTLESS_TELEMETRY]
|
|
32
|
-
|
|
36
|
+
headers: headers
|
|
37
|
+
) do |transport|
|
|
38
|
+
transport.api 'agentless_telemetry', AGENTLESS_TELEMETRY
|
|
33
39
|
# Call block to apply any customization, if provided
|
|
34
40
|
yield(transport) if block_given?
|
|
35
41
|
end.to_transport(Core::Telemetry::Transport::Telemetry::Transport).tap do |transport|
|
|
@@ -42,15 +48,14 @@ module Datadog
|
|
|
42
48
|
def agent_telemetry(
|
|
43
49
|
agent_settings:,
|
|
44
50
|
logger:,
|
|
45
|
-
api_version: nil,
|
|
46
51
|
headers: nil
|
|
47
52
|
)
|
|
48
|
-
Core::Transport::HTTP.build(
|
|
53
|
+
Core::Transport::HTTP.build(
|
|
49
54
|
logger: logger,
|
|
50
|
-
agent_settings: agent_settings,
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
transport.api
|
|
55
|
+
agent_settings: agent_settings,
|
|
56
|
+
headers: headers
|
|
57
|
+
) do |transport|
|
|
58
|
+
transport.api 'agent_telemetry', AGENT_TELEMETRY
|
|
54
59
|
|
|
55
60
|
# Call block to apply any customization, if provided
|
|
56
61
|
yield(transport) if block_given?
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative '../../transport/parcel'
|
|
4
|
+
require_relative '../../transport/transport'
|
|
4
5
|
require_relative 'http/telemetry'
|
|
5
6
|
|
|
6
7
|
module Datadog
|
|
@@ -23,23 +24,15 @@ module Datadog
|
|
|
23
24
|
end
|
|
24
25
|
end
|
|
25
26
|
|
|
26
|
-
class Transport
|
|
27
|
-
attr_reader :client, :apis, :default_api, :current_api_id, :logger
|
|
27
|
+
class Transport < Core::Transport::Transport
|
|
28
28
|
attr_accessor :api_key
|
|
29
29
|
|
|
30
|
-
def initialize(apis, default_api, logger:)
|
|
31
|
-
@apis = apis
|
|
32
|
-
@logger = logger
|
|
33
|
-
|
|
34
|
-
@client = Core::Telemetry::Transport::HTTP::Telemetry::Client.new(@apis[default_api], logger: logger)
|
|
35
|
-
end
|
|
36
|
-
|
|
37
30
|
def send_telemetry(request_type:, payload:)
|
|
38
31
|
json = JSON.dump(payload)
|
|
39
32
|
parcel = EncodedParcel.new(json)
|
|
40
33
|
request = Request.new(request_type, parcel, api_key)
|
|
41
34
|
|
|
42
|
-
@client.
|
|
35
|
+
@client.send_request(:telemetry, request)
|
|
43
36
|
# Perform no error checking here
|
|
44
37
|
end
|
|
45
38
|
end
|
|
@@ -9,7 +9,10 @@ require_relative '../workers/queue'
|
|
|
9
9
|
module Datadog
|
|
10
10
|
module Core
|
|
11
11
|
module Telemetry
|
|
12
|
-
# Accumulates events and sends them to the API at a regular interval,
|
|
12
|
+
# Accumulates events and sends them to the API at a regular interval,
|
|
13
|
+
# including heartbeat event.
|
|
14
|
+
#
|
|
15
|
+
# @api private
|
|
13
16
|
class Worker
|
|
14
17
|
include Core::Workers::Queue
|
|
15
18
|
include Core::Workers::Polling
|
|
@@ -40,11 +43,23 @@ module Datadog
|
|
|
40
43
|
self.enabled = enabled
|
|
41
44
|
# Workers::IntervalLoop settings
|
|
42
45
|
self.loop_base_interval = metrics_aggregation_interval_seconds
|
|
43
|
-
|
|
46
|
+
# We actually restart the worker after fork, but this is done
|
|
47
|
+
# via the AtForkMonkeyPatch rather than the worker fork policy
|
|
48
|
+
# because we also need to reset state outside of the worker
|
|
49
|
+
# (e.g. the metrics).
|
|
50
|
+
self.fork_policy = Core::Workers::Async::Thread::FORK_POLICY_RESTART
|
|
44
51
|
|
|
45
52
|
@shutdown_timeout = shutdown_timeout
|
|
46
53
|
@buffer_size = buffer_size
|
|
47
54
|
|
|
55
|
+
initialize_state
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# To make the method calls clear, the initialization code is in this
|
|
59
|
+
# method called +initialize_state+ which is called from +after_fork+.
|
|
60
|
+
# This way users of this class (e.g. telemetry Component) do not
|
|
61
|
+
# need to invoke +initialize_state+ directly, which can be confusing.
|
|
62
|
+
private def initialize_state
|
|
48
63
|
self.buffer = buffer_klass.new(@buffer_size)
|
|
49
64
|
|
|
50
65
|
@initial_event_once = Utils::OnlyOnceSuccessful.new(APP_STARTED_EVENT_RETRIES)
|
|
@@ -53,12 +68,13 @@ module Datadog
|
|
|
53
68
|
attr_reader :logger
|
|
54
69
|
attr_reader :initial_event_once
|
|
55
70
|
attr_reader :initial_event
|
|
71
|
+
attr_reader :emitter
|
|
56
72
|
|
|
57
73
|
# Returns true if worker thread is successfully started,
|
|
58
74
|
# false if worker thread was not started but telemetry is enabled,
|
|
59
75
|
# nil if telemetry is disabled.
|
|
60
76
|
def start(initial_event)
|
|
61
|
-
return
|
|
77
|
+
return unless enabled?
|
|
62
78
|
|
|
63
79
|
@initial_event = initial_event
|
|
64
80
|
|
|
@@ -79,7 +95,7 @@ module Datadog
|
|
|
79
95
|
# for not enqueueing event (presently) is that telemetry is disabled
|
|
80
96
|
# altogether, and in this case other methods return nil.
|
|
81
97
|
def enqueue(event)
|
|
82
|
-
return
|
|
98
|
+
return unless enabled?
|
|
83
99
|
|
|
84
100
|
buffer.push(event)
|
|
85
101
|
true
|
|
@@ -102,38 +118,16 @@ module Datadog
|
|
|
102
118
|
# been flushed.
|
|
103
119
|
#
|
|
104
120
|
# @api private
|
|
105
|
-
def flush
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
loop do
|
|
110
|
-
# The AppStarted event is triggered by the worker itself,
|
|
111
|
-
# from the worker thread. As such the main thread has no way
|
|
112
|
-
# to delay itself until that event is queued and we need some
|
|
113
|
-
# way to wait until that event is sent out to assert on it in
|
|
114
|
-
# the test suite. Check the run once flag which *should*
|
|
115
|
-
# indicate the event has been queued (at which point our queue
|
|
116
|
-
# depth check should waint until it's sent).
|
|
117
|
-
# This is still a hack because the flag can be overridden
|
|
118
|
-
# either way with or without the event being sent out.
|
|
119
|
-
# Note that if the AppStarted sending fails, this check
|
|
120
|
-
# will return false and flushing will be blocked until the
|
|
121
|
-
# 15 second timeout.
|
|
122
|
-
# Note that the first wait interval between telemetry event
|
|
123
|
-
# sending is 10 seconds, the timeout needs to be strictly
|
|
124
|
-
# greater than that.
|
|
125
|
-
return true if buffer.empty? && !in_iteration? && sent_initial_event?
|
|
126
|
-
|
|
127
|
-
sleep 0.5
|
|
128
|
-
|
|
129
|
-
return false if Utils::Time.get_time - started > 15
|
|
130
|
-
end
|
|
121
|
+
def flush(timeout: nil)
|
|
122
|
+
# Increase default timeout to 15 seconds - see the comment in
|
|
123
|
+
# +idle?+ for more details.
|
|
124
|
+
super(timeout: timeout || 15)
|
|
131
125
|
end
|
|
132
126
|
|
|
133
127
|
private
|
|
134
128
|
|
|
135
129
|
def perform(*events)
|
|
136
|
-
return
|
|
130
|
+
return unless enabled?
|
|
137
131
|
|
|
138
132
|
if need_initial_event?
|
|
139
133
|
started!
|
|
@@ -189,7 +183,9 @@ module Datadog
|
|
|
189
183
|
# dependencies and send the new ones.
|
|
190
184
|
# System tests demand only one instance of this event per
|
|
191
185
|
# dependency.
|
|
192
|
-
|
|
186
|
+
if @dependency_collection && initial_event.app_started?
|
|
187
|
+
send_event(Event::AppDependenciesLoaded.new)
|
|
188
|
+
end
|
|
193
189
|
|
|
194
190
|
true
|
|
195
191
|
else
|
|
@@ -212,6 +208,8 @@ module Datadog
|
|
|
212
208
|
res
|
|
213
209
|
end
|
|
214
210
|
|
|
211
|
+
# This method overrides Queue's dequeue method and does LIFO instead
|
|
212
|
+
# of FIFO that Queue implements. Why?
|
|
215
213
|
def dequeue
|
|
216
214
|
buffer.pop
|
|
217
215
|
end
|
|
@@ -240,6 +238,45 @@ module Datadog
|
|
|
240
238
|
disable!
|
|
241
239
|
end
|
|
242
240
|
|
|
241
|
+
# Call this method in a forked child to reset the state of this worker.
|
|
242
|
+
#
|
|
243
|
+
# Discard any accumulated events since they will be sent by
|
|
244
|
+
# the parent.
|
|
245
|
+
# Discard any accumulated metrics.
|
|
246
|
+
# Restart the worker thread, if it was running in the parent process.
|
|
247
|
+
#
|
|
248
|
+
# This method cannot be called +after_fork+ because workers define
|
|
249
|
+
# and call +after_fork+ which is supposed to do different things.
|
|
250
|
+
def after_fork_monkey_patched
|
|
251
|
+
# If telemetry is disabled, we still reset the state to avoid
|
|
252
|
+
# having wrong state. It is possible that in the future telemetry
|
|
253
|
+
# will be re-enabled after errors.
|
|
254
|
+
initialize_state
|
|
255
|
+
# In the child process, we get a new runtime_id.
|
|
256
|
+
# As such we need to send AppStarted event.
|
|
257
|
+
# In the parent process, the event may have been the
|
|
258
|
+
# SynthAppClientConfigurationChange instead of AppStarted,
|
|
259
|
+
# and in that case we need to convert it to the "regular"
|
|
260
|
+
# AppStarted event.
|
|
261
|
+
if defined?(@initial_event) && @initial_event.is_a?(Event::SynthAppClientConfigurationChange)
|
|
262
|
+
# It would be great to just replace the initial event in
|
|
263
|
+
# +initialize_state+ method. Unfortunately this event requires
|
|
264
|
+
# the entire component tree to build its payload, which we
|
|
265
|
+
# 1) don't currently have in telemetry and
|
|
266
|
+
# 2) don't want to keep a permanent reference to in any case.
|
|
267
|
+
# Therefore we have this +reset!+ method that changes the
|
|
268
|
+
# event type while keeping the payload.
|
|
269
|
+
@initial_event.reset! # steep:ignore NoMethod
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
if enabled? && !worker.nil?
|
|
273
|
+
# Start the background thread if it was started in the parent
|
|
274
|
+
# process (which requires telemetry to be enabled).
|
|
275
|
+
# This should be done after all of the state resets.
|
|
276
|
+
perform
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
|
|
243
280
|
# Deduplicate logs by counting the number of repeated occurrences of the same log
|
|
244
281
|
# entry and replacing them with a single entry with the calculated `count` value.
|
|
245
282
|
# Non-log events are unchanged.
|
|
@@ -270,6 +307,25 @@ module Datadog
|
|
|
270
307
|
|
|
271
308
|
other_events + uniq_logs
|
|
272
309
|
end
|
|
310
|
+
|
|
311
|
+
def idle?
|
|
312
|
+
# The AppStarted event is triggered by the worker itself,
|
|
313
|
+
# from the worker thread. As such the main thread has no way
|
|
314
|
+
# to delay itself until that event is queued and we need some
|
|
315
|
+
# way to wait until that event is sent out to assert on it in
|
|
316
|
+
# the test suite. Check the run once flag which *should*
|
|
317
|
+
# indicate the event has been queued (at which point our queue
|
|
318
|
+
# depth check should wait until it's sent).
|
|
319
|
+
# This is still a hack because the flag can be overridden
|
|
320
|
+
# either way with or without the event being sent out.
|
|
321
|
+
# Note that if the AppStarted sending fails, this check
|
|
322
|
+
# will return false and flushing will be blocked until the
|
|
323
|
+
# 15 second timeout.
|
|
324
|
+
# Note that the first wait interval between telemetry event
|
|
325
|
+
# sending is 10 seconds, the timeout needs to be strictly
|
|
326
|
+
# greater than that.
|
|
327
|
+
super && sent_initial_event?
|
|
328
|
+
end
|
|
273
329
|
end
|
|
274
330
|
end
|
|
275
331
|
end
|
|
@@ -8,6 +8,8 @@ module Datadog
|
|
|
8
8
|
module Ext
|
|
9
9
|
module HTTP
|
|
10
10
|
HEADER_CONTAINER_ID = 'Datadog-Container-ID'
|
|
11
|
+
HEADER_ENTITY_ID = 'Datadog-Entity-ID'
|
|
12
|
+
HEADER_EXTERNAL_ENV = 'Datadog-External-Env'
|
|
11
13
|
HEADER_DD_API_KEY = 'DD-API-KEY'
|
|
12
14
|
# Tells agent that `_dd.top_level` metrics have been set by the tracer.
|
|
13
15
|
# The agent will not calculate top-level spans but instead trust the tracer tagging.
|
|
@@ -9,13 +9,18 @@ module Datadog
|
|
|
9
9
|
module API
|
|
10
10
|
# Endpoint
|
|
11
11
|
class Endpoint
|
|
12
|
-
attr_reader
|
|
13
|
-
|
|
14
|
-
:path
|
|
12
|
+
attr_reader :verb
|
|
13
|
+
attr_reader :path
|
|
15
14
|
|
|
16
|
-
|
|
15
|
+
# TODO Currently only Traces transport specifies an encoder.
|
|
16
|
+
# Other transports perform encoding "inline" / ad-hoc.
|
|
17
|
+
# They should probably use this encoder field instead.
|
|
18
|
+
attr_reader :encoder
|
|
19
|
+
|
|
20
|
+
def initialize(verb, path, encoder: nil)
|
|
17
21
|
@verb = verb
|
|
18
22
|
@path = path
|
|
23
|
+
@encoder = encoder
|
|
19
24
|
end
|
|
20
25
|
|
|
21
26
|
def call(env)
|
|
@@ -7,36 +7,19 @@ module Datadog
|
|
|
7
7
|
module API
|
|
8
8
|
# An API configured with adapter and routes
|
|
9
9
|
class Instance
|
|
10
|
-
# Raised when an endpoint is invoked on an API that is not the
|
|
11
|
-
# of expected API class for that endpoint.
|
|
12
|
-
class EndpointNotSupportedError < StandardError
|
|
13
|
-
attr_reader :spec, :endpoint_name
|
|
14
|
-
|
|
15
|
-
def initialize(endpoint_name, spec)
|
|
16
|
-
@spec = spec
|
|
17
|
-
@endpoint_name = endpoint_name
|
|
18
|
-
|
|
19
|
-
super(message)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def message
|
|
23
|
-
"#{endpoint_name} not supported for this API!"
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
|
|
27
10
|
attr_reader \
|
|
28
11
|
:adapter,
|
|
29
12
|
:headers,
|
|
30
|
-
:
|
|
13
|
+
:endpoint
|
|
31
14
|
|
|
32
|
-
def initialize(
|
|
33
|
-
@
|
|
15
|
+
def initialize(endpoint, adapter, options = {})
|
|
16
|
+
@endpoint = endpoint
|
|
34
17
|
@adapter = adapter
|
|
35
18
|
@headers = options.fetch(:headers, {})
|
|
36
19
|
end
|
|
37
20
|
|
|
38
21
|
def encoder
|
|
39
|
-
|
|
22
|
+
endpoint.encoder
|
|
40
23
|
end
|
|
41
24
|
|
|
42
25
|
def call(env)
|
|
@@ -13,7 +13,6 @@ module Datadog
|
|
|
13
13
|
REGISTRY = Datadog::Core::Transport::HTTP::Adapters::Registry.new
|
|
14
14
|
|
|
15
15
|
attr_reader \
|
|
16
|
-
:api_instance_class,
|
|
17
16
|
:apis,
|
|
18
17
|
:api_options,
|
|
19
18
|
:default_adapter,
|
|
@@ -21,7 +20,7 @@ module Datadog
|
|
|
21
20
|
:default_headers,
|
|
22
21
|
:logger
|
|
23
22
|
|
|
24
|
-
def initialize(
|
|
23
|
+
def initialize(logger: Datadog.logger)
|
|
25
24
|
# Global settings
|
|
26
25
|
@default_adapter = nil
|
|
27
26
|
@default_headers = {}
|
|
@@ -33,7 +32,6 @@ module Datadog
|
|
|
33
32
|
# API settings
|
|
34
33
|
@api_options = {}
|
|
35
34
|
|
|
36
|
-
@api_instance_class = api_instance_class
|
|
37
35
|
@logger = logger
|
|
38
36
|
|
|
39
37
|
yield(self) if block_given?
|
|
@@ -73,6 +71,12 @@ module Datadog
|
|
|
73
71
|
@apis[key] = spec
|
|
74
72
|
|
|
75
73
|
# Apply as default API, if specified to do so.
|
|
74
|
+
#
|
|
75
|
+
# This code also sets the first defined API to be the default API.
|
|
76
|
+
# In APIs without fallbacks (currently, everything other than
|
|
77
|
+
# tracing's Traces, though DI Input will also have fallbacks soon)
|
|
78
|
+
# there is only one declaration of `transport.api`, and that
|
|
79
|
+
# API is set as the default API in the transport by this line.
|
|
76
80
|
@default_api = key if options.delete(:default) || @default_api.nil?
|
|
77
81
|
|
|
78
82
|
# Save all other settings for initialization
|
|
@@ -94,7 +98,7 @@ module Datadog
|
|
|
94
98
|
def to_api_instances
|
|
95
99
|
raise NoApisError if @apis.empty?
|
|
96
100
|
|
|
97
|
-
@apis.inject(
|
|
101
|
+
@apis.inject(API::Map.new) do |instances, (key, spec)|
|
|
98
102
|
instances.tap do
|
|
99
103
|
api_options = @api_options[key].dup
|
|
100
104
|
|
|
@@ -107,7 +111,7 @@ module Datadog
|
|
|
107
111
|
api_options[:headers] = @default_headers.merge((api_options[:headers] || {}))
|
|
108
112
|
|
|
109
113
|
# Add API::Instance with all settings
|
|
110
|
-
instances[key] =
|
|
114
|
+
instances[key] = API::Instance.new(
|
|
111
115
|
spec,
|
|
112
116
|
adapter,
|
|
113
117
|
api_options
|
|
@@ -11,21 +11,32 @@ module Datadog
|
|
|
11
11
|
#
|
|
12
12
|
# @api private
|
|
13
13
|
class Client
|
|
14
|
-
attr_reader :
|
|
14
|
+
attr_reader :instance, :logger
|
|
15
15
|
|
|
16
|
-
def initialize(
|
|
17
|
-
@
|
|
16
|
+
def initialize(instance, logger:)
|
|
17
|
+
@instance = instance
|
|
18
18
|
@logger = logger
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def send_request(request, &block)
|
|
21
|
+
def send_request(action, request)
|
|
24
22
|
# Build request into env
|
|
25
23
|
env = build_env(request)
|
|
26
24
|
|
|
27
|
-
# Get responses from API
|
|
28
|
-
|
|
25
|
+
# Get responses from API.
|
|
26
|
+
# All of our APIs send only one type of request each.
|
|
27
|
+
instance.endpoint.call(env) do |request_env|
|
|
28
|
+
instance.call(request_env)
|
|
29
|
+
end.tap do |response|
|
|
30
|
+
unless response.ok?
|
|
31
|
+
# This logging is on debug level.
|
|
32
|
+
# To report the failed operations on lower levels,
|
|
33
|
+
# throttling needs to be implemented because
|
|
34
|
+
# agent unavailability can produce a lot of spam that would
|
|
35
|
+
# be not desired by customers.
|
|
36
|
+
# Some transports do report failed operations on warn level
|
|
37
|
+
# with such throttling.
|
|
38
|
+
logger.debug { "send_request #{action.inspect} failed: #{response.inspect}" }
|
|
39
|
+
end
|
|
29
40
|
on_response(response)
|
|
30
41
|
end
|
|
31
42
|
rescue => exception
|
|
@@ -12,16 +12,16 @@ module Datadog
|
|
|
12
12
|
module HTTP
|
|
13
13
|
# Add adapters to registry
|
|
14
14
|
Builder::REGISTRY.set(
|
|
15
|
-
Transport::HTTP::Adapters::Net,
|
|
15
|
+
Core::Transport::HTTP::Adapters::Net,
|
|
16
16
|
Core::Configuration::Ext::Agent::HTTP::ADAPTER
|
|
17
17
|
)
|
|
18
18
|
Builder::REGISTRY.set(
|
|
19
|
-
Transport::HTTP::Adapters::Test,
|
|
20
|
-
Transport::Ext::Test::ADAPTER
|
|
19
|
+
Core::Transport::HTTP::Adapters::Test,
|
|
20
|
+
Core::Transport::Ext::Test::ADAPTER
|
|
21
21
|
)
|
|
22
22
|
Builder::REGISTRY.set(
|
|
23
|
-
Transport::HTTP::Adapters::UnixSocket,
|
|
24
|
-
Transport::Ext::UnixSocket::ADAPTER
|
|
23
|
+
Core::Transport::HTTP::Adapters::UnixSocket,
|
|
24
|
+
Core::Transport::Ext::UnixSocket::ADAPTER
|
|
25
25
|
)
|
|
26
26
|
|
|
27
27
|
module_function
|
|
@@ -29,8 +29,13 @@ module Datadog
|
|
|
29
29
|
# Helper function that delegates to Builder.new
|
|
30
30
|
# but is under HTTP namespace so that client code requires this file
|
|
31
31
|
# to get the adapters configured, and not the builder directly.
|
|
32
|
-
def build(
|
|
33
|
-
|
|
32
|
+
def build(
|
|
33
|
+
agent_settings:,
|
|
34
|
+
logger: Datadog.logger,
|
|
35
|
+
headers: nil,
|
|
36
|
+
&block
|
|
37
|
+
)
|
|
38
|
+
Builder.new(logger: logger) do |transport|
|
|
34
39
|
transport.adapter(agent_settings)
|
|
35
40
|
transport.headers(default_headers)
|
|
36
41
|
|
|
@@ -38,34 +43,32 @@ module Datadog
|
|
|
38
43
|
yield transport
|
|
39
44
|
|
|
40
45
|
# Apply any settings given by options
|
|
41
|
-
transport.default_api = api_version if api_version
|
|
42
46
|
transport.headers(headers) if headers
|
|
43
47
|
end
|
|
44
48
|
end
|
|
45
49
|
|
|
46
50
|
def default_headers
|
|
47
51
|
{
|
|
48
|
-
|
|
49
|
-
|
|
52
|
+
Core::Transport::Ext::HTTP::HEADER_CLIENT_COMPUTED_TOP_LEVEL => '1',
|
|
53
|
+
Core::Transport::Ext::HTTP::HEADER_META_LANG =>
|
|
50
54
|
Datadog::Core::Environment::Ext::LANG,
|
|
51
|
-
|
|
55
|
+
Core::Transport::Ext::HTTP::HEADER_META_LANG_VERSION =>
|
|
52
56
|
Datadog::Core::Environment::Ext::LANG_VERSION,
|
|
53
|
-
|
|
57
|
+
Core::Transport::Ext::HTTP::HEADER_META_LANG_INTERPRETER =>
|
|
54
58
|
Datadog::Core::Environment::Ext::LANG_INTERPRETER,
|
|
55
|
-
|
|
59
|
+
Core::Transport::Ext::HTTP::HEADER_META_LANG_INTERPRETER_VENDOR =>
|
|
56
60
|
Core::Environment::Ext::LANG_ENGINE,
|
|
57
|
-
|
|
61
|
+
Core::Transport::Ext::HTTP::HEADER_META_TRACER_VERSION =>
|
|
58
62
|
Datadog::Core::Environment::Ext::GEM_DATADOG_VERSION
|
|
59
63
|
}.tap do |headers|
|
|
60
|
-
# Add container
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
end
|
|
64
|
+
# Add application container info
|
|
65
|
+
headers.merge!(Core::Environment::Container.to_headers)
|
|
66
|
+
|
|
64
67
|
# TODO: inject configuration rather than reading from global here
|
|
65
68
|
unless Datadog.configuration.apm.tracing.enabled
|
|
66
69
|
# Sending this header to the agent will disable metrics computation (and billing) on the agent side
|
|
67
70
|
# by pretending it has already been done on the library side.
|
|
68
|
-
headers[
|
|
71
|
+
headers[Core::Transport::Ext::HTTP::HEADER_CLIENT_COMPUTED_STATS] = 'yes'
|
|
69
72
|
end
|
|
70
73
|
end
|
|
71
74
|
end
|
|
@@ -37,6 +37,15 @@ module Datadog
|
|
|
37
37
|
maybe_code = if respond_to?(:code)
|
|
38
38
|
" code:#{code}," # steep:ignore
|
|
39
39
|
end
|
|
40
|
+
payload = self.payload
|
|
41
|
+
# Truncation thresholds are arbitrary but we need to truncate the
|
|
42
|
+
# payload here because outputting multi-MB request body to the
|
|
43
|
+
# log is not useful.
|
|
44
|
+
#
|
|
45
|
+
# Note that payload can be nil here.
|
|
46
|
+
if payload && payload.length > 2000 # steep:ignore
|
|
47
|
+
payload = Utils::Truncation.truncate_in_middle(payload, 1500, 500) # steep:ignore
|
|
48
|
+
end
|
|
40
49
|
"#{self.class} ok?:#{ok?},#{maybe_code} unsupported?:#{unsupported?}, " \
|
|
41
50
|
"not_found?:#{not_found?}, client_error?:#{client_error?}, " \
|
|
42
51
|
"server_error?:#{server_error?}, internal_error?:#{internal_error?}, " \
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'http/client'
|
|
4
|
+
|
|
5
|
+
module Datadog
|
|
6
|
+
module Core
|
|
7
|
+
module Transport
|
|
8
|
+
# Raised when configured with an unknown API version
|
|
9
|
+
class UnknownApiVersionError < StandardError
|
|
10
|
+
attr_reader :version
|
|
11
|
+
|
|
12
|
+
def initialize(version)
|
|
13
|
+
super
|
|
14
|
+
|
|
15
|
+
@version = version
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def message
|
|
19
|
+
"No matching transport API for version #{version}!"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Raised when the API verson to downgrade to does not map to a
|
|
24
|
+
# defined API.
|
|
25
|
+
class NoDowngradeAvailableError < StandardError
|
|
26
|
+
attr_reader :version
|
|
27
|
+
|
|
28
|
+
def initialize(version)
|
|
29
|
+
super
|
|
30
|
+
|
|
31
|
+
@version = version
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def message
|
|
35
|
+
"No downgrade from transport API version #{version} is available!"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Base class for transports.
|
|
40
|
+
class Transport
|
|
41
|
+
attr_reader :client, :apis, :default_api, :current_api_id, :logger
|
|
42
|
+
|
|
43
|
+
class << self
|
|
44
|
+
# The HTTP client class to use for requests, derived from
|
|
45
|
+
# Core::Transport::HTTP::Client.
|
|
46
|
+
#
|
|
47
|
+
# Important: this attribute is NOT inherited by derived classes -
|
|
48
|
+
# it must be set by every Transport class that wants to have a
|
|
49
|
+
# non-default HTTP::Client instance.
|
|
50
|
+
attr_accessor :http_client_class
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def initialize(apis, default_api, logger:)
|
|
54
|
+
@apis = apis
|
|
55
|
+
@default_api = default_api
|
|
56
|
+
@logger = logger
|
|
57
|
+
|
|
58
|
+
set_api!(default_api)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def current_api
|
|
62
|
+
apis[current_api_id]
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def set_api!(api_id)
|
|
68
|
+
raise UnknownApiVersionError, api_id unless apis.key?(api_id)
|
|
69
|
+
|
|
70
|
+
@current_api_id = api_id
|
|
71
|
+
client_class = self.class.http_client_class || Core::Transport::HTTP::Client
|
|
72
|
+
@client = client_class.new(current_api, logger: logger) # steep:ignore
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def downgrade?(response)
|
|
76
|
+
return false unless apis.fallbacks.key?(current_api_id)
|
|
77
|
+
|
|
78
|
+
response.not_found? || response.unsupported?
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def downgrade!
|
|
82
|
+
downgrade_api_id = apis.fallbacks[current_api_id]
|
|
83
|
+
raise NoDowngradeAvailableError, current_api_id if downgrade_api_id.nil?
|
|
84
|
+
|
|
85
|
+
set_api!(downgrade_api_id)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|