datadog 2.46.0 → 2.48.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 +56 -2
- data/LICENSE-3rdparty.csv +1 -0
- data/ext/datadog_profiling_native_extension/collectors_stack.c +143 -86
- data/ext/datadog_profiling_native_extension/collectors_stack.h +9 -1
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +6 -1
- data/ext/datadog_profiling_native_extension/extconf.rb +7 -13
- data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +3 -3
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +67 -14
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +8 -1
- data/ext/datadog_profiling_native_extension/profiling.c +1 -1
- data/ext/libdatadog_api/extconf.rb +9 -7
- data/ext/libdatadog_api/trace_exporter.c +34 -7
- data/ext/libdatadog_extconf_helpers.rb +2 -2
- data/lib/datadog/ai_guard/component.rb +1 -1
- data/lib/datadog/ai_guard/contrib/rack/request_middleware.rb +1 -9
- data/lib/datadog/ai_guard/contrib/rails/patcher.rb +1 -1
- data/lib/datadog/ai_guard/contrib/ruby_llm/message_adapter.rb +1 -1
- data/lib/datadog/ai_guard/contrib/ruby_llm/redaction_change_set.rb +3 -3
- data/lib/datadog/ai_guard/evaluation/client.rb +3 -3
- data/lib/datadog/ai_guard/evaluation.rb +2 -2
- data/lib/datadog/ai_guard/http_client.rb +3 -2
- data/lib/datadog/ai_guard/redaction.rb +1 -1
- data/lib/datadog/appsec/api_security/endpoint_collection/rails_collector.rb +1 -1
- data/lib/datadog/appsec/assets/waf_rules/README.md +3 -3
- data/lib/datadog/appsec/assets/waf_rules/recommended.json +5366 -1517
- data/lib/datadog/appsec/assets/waf_rules/strict.json +2 -2
- data/lib/datadog/appsec/autoload.rb +1 -1
- data/lib/datadog/appsec/component.rb +1 -1
- data/lib/datadog/appsec/compressed_json.rb +1 -1
- data/lib/datadog/appsec/configuration.rb +3 -3
- data/lib/datadog/appsec/context.rb +1 -1
- data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +11 -1
- data/lib/datadog/appsec/contrib/active_record/patcher.rb +12 -4
- data/lib/datadog/appsec/contrib/aws_lambda/gateway/watcher.rb +2 -2
- data/lib/datadog/appsec/contrib/aws_lambda/waf_addresses.rb +1 -1
- data/lib/datadog/appsec/contrib/devise/configuration.rb +1 -1
- data/lib/datadog/appsec/contrib/devise/patches/signin_tracking_patch.rb +1 -1
- data/lib/datadog/appsec/contrib/devise/patches/signup_tracking_patch.rb +1 -1
- data/lib/datadog/appsec/contrib/devise/tracking_middleware.rb +1 -1
- data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +1 -1
- data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +1 -1
- data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +7 -7
- data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +1 -1
- data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +3 -3
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +5 -5
- data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +31 -4
- data/lib/datadog/appsec/contrib/rails/patcher.rb +2 -2
- data/lib/datadog/appsec/contrib/rails/patches/render_to_body_patch.rb +8 -7
- data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +1 -1
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +17 -5
- data/lib/datadog/appsec/contrib/sinatra/patcher.rb +1 -1
- data/lib/datadog/appsec/contrib/sinatra/patches/json_patch.rb +8 -7
- data/lib/datadog/appsec/metrics/collector.rb +5 -5
- data/lib/datadog/appsec/metrics/telemetry_exporter.rb +3 -3
- data/lib/datadog/appsec/monitor/gateway/watcher.rb +2 -2
- data/lib/datadog/appsec/processor/rule_loader.rb +1 -1
- data/lib/datadog/appsec/response.rb +1 -1
- data/lib/datadog/appsec/security_engine/engine.rb +8 -7
- data/lib/datadog/appsec/security_engine/runner.rb +2 -2
- data/lib/datadog/appsec/trace_keeper.rb +1 -1
- data/lib/datadog/appsec.rb +1 -1
- data/lib/datadog/core/configuration/agent_settings_resolver.rb +14 -13
- data/lib/datadog/core/configuration/agentless_settings_resolver.rb +2 -2
- data/lib/datadog/core/configuration/components.rb +12 -11
- data/lib/datadog/core/configuration/components_state.rb +8 -1
- data/lib/datadog/core/configuration/settings.rb +5 -2
- data/lib/datadog/core/configuration/supported_configurations.rb +3 -0
- data/lib/datadog/core/configuration.rb +2 -2
- data/lib/datadog/core/crashtracking/component.rb +1 -1
- data/lib/datadog/core/diagnostics/environment_logger.rb +1 -1
- data/lib/datadog/core/environment/cgroup.rb +2 -2
- data/lib/datadog/core/environment/container.rb +3 -3
- data/lib/datadog/core/metrics/client.rb +6 -6
- data/lib/datadog/core/process_discovery.rb +1 -1
- data/lib/datadog/core/remote/client.rb +44 -3
- data/lib/datadog/core/remote/dispatcher.rb +34 -3
- data/lib/datadog/core/remote/tie/tracing.rb +4 -3
- data/lib/datadog/core/remote/transport/http.rb +2 -2
- data/lib/datadog/core/runtime/metrics.rb +14 -14
- data/lib/datadog/core/semaphore.rb +3 -1
- data/lib/datadog/core/telemetry/event/app_started.rb +9 -9
- data/lib/datadog/core/telemetry/logger.rb +1 -1
- data/lib/datadog/core/telemetry/logging.rb +1 -1
- data/lib/datadog/core/telemetry/request.rb +9 -3
- data/lib/datadog/core/telemetry/transport/http.rb +2 -2
- data/lib/datadog/core/telemetry/worker.rb +1 -1
- data/lib/datadog/core/transport/http/adapters/net.rb +1 -1
- data/lib/datadog/core/transport/http/adapters/unix_socket.rb +1 -1
- data/lib/datadog/core/transport/http/builder.rb +1 -1
- data/lib/datadog/core/transport/http.rb +3 -3
- data/lib/datadog/core/utils/at_fork_monkey_patch.rb +2 -2
- data/lib/datadog/core/workers/async.rb +1 -1
- data/lib/datadog/core/workers/runtime_metrics.rb +2 -2
- data/lib/datadog/data_streams/pathway_context.rb +1 -1
- data/lib/datadog/data_streams/processor.rb +48 -27
- data/lib/datadog/data_streams/transport/http.rb +1 -1
- data/lib/datadog/data_streams.rb +16 -0
- data/lib/datadog/di/capture_expression_evaluator.rb +48 -11
- data/lib/datadog/di/component.rb +73 -2
- data/lib/datadog/di/configuration.rb +19 -0
- data/lib/datadog/di/contrib/active_record.rb +1 -1
- data/lib/datadog/di/el/compiler.rb +4 -4
- data/lib/datadog/di/el/evaluator.rb +247 -43
- data/lib/datadog/di/el/expression.rb +36 -6
- data/lib/datadog/di/error.rb +16 -0
- data/lib/datadog/di/guardrails_telemetry/reason.rb +3 -2
- data/lib/datadog/di/instrumenter.rb +39 -10
- data/lib/datadog/di/probe_notification_builder.rb +58 -10
- data/lib/datadog/di/probe_notifier_worker.rb +1 -1
- data/lib/datadog/di/remote.rb +46 -2
- data/lib/datadog/di/serializer.rb +47 -13
- data/lib/datadog/di.rb +27 -0
- data/lib/datadog/error_tracking/component.rb +1 -1
- data/lib/datadog/error_tracking/configuration.rb +1 -1
- data/lib/datadog/kit/appsec/events/v2.rb +6 -6
- data/lib/datadog/kit/appsec/events.rb +1 -1
- data/lib/datadog/kit/enable_core_dumps.rb +1 -1
- data/lib/datadog/kit/identity.rb +1 -1
- data/lib/datadog/open_feature/activation.rb +26 -1
- data/lib/datadog/open_feature/agentless/configuration_source.rb +94 -15
- data/lib/datadog/open_feature/component.rb +22 -2
- data/lib/datadog/open_feature/configuration/agentless_endpoint.rb +6 -0
- data/lib/datadog/open_feature/configuration.rb +10 -4
- data/lib/datadog/open_feature/evaluation_engine.rb +62 -4
- data/lib/datadog/open_feature/exposures/buffer.rb +4 -0
- data/lib/datadog/open_feature/exposures/reporter.rb +17 -4
- data/lib/datadog/open_feature/exposures/worker.rb +37 -6
- data/lib/datadog/open_feature/flag_evaluation/aggregator.rb +5 -5
- data/lib/datadog/open_feature/flag_evaluation/writer.rb +1 -1
- data/lib/datadog/open_feature/hooks/debug_logging_hook.rb +56 -0
- data/lib/datadog/open_feature/hooks/evaluation_telemetry_opt_out_hook.rb +25 -0
- data/lib/datadog/open_feature/hooks/span_enrichment_hook.rb +2 -2
- data/lib/datadog/open_feature/metrics/flag_eval_metrics.rb +1 -1
- data/lib/datadog/open_feature/native_evaluator.rb +1 -1
- data/lib/datadog/open_feature/noop_evaluator.rb +1 -1
- data/lib/datadog/open_feature/provider.rb +69 -12
- data/lib/datadog/open_feature/remote.rb +8 -1
- data/lib/datadog/open_feature/resolution_details.rb +2 -2
- data/lib/datadog/open_feature/transport.rb +6 -6
- data/lib/datadog/opentelemetry/api/context.rb +3 -3
- data/lib/datadog/opentelemetry/logs.rb +5 -5
- data/lib/datadog/opentelemetry/metrics.rb +3 -3
- data/lib/datadog/opentelemetry/sdk/propagator.rb +3 -3
- data/lib/datadog/opentelemetry/sdk/span_processor.rb +2 -2
- data/lib/datadog/opentelemetry/sdk/trace/span.rb +1 -1
- data/lib/datadog/profiling/collectors/code_provenance.rb +3 -3
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +4 -4
- data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -1
- data/lib/datadog/profiling/collectors/info.rb +37 -6
- data/lib/datadog/profiling/collectors/thread_context.rb +1 -1
- data/lib/datadog/profiling/component.rb +41 -39
- data/lib/datadog/profiling/exporter.rb +4 -6
- data/lib/datadog/profiling/http_transport.rb +3 -3
- data/lib/datadog/profiling/profiler.rb +2 -2
- data/lib/datadog/profiling/scheduler.rb +2 -2
- data/lib/datadog/profiling/tag_builder.rb +1 -1
- data/lib/datadog/symbol_database/extractor.rb +8 -8
- data/lib/datadog/symbol_database/uploader.rb +1 -1
- data/lib/datadog/tracing/buffer.rb +3 -3
- data/lib/datadog/tracing/client_ip.rb +1 -1
- data/lib/datadog/tracing/component.rb +15 -10
- data/lib/datadog/tracing/configuration/dynamic/option.rb +2 -2
- data/lib/datadog/tracing/configuration/ext.rb +2 -0
- data/lib/datadog/tracing/configuration/settings.rb +14 -3
- data/lib/datadog/tracing/context.rb +1 -1
- data/lib/datadog/tracing/context_provider.rb +8 -0
- data/lib/datadog/tracing/contrib/action_cable/instrumentation.rb +2 -2
- data/lib/datadog/tracing/contrib/active_job/data_streams.rb +27 -2
- data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +2 -2
- data/lib/datadog/tracing/contrib/active_record/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/active_record/utils.rb +1 -1
- data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +1 -1
- data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/active_support/notifications/subscriber.rb +2 -2
- data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +2 -2
- data/lib/datadog/tracing/contrib/aws/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/aws/instrumentation.rb +1 -1
- data/lib/datadog/tracing/contrib/dalli/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/dalli/instrumentation.rb +1 -1
- data/lib/datadog/tracing/contrib/delayed_job/plugin.rb +2 -2
- data/lib/datadog/tracing/contrib/elasticsearch/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/ethon/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +17 -12
- data/lib/datadog/tracing/contrib/ethon/multi_patch.rb +1 -1
- data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/excon/middleware.rb +16 -9
- data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/faraday/middleware.rb +14 -8
- data/lib/datadog/tracing/contrib/grape/endpoint.rb +4 -4
- data/lib/datadog/tracing/contrib/graphql/patcher.rb +1 -1
- data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +3 -3
- data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +2 -2
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +1 -1
- data/lib/datadog/tracing/contrib/grpc.rb +1 -1
- data/lib/datadog/tracing/contrib/hanami/action_tracer.rb +1 -1
- data/lib/datadog/tracing/contrib/hanami/plugin.rb +1 -1
- data/lib/datadog/tracing/contrib/hanami/renderer_policy_tracing.rb +1 -1
- data/lib/datadog/tracing/contrib/hanami/router_tracing.rb +1 -1
- data/lib/datadog/tracing/contrib/http/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/http/instrumentation.rb +21 -6
- data/lib/datadog/tracing/contrib/http.rb +1 -1
- data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +14 -8
- data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +14 -8
- data/lib/datadog/tracing/contrib/kafka/instrumentation/consumer.rb +2 -2
- data/lib/datadog/tracing/contrib/kafka/instrumentation/producer.rb +2 -2
- data/lib/datadog/tracing/contrib/karafka/monitor.rb +1 -1
- data/lib/datadog/tracing/contrib/karafka/patcher.rb +1 -1
- data/lib/datadog/tracing/contrib/karafka.rb +1 -1
- data/lib/datadog/tracing/contrib/lograge/patcher.rb +1 -1
- data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/mongodb/parsers.rb +1 -1
- data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +1 -1
- data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +3 -3
- data/lib/datadog/tracing/contrib/opensearch/configuration/settings.rb +2 -2
- data/lib/datadog/tracing/contrib/opensearch/patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/pg/instrumentation.rb +4 -4
- data/lib/datadog/tracing/contrib/presto/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/presto/instrumentation.rb +4 -4
- data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +1 -1
- data/lib/datadog/tracing/contrib/que/tracer.rb +1 -1
- data/lib/datadog/tracing/contrib/racecar/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/racecar/events/batch.rb +1 -1
- data/lib/datadog/tracing/contrib/racecar/events/message.rb +1 -1
- data/lib/datadog/tracing/contrib/rack/middlewares.rb +3 -3
- data/lib/datadog/tracing/contrib/rack/patcher.rb +1 -1
- data/lib/datadog/tracing/contrib/rails/framework.rb +7 -7
- data/lib/datadog/tracing/contrib/rails/log_injection.rb +1 -1
- data/lib/datadog/tracing/contrib/rails/runner.rb +4 -4
- data/lib/datadog/tracing/contrib/redis/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/redis/quantize.rb +1 -1
- data/lib/datadog/tracing/contrib/redis/tags.rb +1 -1
- data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +17 -10
- data/lib/datadog/tracing/contrib/sequel/database.rb +1 -1
- data/lib/datadog/tracing/contrib/sequel/dataset.rb +1 -1
- data/lib/datadog/tracing/contrib/shoryuken/tracer.rb +1 -1
- data/lib/datadog/tracing/contrib/sidekiq/client_tracer.rb +2 -2
- data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +2 -2
- data/lib/datadog/tracing/contrib/sidekiq.rb +1 -1
- data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +1 -1
- data/lib/datadog/tracing/contrib/status_range_env_parser.rb +1 -1
- data/lib/datadog/tracing/contrib/sucker_punch/instrumentation.rb +1 -1
- data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +3 -3
- data/lib/datadog/tracing/contrib/utils/quantization/http.rb +102 -0
- data/lib/datadog/tracing/contrib/waterdrop/middleware.rb +2 -2
- data/lib/datadog/tracing/contrib/waterdrop.rb +1 -1
- data/lib/datadog/tracing/diagnostics/environment_logger.rb +1 -1
- data/lib/datadog/tracing/distributed/b3_multi.rb +1 -1
- data/lib/datadog/tracing/distributed/b3_single.rb +1 -1
- data/lib/datadog/tracing/distributed/baggage.rb +3 -3
- data/lib/datadog/tracing/distributed/datadog.rb +3 -3
- data/lib/datadog/tracing/distributed/propagation.rb +7 -7
- data/lib/datadog/tracing/distributed/trace_context.rb +1 -1
- data/lib/datadog/tracing/distributed/trace_state/open_telemetry.rb +1 -1
- data/lib/datadog/tracing/otel_thread_context.rb +3 -3
- data/lib/datadog/tracing/pipeline.rb +1 -1
- data/lib/datadog/tracing/remote.rb +50 -19
- data/lib/datadog/tracing/sampling/priority_sampler.rb +1 -1
- data/lib/datadog/tracing/sampling/rule.rb +1 -1
- data/lib/datadog/tracing/sampling/rule_sampler.rb +1 -1
- data/lib/datadog/tracing/sampling/span/rule_parser.rb +2 -2
- data/lib/datadog/tracing/span_operation.rb +1 -1
- data/lib/datadog/tracing/trace_digest.rb +1 -1
- data/lib/datadog/tracing/trace_operation.rb +6 -6
- data/lib/datadog/tracing/tracer.rb +6 -6
- data/lib/datadog/tracing/transport/http/telemetry.rb +1 -1
- data/lib/datadog/tracing/transport/http.rb +2 -2
- data/lib/datadog/tracing/transport/io.rb +1 -1
- data/lib/datadog/tracing/transport/native.rb +3 -3
- data/lib/datadog/tracing/transport/statistics.rb +2 -2
- data/lib/datadog/tracing/transport/telemetry.rb +3 -1
- data/lib/datadog/tracing/transport/trace_formatter.rb +12 -12
- data/lib/datadog/tracing/transport/traces.rb +1 -1
- data/lib/datadog/tracing/utils.rb +1 -1
- data/lib/datadog/tracing/workers.rb +1 -1
- data/lib/datadog/version.rb +1 -1
- metadata +9 -8
- data/lib/datadog/appsec/utils/hash_coercion.rb +0 -23
|
@@ -7,6 +7,7 @@ require_relative "evaluation_failure"
|
|
|
7
7
|
require_relative "fatal_exceptions"
|
|
8
8
|
require_relative "guardrails_telemetry"
|
|
9
9
|
require_relative "../core/utils/time"
|
|
10
|
+
require_relative "el"
|
|
10
11
|
|
|
11
12
|
module Datadog
|
|
12
13
|
module DI
|
|
@@ -16,7 +17,7 @@ module Datadog
|
|
|
16
17
|
# @param settings [Datadog::Core::Configuration::Settings] tracer settings
|
|
17
18
|
# @param serializer [Serializer] serializer for captured expression values
|
|
18
19
|
# @param logger [DI::Logger] logger for evaluation failure diagnostics
|
|
19
|
-
# @param guardrails_telemetry [GuardrailsTelemetry] emitter for the canonical
|
|
20
|
+
# @param guardrails_telemetry [GuardrailsTelemetry] emitter for the canonical evaluation-timeout skip metric
|
|
20
21
|
# @param telemetry [Datadog::Core::Telemetry::Component, nil] component evaluation failures are reported through
|
|
21
22
|
# @return [void]
|
|
22
23
|
def initialize(settings:, serializer:, logger:, guardrails_telemetry:, telemetry: nil)
|
|
@@ -42,8 +43,13 @@ module Datadog
|
|
|
42
43
|
# Evaluates each of the probe's capture expressions against the given
|
|
43
44
|
# context, serializing the captured values under one capture deadline
|
|
44
45
|
# resolved through the serializer and shared across all expressions.
|
|
45
|
-
#
|
|
46
|
-
#
|
|
46
|
+
# Each expression resolves its own evaluation deadline immediately
|
|
47
|
+
# before its evaluation and is evaluated under the earlier of that
|
|
48
|
+
# deadline and the capture deadline, so the capture expressions
|
|
49
|
+
# together stay within the capture budget. An expression the capture
|
|
50
|
+
# budget stops is reported as not captured; an expression that fails
|
|
51
|
+
# to evaluate or serialize otherwise contributes an entry to the
|
|
52
|
+
# returned errors.
|
|
47
53
|
#
|
|
48
54
|
# @param probe [Probe] the probe whose capture expressions are evaluated
|
|
49
55
|
# @param context [Context] evaluation context the expressions are evaluated against
|
|
@@ -59,18 +65,14 @@ module Datadog
|
|
|
59
65
|
probe.capture_expressions.each do |capture_expression|
|
|
60
66
|
name = capture_expression.name
|
|
61
67
|
|
|
62
|
-
if
|
|
63
|
-
output
|
|
64
|
-
telemetry&.inc(DI::TELEMETRY_NAMESPACE, "capture_expressions_skipped_by_timeout", 1)
|
|
65
|
-
guardrails_telemetry.skipped(
|
|
66
|
-
reason: GuardrailsTelemetry::Reason::EVALUATION_TIMEOUT,
|
|
67
|
-
probe: probe,
|
|
68
|
-
)
|
|
68
|
+
if capture_deadline_passed?(deadline)
|
|
69
|
+
record_capture_timeout(output, name, probe)
|
|
69
70
|
next
|
|
70
71
|
end
|
|
71
72
|
|
|
72
73
|
begin
|
|
73
|
-
|
|
74
|
+
evaluation_deadline = [serializer.evaluation_deadline, deadline].min
|
|
75
|
+
value = capture_expression.expr.evaluate(context, evaluation_deadline: evaluation_deadline)
|
|
74
76
|
limits = CaptureLimits.resolve(
|
|
75
77
|
expr_limits: capture_expression.limits,
|
|
76
78
|
probe: probe,
|
|
@@ -86,6 +88,14 @@ module Datadog
|
|
|
86
88
|
)
|
|
87
89
|
rescue Exception => exc # standard:disable Lint/RescueException
|
|
88
90
|
Datadog::DI.reraise_if_fatal(exc)
|
|
91
|
+
if exc.is_a?(DI::Error::EvaluationTimeout) && capture_deadline_passed?(deadline)
|
|
92
|
+
record_capture_timeout(output, name, probe)
|
|
93
|
+
next
|
|
94
|
+
end
|
|
95
|
+
DI.report_evaluation_timeout(
|
|
96
|
+
telemetry: telemetry, guardrails_telemetry: guardrails_telemetry, logger: logger,
|
|
97
|
+
probe: probe, kind: "capture expression", exc: exc,
|
|
98
|
+
)
|
|
89
99
|
evaluation_errors << EvaluationFailure.new(expr: name, message: "#{exc.class}: #{exc.message}")
|
|
90
100
|
logger.debug do
|
|
91
101
|
"di: probe #{probe.id}: capture expression #{name}: evaluation failed: #{exc.class}: #{exc.message}"
|
|
@@ -96,6 +106,33 @@ module Datadog
|
|
|
96
106
|
|
|
97
107
|
Result.new(serialized_values: output, errors: evaluation_errors)
|
|
98
108
|
end
|
|
109
|
+
|
|
110
|
+
private
|
|
111
|
+
|
|
112
|
+
# Returns whether the thread CPU clock has reached +capture_deadline+.
|
|
113
|
+
#
|
|
114
|
+
# @param capture_deadline [Float] the capture deadline, as
|
|
115
|
+
# CLOCK_THREAD_CPUTIME_ID float seconds.
|
|
116
|
+
# @return [Boolean]
|
|
117
|
+
def capture_deadline_passed?(capture_deadline)
|
|
118
|
+
Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :float_second) >= capture_deadline
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Reports capture expression +name+ as not captured because the
|
|
122
|
+
# capture budget ran out.
|
|
123
|
+
#
|
|
124
|
+
# @param output [Hash{String => Object}] serialized values by name.
|
|
125
|
+
# @param name [String] the capture expression's name.
|
|
126
|
+
# @param probe [Probe] the probe whose capture expression is stopped.
|
|
127
|
+
# @return [void]
|
|
128
|
+
def record_capture_timeout(output, name, probe)
|
|
129
|
+
output[name] = {notCapturedReason: "timeout"}
|
|
130
|
+
telemetry&.inc(DI::TELEMETRY_NAMESPACE, "capture_expressions_skipped_by_timeout", 1)
|
|
131
|
+
guardrails_telemetry.skipped(
|
|
132
|
+
reason: GuardrailsTelemetry::Reason::EVALUATION_TIMEOUT,
|
|
133
|
+
probe: probe,
|
|
134
|
+
)
|
|
135
|
+
end
|
|
99
136
|
end
|
|
100
137
|
end
|
|
101
138
|
end
|
data/lib/datadog/di/component.rb
CHANGED
|
@@ -100,10 +100,11 @@ module Datadog
|
|
|
100
100
|
)
|
|
101
101
|
@instrumenter = Instrumenter.new(settings, serializer, logger, code_tracker: code_tracker, telemetry: telemetry,
|
|
102
102
|
guardrails_telemetry: guardrails_telemetry,
|
|
103
|
-
capture_expression_evaluator: capture_expression_evaluator
|
|
103
|
+
capture_expression_evaluator: capture_expression_evaluator)
|
|
104
104
|
@probe_repository = ProbeRepository.new
|
|
105
105
|
@probe_notification_builder = ProbeNotificationBuilder.new(settings, serializer, logger,
|
|
106
|
-
capture_expression_evaluator: capture_expression_evaluator,
|
|
106
|
+
capture_expression_evaluator: capture_expression_evaluator, guardrails_telemetry: guardrails_telemetry,
|
|
107
|
+
telemetry: telemetry)
|
|
107
108
|
@probe_notifier_worker = ProbeNotifierWorker.new(
|
|
108
109
|
settings, logger,
|
|
109
110
|
agent_settings: agent_settings,
|
|
@@ -122,6 +123,7 @@ module Datadog
|
|
|
122
123
|
# cannot race a foreground start! with a background stop!.
|
|
123
124
|
@lifecycle_mutex = Mutex.new
|
|
124
125
|
@started = false
|
|
126
|
+
@rc_enablement_signal_paths = []
|
|
125
127
|
end
|
|
126
128
|
|
|
127
129
|
attr_reader :settings
|
|
@@ -201,6 +203,75 @@ module Datadog
|
|
|
201
203
|
@started
|
|
202
204
|
end
|
|
203
205
|
|
|
206
|
+
# Whether the component is started while the customer left
|
|
207
|
+
# DD_DYNAMIC_INSTRUMENTATION_ENABLED at its default, the state produced
|
|
208
|
+
# by remote-configuration enablement.
|
|
209
|
+
#
|
|
210
|
+
# An explicit `enabled = false` takes precedence over a prior RC
|
|
211
|
+
# enablement: once the customer sets the option, this returns false
|
|
212
|
+
# even while the component is still started.
|
|
213
|
+
#
|
|
214
|
+
# @return [Boolean]
|
|
215
|
+
def implicitly_enabled?
|
|
216
|
+
started? && settings.dynamic_instrumentation.using_default?(:enabled)
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# Whether an APM_TRACING merge that omitted dynamic_instrumentation_enabled
|
|
220
|
+
# withdraws this component's RC enable signal, so the component must stop.
|
|
221
|
+
#
|
|
222
|
+
# Returns true when the component is implicitly enabled and no unparseable
|
|
223
|
+
# content can be the signal carrier. A content at a recorded signal path can be
|
|
224
|
+
# the carrier. When no paths are recorded, every unparseable content can be the
|
|
225
|
+
# carrier.
|
|
226
|
+
#
|
|
227
|
+
# @param unparseable_contents [Array<Core::Remote::Configuration::Content>] the APM_TRACING contents that failed to parse at this merge
|
|
228
|
+
# @return [Boolean]
|
|
229
|
+
def rc_enablement_signal_withdrawn?(unparseable_contents)
|
|
230
|
+
return false unless implicitly_enabled?
|
|
231
|
+
|
|
232
|
+
recorded_signal_paths = rc_enablement_signal_paths
|
|
233
|
+
signal_source_unparseable =
|
|
234
|
+
if recorded_signal_paths.empty?
|
|
235
|
+
unparseable_contents.any?
|
|
236
|
+
else
|
|
237
|
+
unparseable_contents.any? { |content| recorded_signal_paths.include?(content.path) }
|
|
238
|
+
end
|
|
239
|
+
if signal_source_unparseable
|
|
240
|
+
if recorded_signal_paths.empty?
|
|
241
|
+
logger.trace do
|
|
242
|
+
"APM_TRACING RC: dynamic_instrumentation_enabled absent while an APM_TRACING config " \
|
|
243
|
+
"failed to parse; the enable signal carrier is unknown (no signal paths recorded), " \
|
|
244
|
+
"keeping the DI state"
|
|
245
|
+
end
|
|
246
|
+
else
|
|
247
|
+
logger.trace do
|
|
248
|
+
"APM_TRACING RC: dynamic_instrumentation_enabled absent while the config that carried " \
|
|
249
|
+
"the enable signal failed to parse; keeping the DI state"
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
false
|
|
253
|
+
else
|
|
254
|
+
logger.trace { "APM_TRACING RC: dynamic_instrumentation_enabled withdrawn; treating as false" }
|
|
255
|
+
true
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
# The repository paths of the APM_TRACING contents that supplied the
|
|
260
|
+
# dynamic_instrumentation_enabled value at the last merge where the field was
|
|
261
|
+
# present. The component holds the paths so that they outlive the RC client.
|
|
262
|
+
#
|
|
263
|
+
# @return [Array<Core::Remote::Configuration::Path>]
|
|
264
|
+
attr_reader :rc_enablement_signal_paths
|
|
265
|
+
|
|
266
|
+
# Replaces the recorded enable-signal carrier paths.
|
|
267
|
+
#
|
|
268
|
+
# @param paths [Array<Core::Remote::Configuration::Path>] the carrier paths, empty when the merged field was absent
|
|
269
|
+
# @return [void]
|
|
270
|
+
def record_rc_enablement_signal_paths(paths)
|
|
271
|
+
@rc_enablement_signal_paths = paths.dup
|
|
272
|
+
nil
|
|
273
|
+
end
|
|
274
|
+
|
|
204
275
|
# Shuts down dynamic instrumentation permanently.
|
|
205
276
|
#
|
|
206
277
|
# Removes all code hooks and stops background threads.
|
|
@@ -169,6 +169,25 @@ module Datadog
|
|
|
169
169
|
end
|
|
170
170
|
end
|
|
171
171
|
|
|
172
|
+
# Maximum thread CPU time, in milliseconds, allowed for
|
|
173
|
+
# evaluating expressions. The env var key is shared with
|
|
174
|
+
# other tracers, which measure wall time for it.
|
|
175
|
+
#
|
|
176
|
+
# @return [Integer]
|
|
177
|
+
option :max_time_to_evaluate_ms do |o|
|
|
178
|
+
o.type :int
|
|
179
|
+
o.default 50
|
|
180
|
+
o.env "DD_DYNAMIC_INSTRUMENTATION_EVALUATION_TIMEOUT_MS"
|
|
181
|
+
o.setter do |value, _old|
|
|
182
|
+
if value < 0
|
|
183
|
+
raise ArgumentError,
|
|
184
|
+
"The setting `dynamic_instrumentation.max_time_to_evaluate_ms` must not be negative, " \
|
|
185
|
+
"but `#{value.inspect}` was provided."
|
|
186
|
+
end
|
|
187
|
+
value
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
172
191
|
# Settings in the 'internal' group are for internal Datadog
|
|
173
192
|
# use only, and are needed to test dynamic instrumentation or
|
|
174
193
|
# experiment with features not released to customers.
|
|
@@ -9,7 +9,7 @@ Datadog::DI::Serializer.register(
|
|
|
9
9
|
# An alternative could have been to make DI specifically provide lookup
|
|
10
10
|
# logic for "instances of classes derived from X", but a condition Proc
|
|
11
11
|
# is more universal.
|
|
12
|
-
condition: lambda { |value| ActiveRecord::Base === value }
|
|
12
|
+
condition: lambda { |value| ActiveRecord::Base === value },
|
|
13
13
|
) do |serializer, value, name:, depth:, deadline:|
|
|
14
14
|
# +serializer+ is an instance of DI::Serializer.
|
|
15
15
|
# Use it to perform the serialization to primitive values.
|
|
@@ -133,7 +133,7 @@ module Datadog
|
|
|
133
133
|
raise DI::Error::BadVariableName, "Bad variable name: #{target}"
|
|
134
134
|
end
|
|
135
135
|
method_name = (($1 == "@") ? "iref" : "ref")
|
|
136
|
-
"#{method_name}('#{target}')"
|
|
136
|
+
"#{method_name}(context, '#{target}')"
|
|
137
137
|
end
|
|
138
138
|
when *SINGLE_ARG_METHODS
|
|
139
139
|
method_name = op.gsub(/[A-Z]/) { |m| "_#{m.downcase}" }
|
|
@@ -146,10 +146,10 @@ module Datadog
|
|
|
146
146
|
if String === second
|
|
147
147
|
# Match against a literal regular expression (string).
|
|
148
148
|
index = precompile_regexp(second, regexps)
|
|
149
|
-
"matches_compiled(#{compile_partial(first, regexps)}, #{index})"
|
|
149
|
+
"matches_compiled(#{compile_partial(first, regexps)}, #{index}, evaluation_deadline)"
|
|
150
150
|
else
|
|
151
151
|
# Match against a (complex) expression.
|
|
152
|
-
"matches(#{compile_partial(first, regexps)}, (#{compile_partial(second, regexps)}))"
|
|
152
|
+
"matches(#{compile_partial(first, regexps)}, (#{compile_partial(second, regexps)}), evaluation_deadline)"
|
|
153
153
|
end
|
|
154
154
|
when *TWO_ARG_METHODS
|
|
155
155
|
method_name = op.gsub(/[A-Z]/) { |m| "_#{m.downcase}" }
|
|
@@ -185,7 +185,7 @@ module Datadog
|
|
|
185
185
|
unless Array === target && target.length == 2
|
|
186
186
|
raise DI::Error::InvalidExpression, "Improper #{op} syntax"
|
|
187
187
|
end
|
|
188
|
-
"#{op}(#{compile_partial(target.first, regexps)}) { |current_item, current_key, current_value| #{compile_partial(target.last, regexps)} }"
|
|
188
|
+
"#{op}(#{compile_partial(target.first, regexps)}, evaluation_deadline) { |current_item, current_key, current_value| #{compile_partial(target.last, regexps)} }"
|
|
189
189
|
else
|
|
190
190
|
raise DI::Error::InvalidExpression, "Unknown operation: #{op}"
|
|
191
191
|
end
|
|
@@ -12,8 +12,29 @@ module Datadog
|
|
|
12
12
|
# @api private
|
|
13
13
|
class Evaluator
|
|
14
14
|
# Maximum wall-clock time allowed for evaluating a single `matches`
|
|
15
|
-
# operator.
|
|
16
|
-
|
|
15
|
+
# operator. Wall clock, while the evaluation budget is thread CPU
|
|
16
|
+
# time: the regexp engine's `timeout:` keyword and Timeout.timeout
|
|
17
|
+
# offer no CPU-time mode.
|
|
18
|
+
MATCHES_TIMEOUT_SECONDS = 0.05
|
|
19
|
+
|
|
20
|
+
MATCHES_TIMEOUT_MESSAGE = "matches operator timeout"
|
|
21
|
+
|
|
22
|
+
# Number of collection items processed between cooperative
|
|
23
|
+
# evaluation-deadline checks; evaluation aborts within one
|
|
24
|
+
# interval's worth of items past the deadline. A `matches`
|
|
25
|
+
# invocation can spend MATCHES_TIMEOUT_SECONDS of wall time, so
|
|
26
|
+
# #matches and #matches_compiled check the deadline before each
|
|
27
|
+
# match; a predicate that runs `matches` per item therefore
|
|
28
|
+
# adds at most the one match in flight when the deadline
|
|
29
|
+
# passes to the overshoot.
|
|
30
|
+
EVALUATION_DEADLINE_CHECK_INTERVAL = 64
|
|
31
|
+
|
|
32
|
+
# Built-in Array#each and Hash#each, resolved once at load and
|
|
33
|
+
# bound per invocation: the collection operators' iteration then
|
|
34
|
+
# dispatches no customer each override during probe evaluation.
|
|
35
|
+
ARRAY_EACH = Array.instance_method(:each)
|
|
36
|
+
HASH_EACH = Hash.instance_method(:each)
|
|
37
|
+
private_constant :ARRAY_EACH, :HASH_EACH
|
|
17
38
|
|
|
18
39
|
# @param regexps [Array<Regexp>] Regexps precompiled from literal
|
|
19
40
|
# `matches` patterns, looked up by index by #matches_compiled.
|
|
@@ -25,12 +46,110 @@ module Datadog
|
|
|
25
46
|
|
|
26
47
|
attr_reader :regexps
|
|
27
48
|
|
|
28
|
-
|
|
29
|
-
|
|
49
|
+
# Raises DI::Error::EvaluationTimeout when the evaluation deadline
|
|
50
|
+
# has been exceeded, aborting evaluation cooperatively.
|
|
51
|
+
#
|
|
52
|
+
# @param evaluation_deadline [Float] invocation-local evaluation
|
|
53
|
+
# deadline, passed from the compiled #evaluate into the operator
|
|
54
|
+
# calls.
|
|
55
|
+
# @return [void]
|
|
56
|
+
# @raise [DI::Error::EvaluationTimeout] the deadline has been exceeded.
|
|
57
|
+
def raise_if_evaluation_deadline_exceeded(evaluation_deadline)
|
|
58
|
+
if self.class.evaluation_deadline_exceeded?(evaluation_deadline)
|
|
59
|
+
raise DI::Error::EvaluationTimeout
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
private :raise_if_evaluation_deadline_exceeded
|
|
63
|
+
|
|
64
|
+
# Raises DI::Error::EvaluationTimeout when +item_index+ falls on
|
|
65
|
+
# a cooperative check boundary and the evaluation deadline has
|
|
66
|
+
# been exceeded. The thread CPU clock is read only at every
|
|
67
|
+
# EVALUATION_DEADLINE_CHECK_INTERVAL-th item, so an already-exceeded
|
|
68
|
+
# deadline raises at the first boundary at or after it passes.
|
|
69
|
+
#
|
|
70
|
+
# @param item_index [Integer] zero-based index of the collection
|
|
71
|
+
# element being processed.
|
|
72
|
+
# @param evaluation_deadline [Float] invocation-local evaluation
|
|
73
|
+
# deadline, passed from the compiled #evaluate into the operator
|
|
74
|
+
# calls.
|
|
75
|
+
# @return [void]
|
|
76
|
+
# @raise [DI::Error::EvaluationTimeout] a check boundary was reached
|
|
77
|
+
# with the deadline exceeded.
|
|
78
|
+
def raise_if_deadline_check_due_and_exceeded(item_index, evaluation_deadline)
|
|
79
|
+
if (item_index % EVALUATION_DEADLINE_CHECK_INTERVAL).zero?
|
|
80
|
+
raise_if_evaluation_deadline_exceeded(evaluation_deadline)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
private :raise_if_deadline_check_due_and_exceeded
|
|
84
|
+
|
|
85
|
+
# Yields the evaluation values for one collection element — the
|
|
86
|
+
# item alone for an Array element, or the [key, value] pair as
|
|
87
|
+
# @it together with the @key and the @value for a Hash element —
|
|
88
|
+
# iterating through the built-in iteration methods bound per
|
|
89
|
+
# invocation and performing the interval-gated evaluation-deadline
|
|
90
|
+
# check and its counter advance between elements.
|
|
91
|
+
#
|
|
92
|
+
# @param collection [Array, Hash] collection whose elements the
|
|
93
|
+
# block receives.
|
|
94
|
+
# @param evaluation_deadline [Float] invocation-local evaluation deadline.
|
|
95
|
+
# @param operator_name [String] name of the invoking collection
|
|
96
|
+
# operator, reported in the bad-collection-type error.
|
|
97
|
+
# @yield [current_item, current_key, current_value] evaluation
|
|
98
|
+
# values for one element.
|
|
99
|
+
# @return [void]
|
|
100
|
+
# @raise [DI::Error::EvaluationTimeout] the evaluation deadline was
|
|
101
|
+
# exceeded mid-iteration.
|
|
102
|
+
# @raise [DI::Error::ExpressionEvaluationError] +collection+ is
|
|
103
|
+
# not an Array or a Hash.
|
|
104
|
+
def each_collection_item(collection, evaluation_deadline, operator_name)
|
|
105
|
+
case collection
|
|
106
|
+
when Array
|
|
107
|
+
i = 0
|
|
108
|
+
ARRAY_EACH.bind(collection).call do |item|
|
|
109
|
+
raise_if_deadline_check_due_and_exceeded(i, evaluation_deadline)
|
|
110
|
+
i += 1
|
|
111
|
+
yield item, nil, nil
|
|
112
|
+
end
|
|
113
|
+
when Hash
|
|
114
|
+
i = 0
|
|
115
|
+
HASH_EACH.bind(collection).call do |key, value|
|
|
116
|
+
raise_if_deadline_check_due_and_exceeded(i, evaluation_deadline)
|
|
117
|
+
i += 1
|
|
118
|
+
yield [key, value], key, value
|
|
119
|
+
end
|
|
120
|
+
else
|
|
121
|
+
raise DI::Error::ExpressionEvaluationError, "Bad collection type for #{operator_name}: #{collection.class}"
|
|
122
|
+
end
|
|
123
|
+
nil
|
|
124
|
+
end
|
|
125
|
+
private :each_collection_item
|
|
126
|
+
|
|
127
|
+
# Returns whether the thread CPU clock has passed +evaluation_deadline+.
|
|
128
|
+
#
|
|
129
|
+
# @param evaluation_deadline [Float] the deadline to check.
|
|
130
|
+
# @return [Boolean] whether the deadline has been exceeded.
|
|
131
|
+
def self.evaluation_deadline_exceeded?(evaluation_deadline)
|
|
132
|
+
Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :float_second) >= evaluation_deadline
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Resolves the value of the local variable or argument +var+ from
|
|
136
|
+
# the evaluation +context+.
|
|
137
|
+
#
|
|
138
|
+
# @param context [Context] context holding the evaluation locals.
|
|
139
|
+
# @param var [Symbol, String] name of the local variable or argument.
|
|
140
|
+
# @return [Object] value of the local variable or argument.
|
|
141
|
+
def ref(context, var)
|
|
142
|
+
context.fetch(var)
|
|
30
143
|
end
|
|
31
144
|
|
|
32
|
-
|
|
33
|
-
|
|
145
|
+
# Resolves the value of the instance variable +var+ from the
|
|
146
|
+
# receiver of the evaluation +context+.
|
|
147
|
+
#
|
|
148
|
+
# @param context [Context] context holding the evaluation receiver.
|
|
149
|
+
# @param var [Symbol, String] name of the instance variable.
|
|
150
|
+
# @return [Object] value of the instance variable.
|
|
151
|
+
def iref(context, var)
|
|
152
|
+
context.fetch_ivar(var)
|
|
34
153
|
end
|
|
35
154
|
|
|
36
155
|
def len(var, var_name)
|
|
@@ -92,10 +211,14 @@ module Datadog
|
|
|
92
211
|
#
|
|
93
212
|
# @param haystack [String] string to match against.
|
|
94
213
|
# @param needle [String] regexp source.
|
|
214
|
+
# @param evaluation_deadline [Float] invocation-local evaluation
|
|
215
|
+
# deadline.
|
|
95
216
|
# @return [Boolean] whether the haystack matches the regexp.
|
|
96
|
-
# @raise [
|
|
97
|
-
#
|
|
98
|
-
|
|
217
|
+
# @raise [DI::Error::EvaluationTimeout] the evaluation deadline was
|
|
218
|
+
# exceeded before the match started, or the match exceeded
|
|
219
|
+
# MATCHES_TIMEOUT_SECONDS.
|
|
220
|
+
def matches(haystack, needle, evaluation_deadline)
|
|
221
|
+
raise_if_evaluation_deadline_exceeded(evaluation_deadline)
|
|
99
222
|
bounded_match?(Evaluator.compile_regexp(needle), haystack)
|
|
100
223
|
end
|
|
101
224
|
|
|
@@ -104,10 +227,14 @@ module Datadog
|
|
|
104
227
|
#
|
|
105
228
|
# @param haystack [String] string to match against.
|
|
106
229
|
# @param index [Integer] position of the precompiled Regexp in +regexps+.
|
|
230
|
+
# @param evaluation_deadline [Float] invocation-local evaluation
|
|
231
|
+
# deadline.
|
|
107
232
|
# @return [Boolean] whether the haystack matches the regexp.
|
|
108
|
-
# @raise [
|
|
109
|
-
#
|
|
110
|
-
|
|
233
|
+
# @raise [DI::Error::EvaluationTimeout] the evaluation deadline was
|
|
234
|
+
# exceeded before the match started, or the match exceeded
|
|
235
|
+
# MATCHES_TIMEOUT_SECONDS.
|
|
236
|
+
def matches_compiled(haystack, index, evaluation_deadline)
|
|
237
|
+
raise_if_evaluation_deadline_exceeded(evaluation_deadline)
|
|
111
238
|
bounded_match?(regexps.fetch(index), haystack)
|
|
112
239
|
end
|
|
113
240
|
|
|
@@ -117,18 +244,32 @@ module Datadog
|
|
|
117
244
|
# @param re [Regexp] regexp to match against.
|
|
118
245
|
# @param haystack [String] string to match against.
|
|
119
246
|
# @return [Boolean] whether the haystack matches the regexp.
|
|
247
|
+
# @raise [DI::Error::EvaluationTimeout] the match exceeded
|
|
248
|
+
# MATCHES_TIMEOUT_SECONDS.
|
|
120
249
|
if Datadog::RubyVersion.is?(">= 3.2")
|
|
121
250
|
def bounded_match?(re, haystack)
|
|
122
251
|
# Uses Regexp#match? rather than =~ so that the
|
|
123
252
|
# thread-local match data ($~, $1, ...) is not mutated as a side
|
|
124
253
|
# effect of DI expression evaluation.
|
|
125
254
|
re.match?(haystack)
|
|
255
|
+
rescue Regexp::TimeoutError
|
|
256
|
+
raise DI::Error::EvaluationTimeout.new(message: MATCHES_TIMEOUT_MESSAGE)
|
|
126
257
|
end
|
|
127
258
|
else
|
|
259
|
+
# Raised by Timeout.timeout in the matching thread. A dedicated
|
|
260
|
+
# class because Timeout.timeout passes a positional message,
|
|
261
|
+
# which EvaluationTimeout does not accept, and because rescuing
|
|
262
|
+
# Timeout::Error would also catch an enclosing customer timeout.
|
|
263
|
+
class MatchTimeoutSignal < StandardError
|
|
264
|
+
end
|
|
265
|
+
private_constant :MatchTimeoutSignal
|
|
266
|
+
|
|
128
267
|
def bounded_match?(re, haystack)
|
|
129
|
-
Timeout.timeout(MATCHES_TIMEOUT_SECONDS) do
|
|
268
|
+
Timeout.timeout(MATCHES_TIMEOUT_SECONDS, MatchTimeoutSignal) do
|
|
130
269
|
re.match?(haystack)
|
|
131
270
|
end
|
|
271
|
+
rescue MatchTimeoutSignal
|
|
272
|
+
raise DI::Error::EvaluationTimeout.new(message: MATCHES_TIMEOUT_MESSAGE)
|
|
132
273
|
end
|
|
133
274
|
end
|
|
134
275
|
private :bounded_match?
|
|
@@ -171,50 +312,113 @@ module Datadog
|
|
|
171
312
|
String === haystack && haystack.end_with?(needle)
|
|
172
313
|
end
|
|
173
314
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
315
|
+
# Returns true when +block+ returns a truthy value for every element
|
|
316
|
+
# of +collection+.
|
|
317
|
+
#
|
|
318
|
+
# @param collection [Array, Hash] collection to test.
|
|
319
|
+
# @param evaluation_deadline [Float] invocation-local evaluation deadline.
|
|
320
|
+
# @return [Boolean] whether the block is satisfied by every element.
|
|
321
|
+
# @raise [DI::Error::EvaluationTimeout] the evaluation deadline was
|
|
322
|
+
# exceeded mid-iteration.
|
|
323
|
+
# @raise [DI::Error::ExpressionEvaluationError] +collection+ is not
|
|
324
|
+
# an Array or a Hash.
|
|
325
|
+
def all(collection, evaluation_deadline, &block)
|
|
326
|
+
result = true
|
|
327
|
+
each_collection_item(collection, evaluation_deadline, "all") do |current_item, current_key, current_value|
|
|
328
|
+
unless block.call(current_item, current_key, current_value)
|
|
329
|
+
result = false
|
|
330
|
+
break
|
|
185
331
|
end
|
|
186
|
-
else
|
|
187
|
-
raise DI::Error::ExpressionEvaluationError, "Bad collection type for all: #{collection.class}"
|
|
188
332
|
end
|
|
333
|
+
result
|
|
189
334
|
end
|
|
190
335
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
336
|
+
# Returns true when +block+ returns a truthy value for any element
|
|
337
|
+
# of +collection+.
|
|
338
|
+
#
|
|
339
|
+
# @param collection [Array, Hash] collection to test.
|
|
340
|
+
# @param evaluation_deadline [Float] invocation-local evaluation deadline.
|
|
341
|
+
# @return [Boolean] whether the block is satisfied by any element.
|
|
342
|
+
# @raise [DI::Error::EvaluationTimeout] the evaluation deadline was
|
|
343
|
+
# exceeded mid-iteration.
|
|
344
|
+
# @raise [DI::Error::ExpressionEvaluationError] +collection+ is not
|
|
345
|
+
# an Array or a Hash.
|
|
346
|
+
def any(collection, evaluation_deadline, &block)
|
|
347
|
+
result = false
|
|
348
|
+
each_collection_item(collection, evaluation_deadline, "any") do |current_item, current_key, current_value|
|
|
349
|
+
if block.call(current_item, current_key, current_value)
|
|
350
|
+
result = true
|
|
351
|
+
break
|
|
196
352
|
end
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
353
|
+
end
|
|
354
|
+
result
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
# Returns the key-value pairs of the Hash +collection+ for which
|
|
358
|
+
# +block+ returns a truthy value.
|
|
359
|
+
#
|
|
360
|
+
# Ruby 3.1 changed Hash#select to reuse the stored key hashes
|
|
361
|
+
# instead of invoking the keys' hash methods and to keep a
|
|
362
|
+
# compare_by_identity hash's distinct-but-eql? keys and identity
|
|
363
|
+
# flag, so this Ruby filters through the built-in Hash#select
|
|
364
|
+
# bound per invocation. Earlier Rubies invoke the keys' hash
|
|
365
|
+
# methods and collapse identity hashes, so they keep the manual
|
|
366
|
+
# iteration through #each_collection_item, preserving the source
|
|
367
|
+
# hash's identity mode in the result.
|
|
368
|
+
if Datadog::RubyVersion.is?(">= 3.1")
|
|
369
|
+
# Built-in Hash#select, resolved once at load and bound per
|
|
370
|
+
# invocation: #filter's Hash iteration then dispatches no
|
|
371
|
+
# customer select override during probe evaluation.
|
|
372
|
+
HASH_SELECT = Hash.instance_method(:select)
|
|
373
|
+
private_constant :HASH_SELECT
|
|
374
|
+
|
|
375
|
+
def filter_hash(collection, evaluation_deadline, &block)
|
|
376
|
+
i = 0
|
|
377
|
+
HASH_SELECT.bind(collection).call do |key, value|
|
|
378
|
+
raise_if_deadline_check_due_and_exceeded(i, evaluation_deadline)
|
|
379
|
+
i += 1
|
|
201
380
|
block.call([key, value], key, value)
|
|
381
|
+
end.to_h
|
|
382
|
+
end
|
|
383
|
+
else
|
|
384
|
+
# Core identity-mode check, resolved once at load and bound per
|
|
385
|
+
# invocation so that #filter dispatches no customer
|
|
386
|
+
# compare_by_identity? override during probe evaluation.
|
|
387
|
+
IDENTITY_MODE_CHECK = Hash.instance_method(:compare_by_identity?)
|
|
388
|
+
private_constant :IDENTITY_MODE_CHECK
|
|
389
|
+
|
|
390
|
+
def filter_hash(collection, evaluation_deadline, &block)
|
|
391
|
+
result = {}
|
|
392
|
+
result.compare_by_identity if IDENTITY_MODE_CHECK.bind(collection).call
|
|
393
|
+
each_collection_item(collection, evaluation_deadline, "filter") do |_current_item, current_key, current_value|
|
|
394
|
+
result[current_key] = current_value if block.call([current_key, current_value], current_key, current_value)
|
|
202
395
|
end
|
|
203
|
-
|
|
204
|
-
raise DI::Error::ExpressionEvaluationError, "Bad collection type for any: #{collection.class}"
|
|
396
|
+
result
|
|
205
397
|
end
|
|
206
398
|
end
|
|
399
|
+
private :filter_hash
|
|
207
400
|
|
|
208
|
-
|
|
401
|
+
# Returns the elements of +collection+ for which +block+ returns a
|
|
402
|
+
# truthy value: the selected items of an Array collection, and a
|
|
403
|
+
# Hash of the selected key-value pairs of a Hash collection.
|
|
404
|
+
#
|
|
405
|
+
# @param collection [Array, Hash] collection to filter.
|
|
406
|
+
# @param evaluation_deadline [Float] invocation-local evaluation deadline.
|
|
407
|
+
# @return [Array, Hash] the selected elements.
|
|
408
|
+
# @raise [DI::Error::EvaluationTimeout] the evaluation deadline was
|
|
409
|
+
# exceeded mid-iteration.
|
|
410
|
+
# @raise [DI::Error::ExpressionEvaluationError] +collection+ is not
|
|
411
|
+
# an Array or a Hash.
|
|
412
|
+
def filter(collection, evaluation_deadline, &block)
|
|
209
413
|
case collection
|
|
210
414
|
when Array
|
|
211
|
-
|
|
212
|
-
|
|
415
|
+
result = []
|
|
416
|
+
each_collection_item(collection, evaluation_deadline, "filter") do |current_item, current_key, current_value|
|
|
417
|
+
result << current_item if block.call(current_item, current_key, current_value)
|
|
213
418
|
end
|
|
419
|
+
result
|
|
214
420
|
when Hash
|
|
215
|
-
collection
|
|
216
|
-
block.call([key, value], key, value)
|
|
217
|
-
end.to_h
|
|
421
|
+
filter_hash(collection, evaluation_deadline, &block)
|
|
218
422
|
else
|
|
219
423
|
raise DI::Error::ExpressionEvaluationError, "Bad collection type for filter: #{collection.class}"
|
|
220
424
|
end
|