datadog 2.33.0 → 2.35.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 +99 -1
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +20 -0
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +2 -15
- data/ext/datadog_profiling_native_extension/macos_sampler_thread.h +55 -0
- data/ext/datadog_profiling_native_extension/stack_recorder.c +6 -11
- data/lib/datadog/ai_guard/configuration.rb +1 -0
- data/lib/datadog/ai_guard/contrib/rack/request_middleware.rb +53 -39
- data/lib/datadog/ai_guard/evaluation.rb +6 -1
- data/lib/datadog/ai_guard/ext.rb +12 -1
- data/lib/datadog/appsec/api_security/route_extractor.rb +3 -0
- data/lib/datadog/appsec/component.rb +4 -1
- data/lib/datadog/appsec/compressed_json.rb +2 -2
- data/lib/datadog/appsec/contrib/aws_lambda/waf_addresses.rb +3 -3
- data/lib/datadog/appsec/contrib/rack/ext.rb +1 -1
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +9 -40
- data/lib/datadog/appsec/default_header_tags.rb +48 -0
- data/lib/datadog/core/configuration/components.rb +8 -1
- data/lib/datadog/core/configuration/settings.rb +16 -7
- data/lib/datadog/core/configuration/supported_configurations.rb +10 -0
- data/lib/datadog/core/environment/ext.rb +4 -0
- data/lib/datadog/core/environment/identity.rb +15 -1
- data/lib/datadog/core/environment/process.rb +50 -27
- data/lib/datadog/core/remote/client/capabilities.rb +11 -2
- data/lib/datadog/core/remote/transport/http/config.rb +5 -5
- data/lib/datadog/core/telemetry/request.rb +0 -2
- data/lib/datadog/core/transport/response.rb +1 -1
- data/lib/datadog/core/utils/{base64.rb → base64_codec.rb} +3 -2
- data/lib/datadog/core/utils/{array.rb → enumerable_compat.rb} +2 -2
- data/lib/datadog/core/utils/hash.rb +0 -23
- data/lib/datadog/core/utils/spawn_monkey_patch.rb +46 -16
- data/lib/datadog/data_streams/pathway_context.rb +3 -3
- data/lib/datadog/di/code_tracker.rb +43 -22
- data/lib/datadog/di/contrib/active_record.rb +6 -2
- data/lib/datadog/di/instrumenter.rb +24 -4
- data/lib/datadog/di/probe_notification_builder.rb +1 -1
- data/lib/datadog/di/remote.rb +4 -4
- data/lib/datadog/di/serializer.rb +5 -5
- data/lib/datadog/di/utils.rb +42 -14
- data/lib/datadog/opentelemetry/configuration/settings.rb +65 -0
- data/lib/datadog/opentelemetry/ext.rb +9 -0
- data/lib/datadog/opentelemetry/logs.rb +98 -0
- data/lib/datadog/opentelemetry/metrics.rb +10 -46
- data/lib/datadog/opentelemetry/sdk/configurator.rb +40 -0
- data/lib/datadog/opentelemetry/sdk/logs_exporter.rb +37 -0
- data/lib/datadog/opentelemetry/signal_configuration.rb +53 -0
- data/lib/datadog/opentelemetry.rb +1 -0
- data/lib/datadog/profiling/collectors/thread_context.rb +0 -4
- data/lib/datadog/profiling/component.rb +3 -11
- data/lib/datadog/profiling/ext/dir_monkey_patches.rb +3 -2
- data/lib/datadog/profiling/stack_recorder.rb +0 -4
- data/lib/datadog/symbol_database/component.rb +409 -0
- data/lib/datadog/symbol_database/configuration.rb +2 -2
- data/lib/datadog/symbol_database/extractor.rb +32 -4
- data/lib/datadog/symbol_database/remote.rb +175 -0
- data/lib/datadog/symbol_database/scope_batcher.rb +8 -0
- data/lib/datadog/symbol_database/service_version.rb +11 -2
- data/lib/datadog/symbol_database/symbol.rb +6 -3
- data/lib/datadog/symbol_database/uploader.rb +62 -8
- data/lib/datadog/tracing/contrib/action_cable/events/broadcast.rb +4 -1
- data/lib/datadog/tracing/contrib/action_cable/events/perform_action.rb +4 -1
- data/lib/datadog/tracing/contrib/action_cable/events/transmit.rb +4 -1
- data/lib/datadog/tracing/contrib/action_cable/instrumentation.rb +4 -1
- data/lib/datadog/tracing/contrib/action_mailer/event.rb +4 -1
- data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +1 -0
- data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +8 -0
- data/lib/datadog/tracing/contrib/action_view/events/render_partial.rb +4 -1
- data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +4 -1
- data/lib/datadog/tracing/contrib/active_job/events/discard.rb +4 -1
- data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +4 -1
- data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +4 -1
- data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +4 -1
- data/lib/datadog/tracing/contrib/active_job/events/perform.rb +4 -1
- data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +4 -1
- data/lib/datadog/tracing/contrib/active_record/events/sql.rb +1 -4
- data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +1 -4
- data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +1 -4
- data/lib/datadog/tracing/contrib/aws/instrumentation.rb +1 -5
- data/lib/datadog/tracing/contrib/dalli/instrumentation.rb +1 -5
- data/lib/datadog/tracing/contrib/delayed_job/plugin.rb +2 -0
- data/lib/datadog/tracing/contrib/delayed_job/server_internal_tracer/worker.rb +1 -0
- data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +1 -5
- data/lib/datadog/tracing/contrib/elasticsearch/quantize.rb +2 -2
- data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +1 -5
- data/lib/datadog/tracing/contrib/ethon/multi_patch.rb +1 -8
- data/lib/datadog/tracing/contrib/excon/middleware.rb +1 -5
- data/lib/datadog/tracing/contrib/ext.rb +3 -1
- data/lib/datadog/tracing/contrib/faraday/middleware.rb +1 -5
- data/lib/datadog/tracing/contrib/grape/endpoint.rb +3 -0
- data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +1 -0
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +1 -5
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +1 -5
- data/lib/datadog/tracing/contrib/hanami/action_tracer.rb +1 -0
- data/lib/datadog/tracing/contrib/hanami/renderer_policy_tracing.rb +1 -0
- data/lib/datadog/tracing/contrib/hanami/router_tracing.rb +1 -0
- data/lib/datadog/tracing/contrib/http/instrumentation.rb +1 -5
- data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +1 -5
- data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +1 -5
- data/lib/datadog/tracing/contrib/kafka/event.rb +1 -0
- data/lib/datadog/tracing/contrib/mongodb/parsers.rb +5 -5
- data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +2 -5
- data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +1 -5
- data/lib/datadog/tracing/contrib/opensearch/patcher.rb +1 -5
- data/lib/datadog/tracing/contrib/opensearch/quantize.rb +2 -2
- data/lib/datadog/tracing/contrib/pg/instrumentation.rb +1 -5
- data/lib/datadog/tracing/contrib/presto/instrumentation.rb +3 -5
- data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +3 -0
- data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +2 -2
- data/lib/datadog/tracing/contrib/que/tracer.rb +1 -0
- data/lib/datadog/tracing/contrib/racecar/event.rb +1 -5
- data/lib/datadog/tracing/contrib/rack/header_tagging.rb +23 -0
- data/lib/datadog/tracing/contrib/rack/middlewares.rb +1 -0
- data/lib/datadog/tracing/contrib/rack/trace_proxy_middleware.rb +2 -0
- data/lib/datadog/tracing/contrib/rails/runner.rb +2 -0
- data/lib/datadog/tracing/contrib/rake/instrumentation.rb +4 -2
- data/lib/datadog/tracing/contrib/redis/tags.rb +0 -5
- data/lib/datadog/tracing/contrib/redis/trace_middleware.rb +2 -0
- data/lib/datadog/tracing/contrib/resque/resque_job.rb +1 -0
- data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +1 -5
- data/lib/datadog/tracing/contrib/roda/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/roda/instrumentation.rb +4 -1
- data/lib/datadog/tracing/contrib/sequel/database.rb +1 -0
- data/lib/datadog/tracing/contrib/sequel/dataset.rb +1 -0
- data/lib/datadog/tracing/contrib/sequel/utils.rb +0 -5
- data/lib/datadog/tracing/contrib/shoryuken/tracer.rb +1 -0
- data/lib/datadog/tracing/contrib/sidekiq/client_tracer.rb +1 -0
- data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/heartbeat.rb +2 -0
- data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/job_fetch.rb +1 -0
- data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/redis_info.rb +1 -0
- data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/scheduled_poller.rb +2 -0
- data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/stop.rb +1 -0
- data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +3 -2
- data/lib/datadog/tracing/contrib/sinatra/tracer.rb +1 -0
- data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +1 -0
- data/lib/datadog/tracing/contrib/sneakers/tracer.rb +1 -0
- data/lib/datadog/tracing/contrib/sucker_punch/instrumentation.rb +1 -0
- data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +1 -5
- data/lib/datadog/tracing/contrib/utils/quantization/{hash.rb → hash_formatter.rb} +1 -1
- data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +0 -13
- data/lib/datadog/tracing/distributed/trace_context.rb +0 -28
- data/lib/datadog/tracing/metadata/ext.rb +10 -0
- data/lib/datadog/tracing/span_operation.rb +13 -0
- data/lib/datadog/tracing/trace_operation.rb +22 -0
- data/lib/datadog/tracing/tracer.rb +9 -0
- data/lib/datadog/tracing/transport/traces.rb +2 -2
- data/lib/datadog/version.rb +1 -1
- metadata +16 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4029cb3d97dee5a685f9f34027d3f46c960d452eb82339f8a167227f597881aa
|
|
4
|
+
data.tar.gz: 3a108d7cb90cb00326d2f9ec563e0370855161ea21c270e31cfe8bbd16b9e154
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c10991420b5026c39d0ca76529be13c276479236ebf01ab8e5050c110f687266620bfa04127f76cdb55c87b54e84c3cee5ffa39f1de46492f2dd90a58f8ed484
|
|
7
|
+
data.tar.gz: f7508958b880c20197ab687ff2eb98c29af51aadbc1835f8ae88a88b8858a520518aefb0d73797ae0728018fe871056c4fa4baccc86aa77b9ec0e1d20a8df298
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,73 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [2.35.0] - 2026-06-03
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
* Tracing: Add `dynamic_service` SQL comment propagation mode for Database Monitoring ([#5812][])
|
|
10
|
+
* Tracing: Prevent Datadog-generated traffic from interfering with application metrics ([#5811][])
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
* AppSec: Improve route extraction performance for Rails applications ([#5836][])
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
* Tracing: Restore `Datadog::Tracing::Contrib::Ext::Metadata::TAG_BASE_SERVICE` constant removed in v2.34.0 ([#5830][])
|
|
19
|
+
|
|
20
|
+
### Removed
|
|
21
|
+
|
|
22
|
+
* Profiling: Deprecate the `profiling.advanced.timeline_enabled` setting for removal; it no longer does anything. Please remove it from `Datadog.configure` and do not set `DD_PROFILING_TIMELINE_ENABLED` ([#5750][])
|
|
23
|
+
|
|
24
|
+
## [2.34.0] - 2026-05-27
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
* Dynamic Instrumentation: Enable opt-in Symbol Database upload so Ruby services populate Live Debugger UI autocomplete when creating probes ([#5717][])
|
|
29
|
+
* Open Telemetry: Add OpenTelemetry logs support with OTLP export. Enable using `DD_LOGS_OTEL_ENABLED=true`; supports standard `OTEL_EXPORTER_OTLP_*` settings ([#5446][])
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
* Core: Fix Remote configuration `TransportError` messages removing wrapper response object memory addresses ([#5762][])
|
|
34
|
+
* Core: Fix `TypeError` from `Process.spawn` when passing an environment Hash ([#5773][], [#5634][])
|
|
35
|
+
* AppSec: Prevent host application crashes when AppSec fails to initialize ([#5768][])
|
|
36
|
+
* Dynamic Instrumentation: Fix off-by-one in `max_capture_depth` so snapshots respect the configured nesting limit exactly ([#5753][])
|
|
37
|
+
* Dynamic Instrumentation: Improve line probes to match `sourceFile` case-insensitively and support Windows-style backslashes for correct installation ([#5754][])
|
|
38
|
+
|
|
39
|
+
## [2.33.0] - 2026-05-13
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
|
|
43
|
+
* AI Guard: Ensure client IP is always collected when AI Guard is enabled ([#5677][])
|
|
44
|
+
* AppSec: Add support for AWS Lambda ([#5663][])
|
|
45
|
+
* Tracing: Add inferred proxy spans for services behind AWS Gateway ([#5681][])
|
|
46
|
+
* Open Telemetry: Add support for `DD_HOSTNAME` to set trace hostname and OTel `host.name` when `DD_TRACE_REPORT_HOSTNAME` is enabled ([#5705][])
|
|
47
|
+
|
|
48
|
+
### Changed
|
|
49
|
+
|
|
50
|
+
* Core: Collect all threads in crash reports to provide full thread context during crashes ([#5724][])
|
|
51
|
+
* Core: Update `libdatadog` dependency to version 33.0.0 ([#5723][])
|
|
52
|
+
|
|
53
|
+
## [2.32.0] - 2026-05-08
|
|
54
|
+
|
|
55
|
+
### Added
|
|
56
|
+
|
|
57
|
+
* Open Feature: Add flag evaluation metrics (`feature_flag.evaluations`) via OpenTelemetry for OpenFeature provider. ([#5599][])
|
|
58
|
+
* Profiling: Add metric for total time spent waiting for the GVL. ([#5569][])
|
|
59
|
+
* Dynamic Instrumentation: Live Debugger line probes can now target third-party and Ruby standard library code loaded before datadog gem is loaded. ([#5501][])
|
|
60
|
+
|
|
61
|
+
### Changed
|
|
62
|
+
|
|
63
|
+
* Profiling: Print failure info when native extension setup fails ([#5657][])
|
|
64
|
+
* Tracing: Add parsing limits to `tracestate` and `traceparent` propagation headers and remove whitespace around list members. ([#5674][])
|
|
65
|
+
* Tracing: Limit extracted [baggage](https://www.w3.org/TR/2024/CR-baggage-20240530/) header parsing to 64 items and 8192 bytes. ([#5672][])
|
|
66
|
+
|
|
67
|
+
### Fixed
|
|
68
|
+
|
|
69
|
+
* Tracing: Enforce `x-datadog-tags` propagation header size limits by byte size. ([#5687][])
|
|
70
|
+
* Tracing: Return empty baggage on invalid UTF-8 baggage extraction ([#5689][])
|
|
71
|
+
|
|
5
72
|
## [2.31.0] - 2026-04-20
|
|
6
73
|
|
|
7
74
|
### Added
|
|
@@ -3567,7 +3634,11 @@ Release notes: https://github.com/DataDog/dd-trace-rb/releases/tag/v0.3.1
|
|
|
3567
3634
|
Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
|
|
3568
3635
|
|
|
3569
3636
|
|
|
3570
|
-
[Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v2.
|
|
3637
|
+
[Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v2.35.0...master
|
|
3638
|
+
[2.35.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.34.0...v2.35.0
|
|
3639
|
+
[2.34.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.33.0...v2.34.0
|
|
3640
|
+
[2.33.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.32.0...v2.33.0
|
|
3641
|
+
[2.32.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.31.0...v2.32.0
|
|
3571
3642
|
[2.31.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.30.0...v2.31.0
|
|
3572
3643
|
[2.30.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.29.0...v2.30.0
|
|
3573
3644
|
[2.29.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.28.0...v2.29.0
|
|
@@ -5278,6 +5349,7 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
|
|
|
5278
5349
|
[#5434]: https://github.com/DataDog/dd-trace-rb/issues/5434
|
|
5279
5350
|
[#5435]: https://github.com/DataDog/dd-trace-rb/issues/5435
|
|
5280
5351
|
[#5436]: https://github.com/DataDog/dd-trace-rb/issues/5436
|
|
5352
|
+
[#5446]: https://github.com/DataDog/dd-trace-rb/issues/5446
|
|
5281
5353
|
[#5448]: https://github.com/DataDog/dd-trace-rb/issues/5448
|
|
5282
5354
|
[#5449]: https://github.com/DataDog/dd-trace-rb/issues/5449
|
|
5283
5355
|
[#5461]: https://github.com/DataDog/dd-trace-rb/issues/5461
|
|
@@ -5286,15 +5358,41 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
|
|
|
5286
5358
|
[#5483]: https://github.com/DataDog/dd-trace-rb/issues/5483
|
|
5287
5359
|
[#5484]: https://github.com/DataDog/dd-trace-rb/issues/5484
|
|
5288
5360
|
[#5499]: https://github.com/DataDog/dd-trace-rb/issues/5499
|
|
5361
|
+
[#5501]: https://github.com/DataDog/dd-trace-rb/issues/5501
|
|
5289
5362
|
[#5509]: https://github.com/DataDog/dd-trace-rb/issues/5509
|
|
5290
5363
|
[#5531]: https://github.com/DataDog/dd-trace-rb/issues/5531
|
|
5291
5364
|
[#5564]: https://github.com/DataDog/dd-trace-rb/issues/5564
|
|
5365
|
+
[#5569]: https://github.com/DataDog/dd-trace-rb/issues/5569
|
|
5292
5366
|
[#5573]: https://github.com/DataDog/dd-trace-rb/issues/5573
|
|
5293
5367
|
[#5574]: https://github.com/DataDog/dd-trace-rb/issues/5574
|
|
5294
5368
|
[#5579]: https://github.com/DataDog/dd-trace-rb/issues/5579
|
|
5295
5369
|
[#5580]: https://github.com/DataDog/dd-trace-rb/issues/5580
|
|
5296
5370
|
[#5587]: https://github.com/DataDog/dd-trace-rb/issues/5587
|
|
5297
5371
|
[#5594]: https://github.com/DataDog/dd-trace-rb/issues/5594
|
|
5372
|
+
[#5599]: https://github.com/DataDog/dd-trace-rb/issues/5599
|
|
5373
|
+
[#5634]: https://github.com/DataDog/dd-trace-rb/issues/5634
|
|
5374
|
+
[#5657]: https://github.com/DataDog/dd-trace-rb/issues/5657
|
|
5375
|
+
[#5663]: https://github.com/DataDog/dd-trace-rb/issues/5663
|
|
5376
|
+
[#5672]: https://github.com/DataDog/dd-trace-rb/issues/5672
|
|
5377
|
+
[#5674]: https://github.com/DataDog/dd-trace-rb/issues/5674
|
|
5378
|
+
[#5677]: https://github.com/DataDog/dd-trace-rb/issues/5677
|
|
5379
|
+
[#5681]: https://github.com/DataDog/dd-trace-rb/issues/5681
|
|
5380
|
+
[#5687]: https://github.com/DataDog/dd-trace-rb/issues/5687
|
|
5381
|
+
[#5689]: https://github.com/DataDog/dd-trace-rb/issues/5689
|
|
5382
|
+
[#5705]: https://github.com/DataDog/dd-trace-rb/issues/5705
|
|
5383
|
+
[#5717]: https://github.com/DataDog/dd-trace-rb/issues/5717
|
|
5384
|
+
[#5723]: https://github.com/DataDog/dd-trace-rb/issues/5723
|
|
5385
|
+
[#5724]: https://github.com/DataDog/dd-trace-rb/issues/5724
|
|
5386
|
+
[#5750]: https://github.com/DataDog/dd-trace-rb/issues/5750
|
|
5387
|
+
[#5753]: https://github.com/DataDog/dd-trace-rb/issues/5753
|
|
5388
|
+
[#5754]: https://github.com/DataDog/dd-trace-rb/issues/5754
|
|
5389
|
+
[#5762]: https://github.com/DataDog/dd-trace-rb/issues/5762
|
|
5390
|
+
[#5768]: https://github.com/DataDog/dd-trace-rb/issues/5768
|
|
5391
|
+
[#5773]: https://github.com/DataDog/dd-trace-rb/issues/5773
|
|
5392
|
+
[#5811]: https://github.com/DataDog/dd-trace-rb/issues/5811
|
|
5393
|
+
[#5812]: https://github.com/DataDog/dd-trace-rb/issues/5812
|
|
5394
|
+
[#5830]: https://github.com/DataDog/dd-trace-rb/issues/5830
|
|
5395
|
+
[#5836]: https://github.com/DataDog/dd-trace-rb/issues/5836
|
|
5298
5396
|
[@AdrianLC]: https://github.com/AdrianLC
|
|
5299
5397
|
[@Azure7111]: https://github.com/Azure7111
|
|
5300
5398
|
[@BabyGroot]: https://github.com/BabyGroot
|
|
@@ -17,6 +17,10 @@
|
|
|
17
17
|
#include "setup_signal_handler.h"
|
|
18
18
|
#include "time_helpers.h"
|
|
19
19
|
|
|
20
|
+
#ifdef __APPLE__
|
|
21
|
+
#include "macos_sampler_thread.h"
|
|
22
|
+
#endif
|
|
23
|
+
|
|
20
24
|
// Used to trigger the execution of Collectors::ThreadContext, which implements all of the sampling logic
|
|
21
25
|
// itself; this class only implements the "when to do it" part.
|
|
22
26
|
//
|
|
@@ -535,6 +539,14 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
|
|
|
535
539
|
|
|
536
540
|
block_sigprof_signal_handler_from_running_in_current_thread(); // We want to interrupt the thread with the global VM lock, never this one
|
|
537
541
|
|
|
542
|
+
#ifdef __APPLE__
|
|
543
|
+
// Promote the native thread that will run the sampling loop to a realtime scheduling policy so
|
|
544
|
+
// its periodic wake-ups are not subject to the default ~1-2ms timeshare wake latency. The matching
|
|
545
|
+
// demote happens below in this same function, alongside the SIGPROF unblock dance, so both per-thread
|
|
546
|
+
// policy changes are reverted before Ruby reuses this native thread for an unrelated Ruby thread.
|
|
547
|
+
promote_sampler_thread_to_realtime(MILLIS_AS_NS(state->cpu_sampling_interval_ms));
|
|
548
|
+
#endif
|
|
549
|
+
|
|
538
550
|
// Release GVL, get to the actual work!
|
|
539
551
|
int exception_state;
|
|
540
552
|
rb_protect(release_gvl_and_run_sampling_trigger_loop, instance, &exception_state);
|
|
@@ -556,6 +568,14 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
|
|
|
556
568
|
// had SIGPROF delivery blocked. :hide_the_pain_harold:
|
|
557
569
|
unblock_sigprof_signal_handler_from_running_in_current_thread();
|
|
558
570
|
|
|
571
|
+
#ifdef __APPLE__
|
|
572
|
+
// Pairs with promote_sampler_thread_to_realtime above. Same reasoning as the SIGPROF unblock:
|
|
573
|
+
// Ruby may reuse this native thread for an unrelated Ruby thread that would otherwise inherit
|
|
574
|
+
// our scheduler policy. Doing it here also covers the exception path, since grab_gvl_and_sample
|
|
575
|
+
// can raise and unwind out of the sampling loop.
|
|
576
|
+
demote_sampler_thread_from_realtime();
|
|
577
|
+
#endif
|
|
578
|
+
|
|
559
579
|
// Why replace and not use remove the signal handler? We do this because when a process receives a SIGPROF without
|
|
560
580
|
// having an explicit signal handler set up, the process will instantly terminate with a confusing
|
|
561
581
|
// "Profiling timer expired" message left behind. (This message doesn't come from us -- it's the default message for
|
|
@@ -142,8 +142,6 @@ typedef struct {
|
|
|
142
142
|
VALUE thread_list_buffer;
|
|
143
143
|
// Used to omit endpoint names (retrieved from tracer) from collected data
|
|
144
144
|
bool endpoint_collection_enabled;
|
|
145
|
-
// Used to omit timestamps / timeline events from collected data
|
|
146
|
-
bool timeline_enabled;
|
|
147
145
|
// Used to control context collection
|
|
148
146
|
otel_context_enabled otel_context_enabled;
|
|
149
147
|
// Used to remember where otel context is being stored after we observe it the first time
|
|
@@ -458,7 +456,6 @@ static VALUE _native_new(VALUE klass) {
|
|
|
458
456
|
VALUE thread_list_buffer = rb_ary_new();
|
|
459
457
|
state->thread_list_buffer = thread_list_buffer;
|
|
460
458
|
state->endpoint_collection_enabled = true;
|
|
461
|
-
state->timeline_enabled = true;
|
|
462
459
|
state->native_filenames_enabled = false;
|
|
463
460
|
state->native_filenames_cache = st_init_numtable();
|
|
464
461
|
state->otel_context_enabled = OTEL_CONTEXT_ENABLED_FALSE;
|
|
@@ -492,14 +489,12 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
|
492
489
|
VALUE max_frames = rb_hash_fetch(options, ID2SYM(rb_intern("max_frames")));
|
|
493
490
|
VALUE tracer_context_key = rb_hash_fetch(options, ID2SYM(rb_intern("tracer_context_key")));
|
|
494
491
|
VALUE endpoint_collection_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("endpoint_collection_enabled")));
|
|
495
|
-
VALUE timeline_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("timeline_enabled")));
|
|
496
492
|
VALUE waiting_for_gvl_threshold_ns = rb_hash_fetch(options, ID2SYM(rb_intern("waiting_for_gvl_threshold_ns")));
|
|
497
493
|
VALUE otel_context_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("otel_context_enabled")));
|
|
498
494
|
VALUE native_filenames_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("native_filenames_enabled")));
|
|
499
495
|
|
|
500
496
|
ENFORCE_TYPE(max_frames, T_FIXNUM);
|
|
501
497
|
ENFORCE_BOOLEAN(endpoint_collection_enabled);
|
|
502
|
-
ENFORCE_BOOLEAN(timeline_enabled);
|
|
503
498
|
ENFORCE_TYPE(waiting_for_gvl_threshold_ns, T_FIXNUM);
|
|
504
499
|
ENFORCE_BOOLEAN(native_filenames_enabled);
|
|
505
500
|
|
|
@@ -512,7 +507,6 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
|
512
507
|
// hash_map_per_thread_context is already initialized, nothing to do here
|
|
513
508
|
state->recorder_instance = enforce_recorder_instance(recorder_instance);
|
|
514
509
|
state->endpoint_collection_enabled = (endpoint_collection_enabled == Qtrue);
|
|
515
|
-
state->timeline_enabled = (timeline_enabled == Qtrue);
|
|
516
510
|
state->native_filenames_enabled = (native_filenames_enabled == Qtrue);
|
|
517
511
|
if (otel_context_enabled == Qfalse || otel_context_enabled == Qnil) {
|
|
518
512
|
state->otel_context_enabled = OTEL_CONTEXT_ENABLED_FALSE;
|
|
@@ -844,11 +838,7 @@ VALUE thread_context_collector_sample_after_gc(VALUE self_instance) {
|
|
|
844
838
|
ddog_prof_Slice_Label slice_labels = {.ptr = labels, .len = label_pos};
|
|
845
839
|
|
|
846
840
|
// The end_timestamp_ns is treated specially by libdatadog and that's why it's not added as a ddog_prof_Label
|
|
847
|
-
int64_t end_timestamp_ns =
|
|
848
|
-
|
|
849
|
-
if (state->timeline_enabled) {
|
|
850
|
-
end_timestamp_ns = monotonic_to_system_epoch_ns(&state->time_converter_state, state->gc_tracking.wall_time_at_previous_gc_ns);
|
|
851
|
-
}
|
|
841
|
+
int64_t end_timestamp_ns = monotonic_to_system_epoch_ns(&state->time_converter_state, state->gc_tracking.wall_time_at_previous_gc_ns);
|
|
852
842
|
|
|
853
843
|
record_placeholder_stack(
|
|
854
844
|
state->recorder_instance,
|
|
@@ -1008,7 +998,7 @@ static void trigger_sample_for_thread(
|
|
|
1008
998
|
|
|
1009
999
|
// The end_timestamp_ns is treated specially by libdatadog and that's why it's not added as a ddog_prof_Label
|
|
1010
1000
|
int64_t end_timestamp_ns = 0;
|
|
1011
|
-
if (
|
|
1001
|
+
if (current_monotonic_wall_time_ns != INVALID_TIME) {
|
|
1012
1002
|
end_timestamp_ns = monotonic_to_system_epoch_ns(&state->time_converter_state, current_monotonic_wall_time_ns);
|
|
1013
1003
|
}
|
|
1014
1004
|
|
|
@@ -1166,7 +1156,6 @@ static VALUE _native_inspect(DDTRACE_UNUSED VALUE _self, VALUE collector_instanc
|
|
|
1166
1156
|
rb_str_concat(result, rb_sprintf(" sample_count=%u", state->sample_count));
|
|
1167
1157
|
rb_str_concat(result, rb_sprintf(" stats=%"PRIsVALUE, stats_as_ruby_hash(state)));
|
|
1168
1158
|
rb_str_concat(result, rb_sprintf(" endpoint_collection_enabled=%"PRIsVALUE, state->endpoint_collection_enabled ? Qtrue : Qfalse));
|
|
1169
|
-
rb_str_concat(result, rb_sprintf(" timeline_enabled=%"PRIsVALUE, state->timeline_enabled ? Qtrue : Qfalse));
|
|
1170
1159
|
rb_str_concat(result, rb_sprintf(" native_filenames_enabled=%"PRIsVALUE, state->native_filenames_enabled ? Qtrue : Qfalse));
|
|
1171
1160
|
// Note: `st_table_size()` is available from Ruby 3.2+ but not before
|
|
1172
1161
|
rb_str_concat(result, rb_sprintf(" native_filenames_cache_size=%zu", state->native_filenames_cache->num_entries));
|
|
@@ -1994,8 +1983,6 @@ static uint64_t otel_span_id_to_uint(VALUE otel_span_id) {
|
|
|
1994
1983
|
thread_context_collector_state *state;
|
|
1995
1984
|
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
|
1996
1985
|
|
|
1997
|
-
if (!state->timeline_enabled) raise_error(rb_eRuntimeError, "GVL profiling requires timeline to be enabled");
|
|
1998
|
-
|
|
1999
1986
|
intptr_t gvl_waiting_at = gvl_profiling_state_thread_object_get(current_thread);
|
|
2000
1987
|
|
|
2001
1988
|
if (gvl_waiting_at >= 0) {
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <mach/mach.h>
|
|
4
|
+
#include <mach/mach_time.h>
|
|
5
|
+
#include <mach/thread_policy.h>
|
|
6
|
+
#include <pthread.h>
|
|
7
|
+
#include <stdio.h>
|
|
8
|
+
|
|
9
|
+
#include "time_helpers.h"
|
|
10
|
+
|
|
11
|
+
// On macOS the default scheduler wake latency for a timeshare thread is ~1-2ms,
|
|
12
|
+
// so nanosleep regularly overshoots a 10ms request by ~2ms. Marking the worker
|
|
13
|
+
// thread with THREAD_TIME_CONSTRAINT_POLICY tells the scheduler this thread has
|
|
14
|
+
// a periodic deadline; the kernel then wakes it close to the requested time.
|
|
15
|
+
//
|
|
16
|
+
// This only affects the sampler worker thread. We pair it with a demote call
|
|
17
|
+
// before the thread can be reused by Ruby for unrelated Ruby threads (see the
|
|
18
|
+
// SIGPROF unblock dance in _native_sampling_loop).
|
|
19
|
+
static inline void promote_sampler_thread_to_realtime(uint64_t period_ns) {
|
|
20
|
+
mach_timebase_info_data_t timebase = (mach_timebase_info_data_t) {};
|
|
21
|
+
mach_timebase_info(&timebase);
|
|
22
|
+
|
|
23
|
+
// ns -> mach ticks: each mach tick is (numer/denom) ns, so divide by that.
|
|
24
|
+
#define NS_TO_MACH_TICKS(ns) ((uint32_t) (((uint64_t)(ns) * (uint64_t) timebase.denom) / (uint64_t) timebase.numer))
|
|
25
|
+
|
|
26
|
+
struct thread_time_constraint_policy policy = {
|
|
27
|
+
.period = NS_TO_MACH_TICKS(period_ns),
|
|
28
|
+
.computation = NS_TO_MACH_TICKS(MICROS_AS_NS(200)), // 200us upper bound on the work we do per period
|
|
29
|
+
.constraint = NS_TO_MACH_TICKS(MILLIS_AS_NS(1)), // wake us within 1ms of the deadline
|
|
30
|
+
.preemptible = TRUE,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
#undef NS_TO_MACH_TICKS
|
|
34
|
+
|
|
35
|
+
kern_return_t kr = thread_policy_set(
|
|
36
|
+
pthread_mach_thread_np(pthread_self()),
|
|
37
|
+
THREAD_TIME_CONSTRAINT_POLICY,
|
|
38
|
+
(thread_policy_t) &policy,
|
|
39
|
+
THREAD_TIME_CONSTRAINT_POLICY_COUNT
|
|
40
|
+
);
|
|
41
|
+
if (kr != KERN_SUCCESS) {
|
|
42
|
+
// Non-fatal: we'll fall back to the default scheduler behavior (overshoot ~2ms).
|
|
43
|
+
fprintf(stderr, "[ddtrace] Failed to set real-time policy on profiler sampler thread (kr=%d); sample cadence may be imprecise\n", kr);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
static inline void demote_sampler_thread_from_realtime(void) {
|
|
48
|
+
struct thread_standard_policy policy = {.no_data = 0};
|
|
49
|
+
thread_policy_set(
|
|
50
|
+
pthread_mach_thread_np(pthread_self()),
|
|
51
|
+
THREAD_STANDARD_POLICY,
|
|
52
|
+
(thread_policy_t) &policy,
|
|
53
|
+
THREAD_STANDARD_POLICY_COUNT
|
|
54
|
+
);
|
|
55
|
+
}
|
|
@@ -420,14 +420,12 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
|
420
420
|
VALUE heap_samples_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("heap_samples_enabled")));
|
|
421
421
|
VALUE heap_size_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("heap_size_enabled")));
|
|
422
422
|
VALUE heap_sample_every = rb_hash_fetch(options, ID2SYM(rb_intern("heap_sample_every")));
|
|
423
|
-
VALUE timeline_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("timeline_enabled")));
|
|
424
423
|
VALUE heap_clean_after_gc_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("heap_clean_after_gc_enabled")));
|
|
425
424
|
|
|
426
425
|
ENFORCE_BOOLEAN(alloc_samples_enabled);
|
|
427
426
|
ENFORCE_BOOLEAN(heap_samples_enabled);
|
|
428
427
|
ENFORCE_BOOLEAN(heap_size_enabled);
|
|
429
428
|
ENFORCE_TYPE(heap_sample_every, T_FIXNUM);
|
|
430
|
-
ENFORCE_BOOLEAN(timeline_enabled);
|
|
431
429
|
ENFORCE_BOOLEAN(heap_clean_after_gc_enabled);
|
|
432
430
|
|
|
433
431
|
stack_recorder_state *state;
|
|
@@ -440,8 +438,7 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
|
440
438
|
uint8_t requested_values_count = ALL_VALUE_TYPES_COUNT -
|
|
441
439
|
(alloc_samples_enabled == Qtrue? 0 : 2) -
|
|
442
440
|
(heap_samples_enabled == Qtrue ? 0 : 1) -
|
|
443
|
-
(heap_size_enabled == Qtrue ? 0 : 1)
|
|
444
|
-
(timeline_enabled == Qtrue ? 0 : 1);
|
|
441
|
+
(heap_size_enabled == Qtrue ? 0 : 1);
|
|
445
442
|
|
|
446
443
|
if (requested_values_count == ALL_VALUE_TYPES_COUNT) return Qtrue; // Nothing to do, this is the default
|
|
447
444
|
|
|
@@ -467,6 +464,10 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
|
467
464
|
enabled_sample_types[next_enabled_pos] = DDOG_PROF_SAMPLE_TYPE_CPU_TIME;
|
|
468
465
|
state->position_for[CPU_TIME_VALUE_ID] = next_enabled_pos++;
|
|
469
466
|
|
|
467
|
+
// TIMELINE is always enabled
|
|
468
|
+
enabled_sample_types[next_enabled_pos] = DDOG_PROF_SAMPLE_TYPE_TIMELINE;
|
|
469
|
+
state->position_for[TIMELINE_VALUE_ID] = next_enabled_pos++;
|
|
470
|
+
|
|
470
471
|
if (alloc_samples_enabled == Qtrue) {
|
|
471
472
|
enabled_sample_types[next_enabled_pos] = DDOG_PROF_SAMPLE_TYPE_ALLOC_SAMPLES;
|
|
472
473
|
state->position_for[ALLOC_SAMPLES_VALUE_ID] = next_enabled_pos++;
|
|
@@ -500,13 +501,6 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
|
500
501
|
state->heap_recorder = NULL;
|
|
501
502
|
}
|
|
502
503
|
|
|
503
|
-
if (timeline_enabled == Qtrue) {
|
|
504
|
-
enabled_sample_types[next_enabled_pos] = DDOG_PROF_SAMPLE_TYPE_TIMELINE;
|
|
505
|
-
state->position_for[TIMELINE_VALUE_ID] = next_enabled_pos++;
|
|
506
|
-
} else {
|
|
507
|
-
state->position_for[TIMELINE_VALUE_ID] = next_disabled_pos++;
|
|
508
|
-
}
|
|
509
|
-
|
|
510
504
|
ddog_prof_Profile_drop(&state->profile_slot_one.profile);
|
|
511
505
|
ddog_prof_Profile_drop(&state->profile_slot_two.profile);
|
|
512
506
|
|
|
@@ -639,6 +633,7 @@ void record_sample(VALUE recorder_instance, ddog_prof_Slice_Location locations,
|
|
|
639
633
|
.values = (ddog_Slice_I64) {.ptr = metric_values, .len = state->enabled_values_count},
|
|
640
634
|
.labels = labels.labels
|
|
641
635
|
},
|
|
636
|
+
/* To disable end_timestamp_ns to get aggregated profiles in pprof, replace the below with `0` */
|
|
642
637
|
labels.end_timestamp_ns
|
|
643
638
|
);
|
|
644
639
|
|
|
@@ -11,71 +11,85 @@ module Datadog
|
|
|
11
11
|
module Rack
|
|
12
12
|
# AI Guard Rack middleware.
|
|
13
13
|
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
# it). This ordering matters: on the way out of the request, AI Guard's
|
|
17
|
-
# `ensure` block unwinds *before* Tracing's ensure, while Tracing's
|
|
18
|
-
# request span is still live. We need that, because Tracing's ensure
|
|
19
|
-
# calls `request_span.finish`, which builds a frozen `Span` snapshot of
|
|
20
|
-
# the meta hash — any `set_tag` call after that point mutates the
|
|
21
|
-
# `SpanOperation` but never reaches the exported `Span`.
|
|
14
|
+
# At request entry the middleware stores some request attributes for
|
|
15
|
+
# on the active trace under the `_dd.ai_guard.` prefix.
|
|
22
16
|
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
# actually recorded during the request.
|
|
17
|
+
# Later when AI Guard evaluation is performed, those attributes are
|
|
18
|
+
# mirrored on AI Guard span.
|
|
26
19
|
class RequestMiddleware
|
|
27
|
-
NETWORK_CLIENT_IP_TAG = "network.client.ip"
|
|
28
|
-
|
|
29
20
|
def initialize(app, opt = {})
|
|
30
21
|
@app = app
|
|
31
22
|
end
|
|
32
23
|
|
|
33
24
|
def call(env)
|
|
25
|
+
trace = Datadog::Tracing.active_trace
|
|
26
|
+
return @app.call(env) unless trace
|
|
27
|
+
|
|
28
|
+
store_anomaly_detection_tags!(trace, env)
|
|
29
|
+
|
|
34
30
|
@app.call(env)
|
|
35
31
|
ensure
|
|
36
|
-
|
|
32
|
+
# @type var trace: Datadog::Tracing::TraceSegment?
|
|
33
|
+
# Steep: https://github.com/soutaro/steep/issues/919
|
|
34
|
+
if trace
|
|
35
|
+
tag_client_ip_on_request_span!(trace) if ai_guard_executed?(trace)
|
|
36
|
+
|
|
37
|
+
clean_up_ai_guard_temp_tags!(trace)
|
|
38
|
+
end
|
|
37
39
|
end
|
|
38
40
|
|
|
39
41
|
private
|
|
40
42
|
|
|
41
|
-
# AI Guard's evaluation flow sets `ai_guard.executed` on the trace
|
|
42
|
-
# whenever an AI Guard span is created during the request. We read
|
|
43
|
-
# it here to know whether to tag client IP, then clear it so the
|
|
44
|
-
# internal flag does not propagate to the exported trace.
|
|
45
|
-
#
|
|
46
|
-
# `Tracing.active_trace` is publicly typed as `TraceSegment?` but at
|
|
47
|
-
# runtime returns a `TraceOperation`, which exposes `get_tag` and
|
|
48
|
-
# `clear_tag`. Pre-existing sig mismatch — hence the steep:ignore.
|
|
49
43
|
# steep:ignore:start
|
|
50
|
-
def
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
44
|
+
def store_anomaly_detection_tags!(trace, env)
|
|
45
|
+
remote_ip = env["REMOTE_ADDR"]
|
|
46
|
+
trace.set_tag(Datadog::AIGuard::Ext::TRACE_NETWORK_CLIENT_IP_TAG, remote_ip) if remote_ip
|
|
47
|
+
|
|
48
|
+
headers = Datadog::Tracing::Contrib::Rack::Header::RequestHeaderCollection.new(env)
|
|
49
|
+
resolved_client_ip = Datadog::Tracing::ClientIp.extract_client_ip(headers, remote_ip)
|
|
50
|
+
trace.set_tag(Datadog::AIGuard::Ext::TRACE_HTTP_CLIENT_IP_TAG, resolved_client_ip) if resolved_client_ip
|
|
51
|
+
|
|
52
|
+
user_agent = env["HTTP_USER_AGENT"]
|
|
53
|
+
trace.set_tag(Datadog::AIGuard::Ext::TRACE_HTTP_USERAGENT_TAG, user_agent) if user_agent
|
|
54
|
+
rescue => e
|
|
55
|
+
Datadog::AIGuard.telemetry&.report(e, description: "AI Guard: failed to get request attributes")
|
|
56
|
+
end
|
|
57
|
+
# steep:ignore:end
|
|
58
|
+
|
|
59
|
+
# steep:ignore:start
|
|
60
|
+
def clean_up_ai_guard_temp_tags!(trace)
|
|
61
|
+
Ext::TRACE_ANOMALY_DETECTION_TAGS.each do |tag|
|
|
62
|
+
trace.clear_tag(tag)
|
|
63
|
+
end
|
|
54
64
|
|
|
55
|
-
trace.clear_tag(Datadog::AIGuard::Ext::
|
|
56
|
-
true
|
|
65
|
+
trace.clear_tag(Datadog::AIGuard::Ext::TRACE_EXECUTED_TAG)
|
|
57
66
|
end
|
|
58
67
|
# steep:ignore:end
|
|
59
68
|
|
|
60
|
-
|
|
69
|
+
# AI Guard's evaluation flow sets `_dd.ai_guard.executed` on the
|
|
70
|
+
# trace whenever an AI Guard span is created during the request.
|
|
71
|
+
# steep:ignore:start
|
|
72
|
+
def ai_guard_executed?(trace)
|
|
73
|
+
trace.get_tag(Datadog::AIGuard::Ext::TRACE_EXECUTED_TAG) == "1"
|
|
74
|
+
end
|
|
75
|
+
# steep:ignore:end
|
|
76
|
+
|
|
77
|
+
# steep:ignore:start
|
|
78
|
+
def tag_client_ip_on_request_span!(trace)
|
|
61
79
|
span = Datadog::Tracing.active_span
|
|
62
80
|
return unless span
|
|
63
81
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
Datadog::Tracing::
|
|
67
|
-
span,
|
|
68
|
-
headers: headers,
|
|
69
|
-
remote_ip: env["REMOTE_ADDR"]
|
|
70
|
-
)
|
|
82
|
+
client_ip = trace.get_tag(Datadog::AIGuard::Ext::TRACE_HTTP_CLIENT_IP_TAG)
|
|
83
|
+
if client_ip && span.get_tag(Datadog::Tracing::Metadata::Ext::HTTP::TAG_CLIENT_IP).nil?
|
|
84
|
+
span.set_tag(Datadog::Tracing::Metadata::Ext::HTTP::TAG_CLIENT_IP, client_ip)
|
|
71
85
|
end
|
|
72
86
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
end
|
|
87
|
+
network_client_ip = trace.get_tag(Datadog::AIGuard::Ext::TRACE_NETWORK_CLIENT_IP_TAG)
|
|
88
|
+
span["network.client.ip"] = network_client_ip if network_client_ip
|
|
76
89
|
rescue => e
|
|
77
90
|
Datadog::AIGuard.telemetry&.report(e, description: "AI Guard: failed to tag client IP on root span")
|
|
78
91
|
end
|
|
92
|
+
# steep:ignore:end
|
|
79
93
|
end
|
|
80
94
|
end
|
|
81
95
|
end
|
|
@@ -16,7 +16,12 @@ module Datadog
|
|
|
16
16
|
Tracing::Sampling::Ext::Decision::AI_GUARD
|
|
17
17
|
)
|
|
18
18
|
trace.set_tag(Ext::EVENT_TAG, true)
|
|
19
|
-
trace.set_tag(Ext::
|
|
19
|
+
trace.set_tag(Ext::TRACE_EXECUTED_TAG, "1")
|
|
20
|
+
|
|
21
|
+
Ext::TRACE_ANOMALY_DETECTION_TAGS.each do |tag|
|
|
22
|
+
value = trace.get_tag(tag)
|
|
23
|
+
span.set_tag(tag, value) if value
|
|
24
|
+
end
|
|
20
25
|
|
|
21
26
|
if (last_message = messages.last)
|
|
22
27
|
if last_message.tool_call
|
data/lib/datadog/ai_guard/ext.rb
CHANGED
|
@@ -11,8 +11,19 @@ module Datadog
|
|
|
11
11
|
REASON_TAG = "ai_guard.reason"
|
|
12
12
|
BLOCKED_TAG = "ai_guard.blocked"
|
|
13
13
|
EVENT_TAG = "ai_guard.event"
|
|
14
|
-
|
|
14
|
+
|
|
15
15
|
METASTRUCT_TAG = "ai_guard"
|
|
16
|
+
|
|
17
|
+
TRACE_EXECUTED_TAG = "_dd.ai_guard.executed"
|
|
18
|
+
TRACE_HTTP_USERAGENT_TAG = "_dd.ai_guard.http.useragent"
|
|
19
|
+
TRACE_HTTP_CLIENT_IP_TAG = "_dd.ai_guard.http.client_ip"
|
|
20
|
+
TRACE_NETWORK_CLIENT_IP_TAG = "_dd.ai_guard.network.client.ip"
|
|
21
|
+
|
|
22
|
+
TRACE_ANOMALY_DETECTION_TAGS = [
|
|
23
|
+
TRACE_HTTP_USERAGENT_TAG,
|
|
24
|
+
TRACE_HTTP_CLIENT_IP_TAG,
|
|
25
|
+
TRACE_NETWORK_CLIENT_IP_TAG
|
|
26
|
+
].freeze
|
|
16
27
|
end
|
|
17
28
|
end
|
|
18
29
|
end
|
|
@@ -15,6 +15,7 @@ module Datadog
|
|
|
15
15
|
RAILS_ROUTES_KEY = 'action_dispatch.routes'
|
|
16
16
|
RAILS_PATH_PARAMS_KEY = 'action_dispatch.request.path_parameters'
|
|
17
17
|
RAILS_FORMAT_SUFFIX = '(.:format)'
|
|
18
|
+
DATADOG_RAILS_ROUTE_KEY = 'datadog.action_dispatch.route'
|
|
18
19
|
|
|
19
20
|
# HACK: We rely on the fact that each contrib will modify `request.env`
|
|
20
21
|
# and store information sufficient to compute the canonical
|
|
@@ -55,6 +56,8 @@ module Datadog
|
|
|
55
56
|
elsif request.env.key?(SINATRA_ROUTE_KEY)
|
|
56
57
|
pattern = request.env[SINATRA_ROUTE_KEY].split(SINATRA_ROUTE_SEPARATOR, 2)[1]
|
|
57
58
|
"#{request.script_name}#{pattern}"
|
|
59
|
+
elsif request.env.key?(DATADOG_RAILS_ROUTE_KEY)
|
|
60
|
+
request.env[DATADOG_RAILS_ROUTE_KEY].path.spec.to_s.delete_suffix(RAILS_FORMAT_SUFFIX)
|
|
58
61
|
elsif request.env.key?(RAILS_ROUTE_KEY)
|
|
59
62
|
request.env[RAILS_ROUTE_KEY].path.spec.to_s.delete_suffix(RAILS_FORMAT_SUFFIX)
|
|
60
63
|
elsif request.env.key?(RAILS_ROUTE_URI_PATTERN_KEY)
|
|
@@ -46,7 +46,10 @@ module Datadog
|
|
|
46
46
|
|
|
47
47
|
security_engine = SecurityEngine::Engine.new(appsec_settings: settings.appsec, telemetry: telemetry)
|
|
48
48
|
new(security_engine: security_engine)
|
|
49
|
-
|
|
49
|
+
# NOTE: At this point we should capture all possible exceptions and
|
|
50
|
+
# gracefully fail component initialization, preventing propagation
|
|
51
|
+
# into the host application code.
|
|
52
|
+
rescue Exception => e # standard:disable Lint/RescueException
|
|
50
53
|
Datadog.logger.warn("AppSec is disabled: #{e.class}: #{e.message}; there may be additional logged errors above")
|
|
51
54
|
|
|
52
55
|
# Not reporting to telemetry here because some of the rescued exceptions
|
|
@@ -4,7 +4,7 @@ require 'json'
|
|
|
4
4
|
require 'zlib'
|
|
5
5
|
require 'stringio'
|
|
6
6
|
|
|
7
|
-
require_relative '../core/utils/
|
|
7
|
+
require_relative '../core/utils/base64_codec'
|
|
8
8
|
|
|
9
9
|
module Datadog
|
|
10
10
|
module AppSec
|
|
@@ -27,7 +27,7 @@ module Datadog
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
private_class_method def self.compress_and_encode(payload)
|
|
30
|
-
Core::Utils::
|
|
30
|
+
Core::Utils::Base64Codec.strict_encode64(
|
|
31
31
|
Zlib.gzip(payload, level: Zlib::BEST_SPEED, strategy: Zlib::DEFAULT_STRATEGY)
|
|
32
32
|
)
|
|
33
33
|
rescue Zlib::Error, TypeError => e
|
|
@@ -4,7 +4,7 @@ require 'uri'
|
|
|
4
4
|
|
|
5
5
|
require_relative '../../utils/http/media_type'
|
|
6
6
|
require_relative '../../utils/http/body'
|
|
7
|
-
require_relative '../../../core/utils/
|
|
7
|
+
require_relative '../../../core/utils/base64_codec'
|
|
8
8
|
require_relative '../../../core/header_collection'
|
|
9
9
|
require_relative '../../../tracing/client_ip'
|
|
10
10
|
|
|
@@ -42,7 +42,7 @@ module Datadog
|
|
|
42
42
|
|
|
43
43
|
headers = parse_headers(payload)
|
|
44
44
|
data = {
|
|
45
|
-
'server.response.status' => payload['
|
|
45
|
+
'server.response.status' => payload['status_code']&.to_s,
|
|
46
46
|
'server.response.headers' => headers,
|
|
47
47
|
'server.response.headers.no_cookies' => headers.dup.tap { |h| h.delete('set-cookie') }
|
|
48
48
|
}
|
|
@@ -94,7 +94,7 @@ module Datadog
|
|
|
94
94
|
body = payload['body']
|
|
95
95
|
return unless body
|
|
96
96
|
|
|
97
|
-
body = Core::Utils::
|
|
97
|
+
body = Core::Utils::Base64Codec.strict_decode64(body) if payload['base64_encoded']
|
|
98
98
|
|
|
99
99
|
content_type = headers['content-type']
|
|
100
100
|
return unless content_type
|