datadog 2.21.0 → 2.23.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 +106 -2
- data/ext/LIBDATADOG_DEVELOPMENT.md +3 -0
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +1 -1
- data/ext/datadog_profiling_native_extension/collectors_stack.c +4 -0
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +1 -1
- data/ext/datadog_profiling_native_extension/extconf.rb +6 -4
- data/ext/datadog_profiling_native_extension/heap_recorder.c +1 -1
- data/ext/libdatadog_api/datadog_ruby_common.h +1 -1
- data/ext/libdatadog_api/ddsketch.c +106 -0
- data/ext/libdatadog_api/feature_flags.c +554 -0
- data/ext/libdatadog_api/feature_flags.h +5 -0
- data/ext/libdatadog_api/init.c +5 -0
- data/ext/libdatadog_api/library_config.c +34 -25
- data/ext/libdatadog_api/process_discovery.c +19 -13
- data/ext/libdatadog_extconf_helpers.rb +1 -1
- data/lib/datadog/appsec/api_security/endpoint_collection/grape_route_serializer.rb +26 -0
- data/lib/datadog/appsec/api_security/endpoint_collection/rails_collector.rb +59 -0
- data/lib/datadog/appsec/api_security/endpoint_collection/rails_route_serializer.rb +29 -0
- data/lib/datadog/appsec/api_security/endpoint_collection/sinatra_route_serializer.rb +26 -0
- data/lib/datadog/appsec/api_security/endpoint_collection.rb +10 -0
- data/lib/datadog/appsec/api_security/route_extractor.rb +23 -6
- data/lib/datadog/appsec/api_security/sampler.rb +7 -4
- data/lib/datadog/appsec/assets/blocked.html +8 -0
- data/lib/datadog/appsec/assets/blocked.json +1 -1
- data/lib/datadog/appsec/assets/blocked.text +3 -1
- data/lib/datadog/appsec/assets/waf_rules/README.md +30 -36
- data/lib/datadog/appsec/assets/waf_rules/recommended.json +359 -4
- data/lib/datadog/appsec/assets/waf_rules/strict.json +43 -2
- data/lib/datadog/appsec/assets.rb +1 -1
- data/lib/datadog/appsec/compressed_json.rb +1 -1
- data/lib/datadog/appsec/configuration/settings.rb +9 -0
- data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +3 -1
- data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +3 -2
- data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +3 -1
- data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +3 -1
- data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +9 -4
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +5 -1
- data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +7 -2
- data/lib/datadog/appsec/contrib/rails/patcher.rb +30 -0
- data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +3 -1
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +10 -4
- data/lib/datadog/appsec/event.rb +12 -14
- data/lib/datadog/appsec/metrics/collector.rb +19 -3
- data/lib/datadog/appsec/metrics/telemetry_exporter.rb +2 -1
- data/lib/datadog/appsec/monitor/gateway/watcher.rb +4 -4
- data/lib/datadog/appsec/remote.rb +29 -13
- data/lib/datadog/appsec/response.rb +18 -4
- data/lib/datadog/appsec/security_engine/result.rb +28 -9
- data/lib/datadog/appsec/security_engine/runner.rb +17 -7
- data/lib/datadog/appsec/security_event.rb +5 -7
- data/lib/datadog/core/configuration/components.rb +44 -9
- data/lib/datadog/core/configuration/config_helper.rb +1 -1
- data/lib/datadog/core/configuration/settings.rb +14 -0
- data/lib/datadog/core/configuration/stable_config.rb +10 -0
- data/lib/datadog/core/configuration/supported_configurations.rb +330 -299
- data/lib/datadog/core/configuration.rb +1 -1
- data/lib/datadog/core/ddsketch.rb +19 -0
- data/lib/datadog/core/environment/ext.rb +6 -0
- data/lib/datadog/core/environment/process.rb +79 -0
- data/lib/datadog/core/environment/yjit.rb +2 -1
- data/lib/datadog/core/feature_flags.rb +61 -0
- data/lib/datadog/core/pin.rb +4 -8
- data/lib/datadog/core/process_discovery.rb +4 -2
- data/lib/datadog/core/remote/client/capabilities.rb +7 -0
- data/lib/datadog/core/remote/component.rb +4 -6
- data/lib/datadog/core/remote/transport/config.rb +2 -10
- data/lib/datadog/core/remote/transport/http/config.rb +9 -9
- data/lib/datadog/core/remote/transport/http/negotiation.rb +17 -8
- data/lib/datadog/core/remote/transport/http.rb +2 -0
- data/lib/datadog/core/remote/transport/negotiation.rb +2 -18
- data/lib/datadog/core/remote/worker.rb +25 -37
- data/lib/datadog/core/tag_builder.rb +0 -4
- data/lib/datadog/core/tag_normalizer.rb +84 -0
- data/lib/datadog/core/telemetry/component.rb +18 -3
- data/lib/datadog/core/telemetry/emitter.rb +6 -6
- data/lib/datadog/core/telemetry/event/app_endpoints_loaded.rb +30 -0
- data/lib/datadog/core/telemetry/event/app_started.rb +52 -49
- data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +1 -1
- data/lib/datadog/core/telemetry/event.rb +1 -0
- data/lib/datadog/core/telemetry/logger.rb +2 -2
- data/lib/datadog/core/telemetry/logging.rb +2 -8
- data/lib/datadog/core/telemetry/transport/http/telemetry.rb +5 -6
- data/lib/datadog/core/telemetry/transport/telemetry.rb +1 -2
- data/lib/datadog/core/transport/http/client.rb +69 -0
- data/lib/datadog/core/transport/response.rb +4 -1
- data/lib/datadog/core/utils/array.rb +29 -0
- data/lib/datadog/{appsec/api_security → core/utils}/lru_cache.rb +10 -21
- data/lib/datadog/core/utils/network.rb +22 -1
- data/lib/datadog/core/utils/only_once_successful.rb +6 -2
- data/lib/datadog/core/utils.rb +2 -0
- data/lib/datadog/data_streams/configuration/settings.rb +49 -0
- data/lib/datadog/data_streams/configuration.rb +11 -0
- data/lib/datadog/data_streams/ext.rb +11 -0
- data/lib/datadog/data_streams/extensions.rb +16 -0
- data/lib/datadog/data_streams/pathway_context.rb +169 -0
- data/lib/datadog/data_streams/processor.rb +509 -0
- data/lib/datadog/data_streams/transport/http/api.rb +33 -0
- data/lib/datadog/data_streams/transport/http/client.rb +21 -0
- data/lib/datadog/data_streams/transport/http/stats.rb +87 -0
- data/lib/datadog/data_streams/transport/http.rb +41 -0
- data/lib/datadog/data_streams/transport/stats.rb +60 -0
- data/lib/datadog/data_streams.rb +100 -0
- data/lib/datadog/di/boot.rb +1 -0
- data/lib/datadog/di/component.rb +14 -16
- data/lib/datadog/di/context.rb +70 -0
- data/lib/datadog/di/el/compiler.rb +164 -0
- data/lib/datadog/di/el/evaluator.rb +159 -0
- data/lib/datadog/di/el/expression.rb +42 -0
- data/lib/datadog/di/el.rb +5 -0
- data/lib/datadog/di/error.rb +29 -0
- data/lib/datadog/di/instrumenter.rb +163 -48
- data/lib/datadog/di/probe.rb +55 -15
- data/lib/datadog/di/probe_builder.rb +39 -1
- data/lib/datadog/di/probe_manager.rb +13 -4
- data/lib/datadog/di/probe_notification_builder.rb +105 -67
- data/lib/datadog/di/proc_responder.rb +32 -0
- data/lib/datadog/di/serializer.rb +151 -7
- data/lib/datadog/di/transport/diagnostics.rb +2 -2
- data/lib/datadog/di/transport/http/diagnostics.rb +2 -4
- data/lib/datadog/di/transport/http/input.rb +2 -4
- data/lib/datadog/di/transport/http.rb +6 -2
- data/lib/datadog/di/transport/input.rb +64 -4
- data/lib/datadog/open_feature/component.rb +60 -0
- data/lib/datadog/open_feature/configuration.rb +27 -0
- data/lib/datadog/open_feature/evaluation_engine.rb +69 -0
- data/lib/datadog/open_feature/exposures/batch_builder.rb +32 -0
- data/lib/datadog/open_feature/exposures/buffer.rb +43 -0
- data/lib/datadog/open_feature/exposures/deduplicator.rb +30 -0
- data/lib/datadog/open_feature/exposures/event.rb +60 -0
- data/lib/datadog/open_feature/exposures/reporter.rb +40 -0
- data/lib/datadog/open_feature/exposures/worker.rb +116 -0
- data/lib/datadog/open_feature/ext.rb +14 -0
- data/lib/datadog/open_feature/native_evaluator.rb +38 -0
- data/lib/datadog/open_feature/noop_evaluator.rb +26 -0
- data/lib/datadog/open_feature/provider.rb +141 -0
- data/lib/datadog/open_feature/remote.rb +74 -0
- data/lib/datadog/open_feature/resolution_details.rb +35 -0
- data/lib/datadog/open_feature/transport.rb +72 -0
- data/lib/datadog/open_feature.rb +19 -0
- data/lib/datadog/opentelemetry/configuration/settings.rb +159 -0
- data/lib/datadog/opentelemetry/metrics.rb +110 -0
- data/lib/datadog/opentelemetry/sdk/configurator.rb +25 -1
- data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +38 -0
- data/lib/datadog/opentelemetry.rb +3 -0
- data/lib/datadog/profiling/collectors/code_provenance.rb +15 -6
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +1 -1
- data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -1
- data/lib/datadog/profiling/profiler.rb +4 -0
- data/lib/datadog/profiling/tag_builder.rb +36 -3
- data/lib/datadog/profiling.rb +1 -2
- data/lib/datadog/single_step_instrument.rb +1 -1
- data/lib/datadog/tracing/component.rb +6 -17
- data/lib/datadog/tracing/configuration/dynamic.rb +2 -2
- data/lib/datadog/tracing/configuration/ext.rb +9 -0
- data/lib/datadog/tracing/configuration/settings.rb +77 -3
- data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +4 -4
- data/lib/datadog/tracing/contrib/action_pack/utils.rb +1 -2
- data/lib/datadog/tracing/contrib/active_job/log_injection.rb +21 -7
- data/lib/datadog/tracing/contrib/active_job/patcher.rb +5 -1
- data/lib/datadog/tracing/contrib/aws/instrumentation.rb +4 -2
- data/lib/datadog/tracing/contrib/component.rb +2 -2
- data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +4 -1
- data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +11 -3
- data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +11 -7
- data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +7 -3
- data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +7 -0
- data/lib/datadog/tracing/contrib/graphql/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +74 -44
- data/lib/datadog/tracing/contrib/http/configuration/settings.rb +11 -3
- data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +11 -3
- data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +11 -3
- data/lib/datadog/tracing/contrib/kafka/instrumentation/consumer.rb +66 -0
- data/lib/datadog/tracing/contrib/kafka/instrumentation/producer.rb +66 -0
- data/lib/datadog/tracing/contrib/kafka/patcher.rb +14 -0
- data/lib/datadog/tracing/contrib/karafka/framework.rb +30 -0
- data/lib/datadog/tracing/contrib/karafka/monitor.rb +11 -0
- data/lib/datadog/tracing/contrib/karafka/patcher.rb +32 -0
- data/lib/datadog/tracing/contrib/rack/middlewares.rb +59 -27
- data/lib/datadog/tracing/contrib/rack/route_inference.rb +53 -0
- data/lib/datadog/tracing/contrib/rails/middlewares.rb +2 -2
- data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +4 -1
- data/lib/datadog/tracing/contrib/roda/instrumentation.rb +3 -1
- data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +3 -1
- data/lib/datadog/tracing/contrib/status_range_matcher.rb +7 -0
- data/lib/datadog/tracing/contrib/waterdrop/configuration/settings.rb +27 -0
- data/lib/datadog/tracing/contrib/waterdrop/distributed/propagation.rb +48 -0
- data/lib/datadog/tracing/contrib/waterdrop/ext.rb +17 -0
- data/lib/datadog/tracing/contrib/waterdrop/integration.rb +43 -0
- data/lib/datadog/tracing/contrib/waterdrop/middleware.rb +46 -0
- data/lib/datadog/tracing/contrib/waterdrop/patcher.rb +46 -0
- data/lib/datadog/tracing/contrib/waterdrop/producer.rb +50 -0
- data/lib/datadog/tracing/contrib/waterdrop.rb +37 -0
- data/lib/datadog/tracing/contrib.rb +1 -0
- data/lib/datadog/tracing/metadata/ext.rb +9 -1
- data/lib/datadog/tracing/transport/http/client.rb +12 -26
- data/lib/datadog/tracing/transport/trace_formatter.rb +11 -0
- data/lib/datadog/tracing/transport/traces.rb +3 -5
- data/lib/datadog/version.rb +2 -2
- data/lib/datadog.rb +2 -0
- metadata +92 -16
- data/ext/libdatadog_api/macos_development.md +0 -26
- data/lib/datadog/core/remote/transport/http/client.rb +0 -49
- data/lib/datadog/core/telemetry/transport/http/client.rb +0 -49
- data/lib/datadog/di/transport/http/client.rb +0 -47
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
# rubocop:disable Lint/AssignmentInCondition
|
|
4
|
+
|
|
3
5
|
module Datadog
|
|
4
6
|
module DI
|
|
5
7
|
# Builds probe status notification and snapshot payloads.
|
|
@@ -40,32 +42,17 @@ module Datadog
|
|
|
40
42
|
|
|
41
43
|
# Duration is in seconds.
|
|
42
44
|
# path is the actual path of the instrumented file.
|
|
43
|
-
def build_executed(
|
|
44
|
-
|
|
45
|
-
serialized_locals: nil, args: nil, kwargs: nil, target_self: nil,
|
|
46
|
-
serialized_entry_args: nil)
|
|
47
|
-
build_snapshot(probe, rv: rv, serialized_locals: serialized_locals,
|
|
48
|
-
# Actual path of the instrumented file.
|
|
49
|
-
path: path,
|
|
50
|
-
duration: duration,
|
|
51
|
-
# TODO check how many stack frames we should be keeping/sending,
|
|
52
|
-
# this should be all frames for enriched probes and no frames for
|
|
53
|
-
# non-enriched probes?
|
|
54
|
-
caller_locations: caller_locations,
|
|
55
|
-
args: args, kwargs: kwargs,
|
|
56
|
-
target_self: target_self,
|
|
57
|
-
serialized_entry_args: serialized_entry_args)
|
|
45
|
+
def build_executed(context)
|
|
46
|
+
build_snapshot(context)
|
|
58
47
|
end
|
|
59
48
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
serialized_entry_args: nil)
|
|
68
|
-
if probe.capture_snapshot? && !target_self
|
|
49
|
+
NANOSECONDS = 10**9
|
|
50
|
+
MILLISECONDS = 1000
|
|
51
|
+
|
|
52
|
+
def build_snapshot(context)
|
|
53
|
+
probe = context.probe
|
|
54
|
+
|
|
55
|
+
if probe.capture_snapshot? && !context.target_self
|
|
69
56
|
raise ArgumentError, "Asked to build snapshot with snapshot capture but target_self is nil"
|
|
70
57
|
end
|
|
71
58
|
|
|
@@ -74,22 +61,14 @@ module Datadog
|
|
|
74
61
|
captures = if probe.capture_snapshot?
|
|
75
62
|
if probe.method?
|
|
76
63
|
return_arguments = {
|
|
77
|
-
"@return": serializer.serialize_value(
|
|
64
|
+
"@return": serializer.serialize_value(context.return_value,
|
|
78
65
|
depth: probe.max_capture_depth || settings.dynamic_instrumentation.max_capture_depth,
|
|
79
66
|
attribute_count: probe.max_capture_attribute_count || settings.dynamic_instrumentation.max_capture_attribute_count),
|
|
80
|
-
self: serializer.serialize_value(target_self),
|
|
67
|
+
self: serializer.serialize_value(context.target_self),
|
|
81
68
|
}
|
|
82
69
|
{
|
|
83
70
|
entry: {
|
|
84
|
-
|
|
85
|
-
arguments: if serialized_entry_args
|
|
86
|
-
serialized_entry_args
|
|
87
|
-
else
|
|
88
|
-
(args || kwargs) && serializer.serialize_args(args, kwargs, target_self,
|
|
89
|
-
depth: probe.max_capture_depth || settings.dynamic_instrumentation.max_capture_depth,
|
|
90
|
-
attribute_count: probe.max_capture_attribute_count || settings.dynamic_instrumentation.max_capture_attribute_count)
|
|
91
|
-
end,
|
|
92
|
-
# standard:enable all
|
|
71
|
+
arguments: context.serialized_entry_args,
|
|
93
72
|
},
|
|
94
73
|
return: {
|
|
95
74
|
arguments: return_arguments,
|
|
@@ -98,20 +77,51 @@ module Datadog
|
|
|
98
77
|
}
|
|
99
78
|
elsif probe.line?
|
|
100
79
|
{
|
|
101
|
-
lines: serialized_locals && {
|
|
80
|
+
lines: (locals = context.serialized_locals) && {
|
|
102
81
|
probe.line_no => {
|
|
103
|
-
locals:
|
|
104
|
-
arguments: {self: serializer.serialize_value(target_self)},
|
|
82
|
+
locals: locals,
|
|
83
|
+
arguments: {self: serializer.serialize_value(context.target_self)},
|
|
105
84
|
},
|
|
106
85
|
},
|
|
107
86
|
}
|
|
108
87
|
end
|
|
109
88
|
end
|
|
110
89
|
|
|
90
|
+
message = nil
|
|
91
|
+
evaluation_errors = []
|
|
92
|
+
if segments = probe.template_segments
|
|
93
|
+
message, evaluation_errors = evaluate_template(segments, context)
|
|
94
|
+
end
|
|
95
|
+
build_snapshot_base(context,
|
|
96
|
+
evaluation_errors: evaluation_errors, message: message,
|
|
97
|
+
captures: captures)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def build_condition_evaluation_failed(context, expression, exception)
|
|
101
|
+
error = {
|
|
102
|
+
message: "#{exception.class}: #{exception}",
|
|
103
|
+
expr: expression.dsl_expr,
|
|
104
|
+
}
|
|
105
|
+
build_snapshot_base(context, evaluation_errors: [error])
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
private
|
|
109
|
+
|
|
110
|
+
def build_snapshot_base(context, evaluation_errors: [], captures: nil, message: nil)
|
|
111
|
+
probe = context.probe
|
|
112
|
+
|
|
113
|
+
timestamp = timestamp_now
|
|
114
|
+
duration = context.duration
|
|
115
|
+
|
|
111
116
|
location = if probe.line?
|
|
112
117
|
{
|
|
113
|
-
file: path,
|
|
114
|
-
|
|
118
|
+
file: context.path,
|
|
119
|
+
# Line numbers are required to be strings by the
|
|
120
|
+
# system tests schema.
|
|
121
|
+
# Backend I think accepts them also as integers, but some
|
|
122
|
+
# other languages send strings and we decided to require
|
|
123
|
+
# strings for everyone.
|
|
124
|
+
lines: [probe.line_no.to_s],
|
|
115
125
|
}
|
|
116
126
|
elsif probe.method?
|
|
117
127
|
{
|
|
@@ -120,34 +130,50 @@ module Datadog
|
|
|
120
130
|
}
|
|
121
131
|
end
|
|
122
132
|
|
|
123
|
-
stack = if caller_locations
|
|
133
|
+
stack = if caller_locations = context.caller_locations
|
|
124
134
|
format_caller_locations(caller_locations)
|
|
125
135
|
end
|
|
126
136
|
|
|
127
|
-
timestamp = timestamp_now
|
|
128
137
|
{
|
|
129
138
|
service: settings.service,
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
139
|
+
debugger: {
|
|
140
|
+
type: 'snapshot',
|
|
141
|
+
# Product can have three values: di, ld, er.
|
|
142
|
+
# We do not currently implement exception replay.
|
|
143
|
+
# There is currently no specification, and no consensus, for
|
|
144
|
+
# when product should be di (dynamic instrumentation) and when
|
|
145
|
+
# it should be ld (live debugger). I thought the backend was
|
|
146
|
+
# supposed to provide this in probe specification via remote
|
|
147
|
+
# config, but apparently this is not the case and the expectation
|
|
148
|
+
# is that the library figures out the product via heuristics,
|
|
149
|
+
# except there is currently no consensus on said heuristics.
|
|
150
|
+
# .NET always sends ld, other languages send nothing at the moment.
|
|
151
|
+
# Don't send anything for the time being.
|
|
152
|
+
#product: 'di/ld',
|
|
153
|
+
snapshot: {
|
|
154
|
+
id: SecureRandom.uuid,
|
|
155
|
+
timestamp: timestamp,
|
|
156
|
+
evaluationErrors: evaluation_errors,
|
|
157
|
+
probe: {
|
|
158
|
+
id: probe.id,
|
|
159
|
+
version: 0,
|
|
160
|
+
location: location,
|
|
161
|
+
},
|
|
162
|
+
language: 'ruby',
|
|
163
|
+
# TODO add test coverage for callers being nil
|
|
164
|
+
stack: stack,
|
|
165
|
+
# System tests schema validation requires captures to
|
|
166
|
+
# always be present
|
|
167
|
+
captures: captures || {},
|
|
138
168
|
},
|
|
139
|
-
language: 'ruby',
|
|
140
|
-
# TODO add test coverage for callers being nil
|
|
141
|
-
stack: stack,
|
|
142
|
-
captures: captures,
|
|
143
169
|
},
|
|
144
170
|
# In python tracer duration is under debugger.snapshot,
|
|
145
171
|
# but UI appears to expect it here at top level.
|
|
146
|
-
duration: duration ? (duration *
|
|
172
|
+
duration: duration ? (duration * NANOSECONDS).to_i : 0,
|
|
147
173
|
host: nil,
|
|
148
174
|
logger: {
|
|
149
175
|
name: probe.file,
|
|
150
|
-
method: probe.method_name
|
|
176
|
+
method: probe.method_name,
|
|
151
177
|
thread_name: Thread.current.name,
|
|
152
178
|
# Dynamic instrumentation currently does not need thread_id for
|
|
153
179
|
# anything. It can be sent if a customer requests it at which point
|
|
@@ -160,14 +186,11 @@ module Datadog
|
|
|
160
186
|
"dd.trace_id": active_trace&.id&.to_s,
|
|
161
187
|
"dd.span_id": active_span&.id&.to_s,
|
|
162
188
|
ddsource: 'dd_debugger',
|
|
163
|
-
message:
|
|
164
|
-
duration: duration ? duration * 1000 : 0),
|
|
189
|
+
message: message,
|
|
165
190
|
timestamp: timestamp,
|
|
166
191
|
}
|
|
167
192
|
end
|
|
168
193
|
|
|
169
|
-
private
|
|
170
|
-
|
|
171
194
|
def build_status(probe, message:, status:)
|
|
172
195
|
{
|
|
173
196
|
service: settings.service,
|
|
@@ -192,16 +215,29 @@ module Datadog
|
|
|
192
215
|
end
|
|
193
216
|
end
|
|
194
217
|
|
|
195
|
-
def evaluate_template(
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
218
|
+
def evaluate_template(template_segments, context)
|
|
219
|
+
evaluation_errors = []
|
|
220
|
+
message = template_segments.map do |segment|
|
|
221
|
+
case segment
|
|
222
|
+
when String
|
|
223
|
+
segment
|
|
224
|
+
when EL::Expression
|
|
225
|
+
serializer.serialize_value_for_message(segment.evaluate(context))
|
|
226
|
+
else
|
|
227
|
+
raise ArgumentError, "Invalid template segment type: #{segment}"
|
|
228
|
+
end
|
|
229
|
+
rescue => exc
|
|
230
|
+
evaluation_errors << {
|
|
231
|
+
message: "#{exc.class}: #{exc}",
|
|
232
|
+
expr: segment.dsl_expr,
|
|
233
|
+
}
|
|
234
|
+
'[evaluation error]'
|
|
235
|
+
end.join
|
|
236
|
+
[message, evaluation_errors]
|
|
201
237
|
end
|
|
202
238
|
|
|
203
239
|
def timestamp_now
|
|
204
|
-
(Core::Utils::Time.now.to_f *
|
|
240
|
+
(Core::Utils::Time.now.to_f * MILLISECONDS).to_i
|
|
205
241
|
end
|
|
206
242
|
|
|
207
243
|
def active_trace
|
|
@@ -218,3 +254,5 @@ module Datadog
|
|
|
218
254
|
end
|
|
219
255
|
end
|
|
220
256
|
end
|
|
257
|
+
|
|
258
|
+
# rubocop:enable Lint/AssignmentInCondition
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Datadog
|
|
4
|
+
module DI
|
|
5
|
+
# An adapter to convert procs to responders.
|
|
6
|
+
#
|
|
7
|
+
# Used in test suite and benchmarks.
|
|
8
|
+
#
|
|
9
|
+
# @api private
|
|
10
|
+
class ProcResponder
|
|
11
|
+
def initialize(executed_proc, failed_proc = nil)
|
|
12
|
+
@executed_proc = executed_proc
|
|
13
|
+
@failed_proc = failed_proc
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
attr_reader :executed_proc
|
|
17
|
+
attr_reader :failed_proc
|
|
18
|
+
|
|
19
|
+
def probe_executed_callback(context)
|
|
20
|
+
executed_proc.call(context)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def probe_condition_evaluation_failed_callback(context, exc)
|
|
24
|
+
if failed_proc.nil?
|
|
25
|
+
raise NotImplementedError, "Failed proc not provided"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
failed_proc.call(context, exc)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -64,6 +64,9 @@ module Datadog
|
|
|
64
64
|
# a common base class but are all of different classes) or for Mongoid
|
|
65
65
|
# models (that do not have a common base class at all but include a
|
|
66
66
|
# standard Mongoid module).
|
|
67
|
+
#
|
|
68
|
+
# Important: these serializers are NOT used in log messages.
|
|
69
|
+
# They are only used for variables that are captured in the snapshots.
|
|
67
70
|
@@flat_registry = []
|
|
68
71
|
def self.register(condition: nil, &block)
|
|
69
72
|
@@flat_registry << {condition: condition, proc: block}
|
|
@@ -79,6 +82,18 @@ module Datadog
|
|
|
79
82
|
attr_reader :redactor
|
|
80
83
|
attr_reader :telemetry
|
|
81
84
|
|
|
85
|
+
def combine_args(args, kwargs, target_self)
|
|
86
|
+
counter = 0
|
|
87
|
+
combined = args.each_with_object({}) do |value, c|
|
|
88
|
+
counter += 1
|
|
89
|
+
# Conversion to symbol is needed here to put args ahead of
|
|
90
|
+
# kwargs when they are merged below.
|
|
91
|
+
c[:"arg#{counter}"] = value
|
|
92
|
+
end.update(kwargs)
|
|
93
|
+
combined[:self] = target_self
|
|
94
|
+
combined
|
|
95
|
+
end
|
|
96
|
+
|
|
82
97
|
# Serializes positional and keyword arguments to a method,
|
|
83
98
|
# as obtained by a method probe.
|
|
84
99
|
#
|
|
@@ -93,13 +108,7 @@ module Datadog
|
|
|
93
108
|
def serialize_args(args, kwargs, target_self,
|
|
94
109
|
depth: settings.dynamic_instrumentation.max_capture_depth,
|
|
95
110
|
attribute_count: settings.dynamic_instrumentation.max_capture_attribute_count)
|
|
96
|
-
|
|
97
|
-
combined = args.each_with_object({}) do |value, c|
|
|
98
|
-
counter += 1
|
|
99
|
-
# Conversion to symbol is needed here to put args ahead of
|
|
100
|
-
# kwargs when they are merged below.
|
|
101
|
-
c[:"arg#{counter}"] = value
|
|
102
|
-
end.update(kwargs).update(self: target_self)
|
|
111
|
+
combined = combine_args(args, kwargs, target_self)
|
|
103
112
|
serialize_vars(combined, depth: depth, attribute_count: attribute_count)
|
|
104
113
|
end
|
|
105
114
|
|
|
@@ -150,6 +159,8 @@ module Datadog
|
|
|
150
159
|
end
|
|
151
160
|
|
|
152
161
|
serialized = {type: class_name(cls)}
|
|
162
|
+
# https://github.com/soutaro/steep/issues/1860
|
|
163
|
+
# @type var serialized: untyped
|
|
153
164
|
case value
|
|
154
165
|
when NilClass
|
|
155
166
|
serialized.update(isNull: true)
|
|
@@ -261,8 +272,120 @@ module Datadog
|
|
|
261
272
|
end
|
|
262
273
|
end
|
|
263
274
|
|
|
275
|
+
# This method is used for serializing arbitrary values into log messages.
|
|
276
|
+
# Because the output is meant to be human-readable, we cannot use
|
|
277
|
+
# the "normal" serialization format which is meant to be machine-readable.
|
|
278
|
+
# Serialize objects with depth of 1 and include the class name.
|
|
279
|
+
#
|
|
280
|
+
# Note that this method does not (currently) utilize the custom
|
|
281
|
+
# serializers that the "normal" serialization logic uses.
|
|
282
|
+
#
|
|
283
|
+
# This serializer differs from the RFC in two ways:
|
|
284
|
+
# 1. We omit the middle of long strings rather than the end,
|
|
285
|
+
# and also the inner entries in arrays/hashes/objects.
|
|
286
|
+
# 2. We use Ruby-ish syntax for hashes and objects.
|
|
287
|
+
#
|
|
288
|
+
# We also use the Ruby-like syntax for symbols, which don't exist
|
|
289
|
+
# in other languages.
|
|
290
|
+
def serialize_value_for_message(value, depth = 1)
|
|
291
|
+
# This method is more verbose than "normal" Ruby code to avoid
|
|
292
|
+
# array allocations.
|
|
293
|
+
case value
|
|
294
|
+
when NilClass
|
|
295
|
+
'nil'
|
|
296
|
+
when Integer, Float, TrueClass, FalseClass, Time, Date
|
|
297
|
+
value.to_s
|
|
298
|
+
when String
|
|
299
|
+
serialize_string_or_symbol_for_message(value)
|
|
300
|
+
when Symbol
|
|
301
|
+
':' + serialize_string_or_symbol_for_message(value)
|
|
302
|
+
when Array
|
|
303
|
+
return '...' if depth <= 0
|
|
304
|
+
|
|
305
|
+
max = max_capture_collection_size_for_message
|
|
306
|
+
if value.length > max
|
|
307
|
+
value_ = value[0...max - 1] || []
|
|
308
|
+
value_ << '...'
|
|
309
|
+
value_ << value[-1]
|
|
310
|
+
value = value_
|
|
311
|
+
end
|
|
312
|
+
'[' + value.map do |item|
|
|
313
|
+
serialize_value_for_message(item, depth - 1)
|
|
314
|
+
end.join(', ') + ']'
|
|
315
|
+
when Hash
|
|
316
|
+
return '...' if depth <= 0
|
|
317
|
+
|
|
318
|
+
max = max_capture_collection_size_for_message
|
|
319
|
+
keys = value.keys
|
|
320
|
+
truncated = false
|
|
321
|
+
if value.length > max
|
|
322
|
+
keys_ = keys[0...max - 1] || []
|
|
323
|
+
keys_ << keys[-1]
|
|
324
|
+
keys = keys_
|
|
325
|
+
truncated = true
|
|
326
|
+
end
|
|
327
|
+
serialized = keys.map do |key|
|
|
328
|
+
"#{serialize_value_for_message(key, depth - 1)} => #{serialize_value_for_message(value[key], depth - 1)}"
|
|
329
|
+
end
|
|
330
|
+
if truncated
|
|
331
|
+
serialized[serialized.length] = serialized[serialized.length - 1]
|
|
332
|
+
serialized[serialized.length - 2] = '...'
|
|
333
|
+
end
|
|
334
|
+
"{#{serialized.join(", ")}}"
|
|
335
|
+
else
|
|
336
|
+
return '...' if depth <= 0
|
|
337
|
+
|
|
338
|
+
vars = value.instance_variables
|
|
339
|
+
truncated = false
|
|
340
|
+
max = max_capture_attribute_count_for_message
|
|
341
|
+
if vars.length > max
|
|
342
|
+
vars_ = vars[0...max - 1] || []
|
|
343
|
+
vars_ << vars[-1]
|
|
344
|
+
truncated = true
|
|
345
|
+
vars = vars_
|
|
346
|
+
end
|
|
347
|
+
serialized = vars.map do |var|
|
|
348
|
+
# +var+ here is always the instance variable name which is a
|
|
349
|
+
# symbol, we do not need to run it through our serializer.
|
|
350
|
+
"#{var}=#{serialize_value_for_message(value.send(:instance_variable_get, var), depth - 1)}"
|
|
351
|
+
end
|
|
352
|
+
if truncated
|
|
353
|
+
serialized << serialized.last
|
|
354
|
+
serialized[-2] = '...'
|
|
355
|
+
end
|
|
356
|
+
serialized = if serialized.any?
|
|
357
|
+
' ' + serialized.join(' ')
|
|
358
|
+
end
|
|
359
|
+
"#<#{class_name(value.class)}#{serialized}>"
|
|
360
|
+
end
|
|
361
|
+
rescue => exc
|
|
362
|
+
telemetry&.report(exc, description: "Error serializing for message")
|
|
363
|
+
# TODO class_name(foo) can also fail, which we don't handle here.
|
|
364
|
+
# Telemetry reporting could potentially also fail?
|
|
365
|
+
"#<#{class_name(value.class)}: serialization error>"
|
|
366
|
+
end
|
|
367
|
+
|
|
264
368
|
private
|
|
265
369
|
|
|
370
|
+
MAX_MESSAGE_COLLECTION_SIZE = 3
|
|
371
|
+
MAX_MESSAGE_ATTRIBUTE_COUNT = 5
|
|
372
|
+
|
|
373
|
+
def max_capture_collection_size_for_message
|
|
374
|
+
max = settings.dynamic_instrumentation.max_capture_collection_size
|
|
375
|
+
if max > MAX_MESSAGE_COLLECTION_SIZE
|
|
376
|
+
max = MAX_MESSAGE_COLLECTION_SIZE
|
|
377
|
+
end
|
|
378
|
+
max
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
def max_capture_attribute_count_for_message
|
|
382
|
+
max = settings.dynamic_instrumentation.max_capture_attribute_count
|
|
383
|
+
if max > MAX_MESSAGE_ATTRIBUTE_COUNT
|
|
384
|
+
max = MAX_MESSAGE_ATTRIBUTE_COUNT
|
|
385
|
+
end
|
|
386
|
+
max
|
|
387
|
+
end
|
|
388
|
+
|
|
266
389
|
# Returns the name for the specified class object.
|
|
267
390
|
#
|
|
268
391
|
# Ruby can have nameless classes, e.g. Class.new is a class object
|
|
@@ -273,6 +396,27 @@ module Datadog
|
|
|
273
396
|
# and we don't want to invoke user code.
|
|
274
397
|
cls.name || "[Unnamed class]"
|
|
275
398
|
end
|
|
399
|
+
|
|
400
|
+
def serialize_string_or_symbol_for_message(value)
|
|
401
|
+
max = settings.dynamic_instrumentation.max_capture_string_length
|
|
402
|
+
if max > 100
|
|
403
|
+
max = 100
|
|
404
|
+
end
|
|
405
|
+
value = value.to_s
|
|
406
|
+
if (length = value.length) > max
|
|
407
|
+
if max < 5
|
|
408
|
+
value[0...max]
|
|
409
|
+
else
|
|
410
|
+
upper = length - max / 2 + 1
|
|
411
|
+
if max % 2 == 0
|
|
412
|
+
upper += 1
|
|
413
|
+
end
|
|
414
|
+
value[0...max / 2 - 1] + '...' + value[upper...length]
|
|
415
|
+
end
|
|
416
|
+
else
|
|
417
|
+
value
|
|
418
|
+
end
|
|
419
|
+
end
|
|
276
420
|
end
|
|
277
421
|
end
|
|
278
422
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative '../../core/transport/parcel'
|
|
4
|
-
require_relative 'http/
|
|
4
|
+
require_relative 'http/diagnostics'
|
|
5
5
|
|
|
6
6
|
module Datadog
|
|
7
7
|
module DI
|
|
@@ -21,7 +21,7 @@ module Datadog
|
|
|
21
21
|
@apis = apis
|
|
22
22
|
@logger = logger
|
|
23
23
|
|
|
24
|
-
@client = HTTP::Client.new(current_api, logger: logger)
|
|
24
|
+
@client = DI::Transport::HTTP::Diagnostics::Client.new(current_api, logger: logger)
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def current_api
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative '../../../core/transport/http/api/instance'
|
|
4
4
|
require_relative '../../../core/transport/http/api/spec'
|
|
5
|
-
require_relative 'client'
|
|
5
|
+
require_relative '../../../core/transport/http/client'
|
|
6
6
|
|
|
7
7
|
module Datadog
|
|
8
8
|
module DI
|
|
9
9
|
module Transport
|
|
10
10
|
module HTTP
|
|
11
11
|
module Diagnostics
|
|
12
|
-
|
|
12
|
+
class Client < Core::Transport::HTTP::Client
|
|
13
13
|
def send_diagnostics_payload(request)
|
|
14
14
|
send_request(request) do |api, env|
|
|
15
15
|
api.send_diagnostics(env)
|
|
@@ -57,8 +57,6 @@ module Datadog
|
|
|
57
57
|
end
|
|
58
58
|
end
|
|
59
59
|
end
|
|
60
|
-
|
|
61
|
-
HTTP::Client.include(Diagnostics::Client)
|
|
62
60
|
end
|
|
63
61
|
end
|
|
64
62
|
end
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative '../../../core/transport/http/api/instance'
|
|
4
4
|
require_relative '../../../core/transport/http/api/spec'
|
|
5
|
-
require_relative 'client'
|
|
5
|
+
require_relative '../../../core/transport/http/client'
|
|
6
6
|
|
|
7
7
|
module Datadog
|
|
8
8
|
module DI
|
|
9
9
|
module Transport
|
|
10
10
|
module HTTP
|
|
11
11
|
module Input
|
|
12
|
-
|
|
12
|
+
class Client < Core::Transport::HTTP::Client
|
|
13
13
|
def send_input_payload(request)
|
|
14
14
|
send_request(request) do |api, env|
|
|
15
15
|
api.send_input(env)
|
|
@@ -69,8 +69,6 @@ module Datadog
|
|
|
69
69
|
end
|
|
70
70
|
end
|
|
71
71
|
end
|
|
72
|
-
|
|
73
|
-
HTTP::Client.include(Input::Client)
|
|
74
72
|
end
|
|
75
73
|
end
|
|
76
74
|
end
|
|
@@ -40,9 +40,13 @@ module Datadog
|
|
|
40
40
|
api_version: nil,
|
|
41
41
|
headers: nil
|
|
42
42
|
)
|
|
43
|
-
Core::Transport::HTTP.build(
|
|
43
|
+
Core::Transport::HTTP.build(
|
|
44
|
+
api_instance_class: Input::API::Instance,
|
|
44
45
|
logger: logger,
|
|
45
|
-
agent_settings: agent_settings,
|
|
46
|
+
agent_settings: agent_settings,
|
|
47
|
+
api_version: api_version,
|
|
48
|
+
headers: headers,
|
|
49
|
+
) do |transport|
|
|
46
50
|
apis = API.defaults
|
|
47
51
|
|
|
48
52
|
transport.api API::INPUT, apis[API::INPUT]
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative '../../core/chunker'
|
|
4
|
+
require_relative '../../core/encoding'
|
|
5
|
+
require_relative '../../core/tag_builder'
|
|
3
6
|
require_relative '../../core/transport/parcel'
|
|
4
|
-
require_relative '
|
|
7
|
+
require_relative '../../core/transport/request'
|
|
8
|
+
require_relative '../error'
|
|
9
|
+
require_relative 'http/input'
|
|
5
10
|
|
|
6
11
|
module Datadog
|
|
7
12
|
module DI
|
|
@@ -24,11 +29,30 @@ module Datadog
|
|
|
24
29
|
class Transport
|
|
25
30
|
attr_reader :client, :apis, :default_api, :current_api_id, :logger
|
|
26
31
|
|
|
32
|
+
# The limit on an individual snapshot payload, aka "log line",
|
|
33
|
+
# is 1 MB.
|
|
34
|
+
#
|
|
35
|
+
# TODO There is an RFC for snapshot pruning that should be
|
|
36
|
+
# implemented to reduce the size of snapshots to be below this
|
|
37
|
+
# limit, so that we can send a portion of the captured data
|
|
38
|
+
# rather than dropping the snapshot entirely.
|
|
39
|
+
MAX_SERIALIZED_SNAPSHOT_SIZE = 1024 * 1024
|
|
40
|
+
|
|
41
|
+
# The maximum chunk (batch) size that intake permits is 5 MB.
|
|
42
|
+
#
|
|
43
|
+
# Two bytes are for the [ and ] of JSON array syntax.
|
|
44
|
+
MAX_CHUNK_SIZE = 5 * 1024 * 1024 - 2
|
|
45
|
+
|
|
46
|
+
# Try to send smaller payloads to avoid large network requests.
|
|
47
|
+
# If a payload is larger than default chunk size but is under the
|
|
48
|
+
# max chunk size, it will still get sent out.
|
|
49
|
+
DEFAULT_CHUNK_SIZE = 2 * 1024 * 1024
|
|
50
|
+
|
|
27
51
|
def initialize(apis, default_api, logger:)
|
|
28
52
|
@apis = apis
|
|
29
53
|
@logger = logger
|
|
30
54
|
|
|
31
|
-
@client = HTTP::Client.new(current_api, logger: logger)
|
|
55
|
+
@client = DI::Transport::HTTP::Input::Client.new(current_api, logger: logger)
|
|
32
56
|
end
|
|
33
57
|
|
|
34
58
|
def current_api
|
|
@@ -36,9 +60,45 @@ module Datadog
|
|
|
36
60
|
end
|
|
37
61
|
|
|
38
62
|
def send_input(payload, tags)
|
|
39
|
-
|
|
40
|
-
parcel = EncodedParcel.new(json)
|
|
63
|
+
# Tags are the same for all chunks, serialize them one time.
|
|
41
64
|
serialized_tags = Core::TagBuilder.serialize_tags(tags)
|
|
65
|
+
|
|
66
|
+
encoder = Core::Encoding::JSONEncoder
|
|
67
|
+
encoded_snapshots = Core::Utils::Array.filter_map(payload) do |snapshot|
|
|
68
|
+
encoded = encoder.encode(snapshot)
|
|
69
|
+
if encoded.length > MAX_SERIALIZED_SNAPSHOT_SIZE
|
|
70
|
+
# Drop the snapshot.
|
|
71
|
+
# TODO report via telemetry metric?
|
|
72
|
+
logger.debug { "di: dropping too big snapshot" }
|
|
73
|
+
nil
|
|
74
|
+
else
|
|
75
|
+
encoded
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
Datadog::Core::Chunker.chunk_by_size(
|
|
80
|
+
encoded_snapshots, DEFAULT_CHUNK_SIZE,
|
|
81
|
+
).each do |chunk|
|
|
82
|
+
# We drop snapshots that are too big earlier.
|
|
83
|
+
# The limit on chunked payload length here is greater
|
|
84
|
+
# than the limit on snapshot size, therefore no chunks
|
|
85
|
+
# can exceed limits here.
|
|
86
|
+
chunked_payload = encoder.join(chunk)
|
|
87
|
+
|
|
88
|
+
# We need to rescue exceptions for each chunk so that
|
|
89
|
+
# subsequent chunks are attempted to be sent.
|
|
90
|
+
begin
|
|
91
|
+
send_input_chunk(chunked_payload, serialized_tags)
|
|
92
|
+
rescue => exc
|
|
93
|
+
logger.debug { "di: failed to send snapshot chunk: #{exc.class}: #{exc} (at #{exc.backtrace.first})" }
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
payload
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def send_input_chunk(chunked_payload, serialized_tags)
|
|
101
|
+
parcel = EncodedParcel.new(chunked_payload)
|
|
42
102
|
request = Request.new(parcel, serialized_tags)
|
|
43
103
|
|
|
44
104
|
response = @client.send_input_payload(request)
|