datadog 2.34.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.
Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +75 -6
  3. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +2 -15
  4. data/ext/datadog_profiling_native_extension/stack_recorder.c +6 -11
  5. data/lib/datadog/ai_guard/configuration.rb +1 -0
  6. data/lib/datadog/ai_guard/contrib/rack/request_middleware.rb +53 -39
  7. data/lib/datadog/ai_guard/evaluation.rb +6 -1
  8. data/lib/datadog/ai_guard/ext.rb +12 -1
  9. data/lib/datadog/appsec/api_security/route_extractor.rb +3 -0
  10. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +9 -40
  11. data/lib/datadog/appsec/default_header_tags.rb +48 -0
  12. data/lib/datadog/core/configuration/settings.rb +10 -6
  13. data/lib/datadog/core/environment/process.rb +4 -2
  14. data/lib/datadog/core/utils/{array.rb → enumerable_compat.rb} +2 -2
  15. data/lib/datadog/profiling/collectors/thread_context.rb +0 -4
  16. data/lib/datadog/profiling/component.rb +3 -11
  17. data/lib/datadog/profiling/ext/dir_monkey_patches.rb +3 -2
  18. data/lib/datadog/profiling/stack_recorder.rb +0 -4
  19. data/lib/datadog/symbol_database/extractor.rb +3 -3
  20. data/lib/datadog/tracing/contrib/action_cable/events/broadcast.rb +4 -1
  21. data/lib/datadog/tracing/contrib/action_cable/events/perform_action.rb +4 -1
  22. data/lib/datadog/tracing/contrib/action_cable/events/transmit.rb +4 -1
  23. data/lib/datadog/tracing/contrib/action_cable/instrumentation.rb +4 -1
  24. data/lib/datadog/tracing/contrib/action_mailer/event.rb +4 -1
  25. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +1 -0
  26. data/lib/datadog/tracing/contrib/action_view/events/render_partial.rb +4 -1
  27. data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +4 -1
  28. data/lib/datadog/tracing/contrib/active_job/events/discard.rb +4 -1
  29. data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +4 -1
  30. data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +4 -1
  31. data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +4 -1
  32. data/lib/datadog/tracing/contrib/active_job/events/perform.rb +4 -1
  33. data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +4 -1
  34. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +1 -0
  35. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +1 -0
  36. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +1 -0
  37. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +1 -0
  38. data/lib/datadog/tracing/contrib/dalli/instrumentation.rb +1 -0
  39. data/lib/datadog/tracing/contrib/delayed_job/plugin.rb +2 -0
  40. data/lib/datadog/tracing/contrib/delayed_job/server_internal_tracer/worker.rb +1 -0
  41. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +1 -0
  42. data/lib/datadog/tracing/contrib/elasticsearch/quantize.rb +2 -2
  43. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +1 -0
  44. data/lib/datadog/tracing/contrib/ethon/multi_patch.rb +1 -0
  45. data/lib/datadog/tracing/contrib/excon/middleware.rb +1 -0
  46. data/lib/datadog/tracing/contrib/ext.rb +3 -0
  47. data/lib/datadog/tracing/contrib/faraday/middleware.rb +1 -0
  48. data/lib/datadog/tracing/contrib/grape/endpoint.rb +3 -0
  49. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +1 -0
  50. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +1 -0
  51. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +1 -0
  52. data/lib/datadog/tracing/contrib/hanami/action_tracer.rb +1 -0
  53. data/lib/datadog/tracing/contrib/hanami/renderer_policy_tracing.rb +1 -0
  54. data/lib/datadog/tracing/contrib/hanami/router_tracing.rb +1 -0
  55. data/lib/datadog/tracing/contrib/http/instrumentation.rb +1 -0
  56. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +1 -0
  57. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +1 -0
  58. data/lib/datadog/tracing/contrib/kafka/event.rb +1 -0
  59. data/lib/datadog/tracing/contrib/mongodb/parsers.rb +5 -5
  60. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +2 -0
  61. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +1 -0
  62. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +1 -0
  63. data/lib/datadog/tracing/contrib/opensearch/quantize.rb +2 -2
  64. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +1 -0
  65. data/lib/datadog/tracing/contrib/presto/instrumentation.rb +3 -0
  66. data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +3 -0
  67. data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +2 -2
  68. data/lib/datadog/tracing/contrib/que/tracer.rb +1 -0
  69. data/lib/datadog/tracing/contrib/racecar/event.rb +1 -0
  70. data/lib/datadog/tracing/contrib/rack/header_tagging.rb +23 -0
  71. data/lib/datadog/tracing/contrib/rack/middlewares.rb +1 -0
  72. data/lib/datadog/tracing/contrib/rack/trace_proxy_middleware.rb +2 -0
  73. data/lib/datadog/tracing/contrib/rails/runner.rb +2 -0
  74. data/lib/datadog/tracing/contrib/rake/instrumentation.rb +4 -2
  75. data/lib/datadog/tracing/contrib/redis/trace_middleware.rb +2 -0
  76. data/lib/datadog/tracing/contrib/resque/resque_job.rb +1 -0
  77. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +1 -0
  78. data/lib/datadog/tracing/contrib/roda/ext.rb +1 -0
  79. data/lib/datadog/tracing/contrib/roda/instrumentation.rb +4 -1
  80. data/lib/datadog/tracing/contrib/sequel/database.rb +1 -0
  81. data/lib/datadog/tracing/contrib/sequel/dataset.rb +1 -0
  82. data/lib/datadog/tracing/contrib/shoryuken/tracer.rb +1 -0
  83. data/lib/datadog/tracing/contrib/sidekiq/client_tracer.rb +1 -0
  84. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/heartbeat.rb +2 -0
  85. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/job_fetch.rb +1 -0
  86. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/redis_info.rb +1 -0
  87. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/scheduled_poller.rb +2 -0
  88. data/lib/datadog/tracing/contrib/sidekiq/server_internal_tracer/stop.rb +1 -0
  89. data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +3 -2
  90. data/lib/datadog/tracing/contrib/sinatra/tracer.rb +1 -0
  91. data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +1 -0
  92. data/lib/datadog/tracing/contrib/sneakers/tracer.rb +1 -0
  93. data/lib/datadog/tracing/contrib/sucker_punch/instrumentation.rb +1 -0
  94. data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +1 -0
  95. data/lib/datadog/tracing/contrib/utils/quantization/{hash.rb → hash_formatter.rb} +1 -1
  96. data/lib/datadog/tracing/metadata/ext.rb +7 -0
  97. data/lib/datadog/tracing/tracer.rb +3 -0
  98. data/lib/datadog/tracing/transport/traces.rb +2 -2
  99. data/lib/datadog/version.rb +1 -1
  100. metadata +8 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aa3ddf8f18dda4b503c654911332fb5cdc50356d4fa8223d43a993be204618ac
4
- data.tar.gz: d917e203823268d36fc01c086969052160a6afdfb2c3cfdc29a081d8ce23a384
3
+ metadata.gz: 4029cb3d97dee5a685f9f34027d3f46c960d452eb82339f8a167227f597881aa
4
+ data.tar.gz: 3a108d7cb90cb00326d2f9ec563e0370855161ea21c270e31cfe8bbd16b9e154
5
5
  SHA512:
6
- metadata.gz: 4fda14c3e26b82d5b93e3581acfa939d9ac3be695c2ad26ca720b4873127f05e949dc78f0cb1b8922ed87cd114881e9191fa36463cd86f3dcbf5d969da0da932
7
- data.tar.gz: ca53286a2a5ad436e460ede12148faed98b9904d0823715954f602cdf292cb03966cde1f96dd2c227581b1ccd814c30fdd0236dfaa1903f59a87ae2873f40dd0
6
+ metadata.gz: c10991420b5026c39d0ca76529be13c276479236ebf01ab8e5050c110f687266620bfa04127f76cdb55c87b54e84c3cee5ffa39f1de46492f2dd90a58f8ed484
7
+ data.tar.gz: f7508958b880c20197ab687ff2eb98c29af51aadbc1835f8ae88a88b8858a520518aefb0d73797ae0728018fe871056c4fa4baccc86aa77b9ec0e1d20a8df298
data/CHANGELOG.md CHANGED
@@ -2,16 +2,31 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
- ## [2.34.0] - 2026-05-27
5
+ ## [2.35.0] - 2026-06-03
6
6
 
7
7
  ### Added
8
8
 
9
- * Dynamic Instrumentation: Enable Symbol Database upload so Ruby services populate Live Debugger UI autocomplete when creating probes ([#5717][])
10
- * Open Telemetry: Add OpenTelemetry logs support with OTLP export. Enable using `DD_LOGS_OTEL_ENABLED=true`; supports standard `OTEL_EXPORTER_OTLP_*` settings ([#5446][])
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
11
 
12
12
  ### Changed
13
13
 
14
- * Dynamic Instrumentation: Improve request latency under load by throttling symbol database extraction CPU usage during background processing ([#5776][])
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][])
15
30
 
16
31
  ### Fixed
17
32
 
@@ -21,6 +36,39 @@
21
36
  * Dynamic Instrumentation: Fix off-by-one in `max_capture_depth` so snapshots respect the configured nesting limit exactly ([#5753][])
22
37
  * Dynamic Instrumentation: Improve line probes to match `sourceFile` case-insensitively and support Windows-style backslashes for correct installation ([#5754][])
23
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
+
24
72
  ## [2.31.0] - 2026-04-20
25
73
 
26
74
  ### Added
@@ -3586,8 +3634,11 @@ Release notes: https://github.com/DataDog/dd-trace-rb/releases/tag/v0.3.1
3586
3634
  Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
3587
3635
 
3588
3636
 
3589
- [Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v2.34.0...master
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
3590
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
3591
3642
  [2.31.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.30.0...v2.31.0
3592
3643
  [2.30.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.29.0...v2.30.0
3593
3644
  [2.29.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.28.0...v2.29.0
@@ -5307,23 +5358,41 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
5307
5358
  [#5483]: https://github.com/DataDog/dd-trace-rb/issues/5483
5308
5359
  [#5484]: https://github.com/DataDog/dd-trace-rb/issues/5484
5309
5360
  [#5499]: https://github.com/DataDog/dd-trace-rb/issues/5499
5361
+ [#5501]: https://github.com/DataDog/dd-trace-rb/issues/5501
5310
5362
  [#5509]: https://github.com/DataDog/dd-trace-rb/issues/5509
5311
5363
  [#5531]: https://github.com/DataDog/dd-trace-rb/issues/5531
5312
5364
  [#5564]: https://github.com/DataDog/dd-trace-rb/issues/5564
5365
+ [#5569]: https://github.com/DataDog/dd-trace-rb/issues/5569
5313
5366
  [#5573]: https://github.com/DataDog/dd-trace-rb/issues/5573
5314
5367
  [#5574]: https://github.com/DataDog/dd-trace-rb/issues/5574
5315
5368
  [#5579]: https://github.com/DataDog/dd-trace-rb/issues/5579
5316
5369
  [#5580]: https://github.com/DataDog/dd-trace-rb/issues/5580
5317
5370
  [#5587]: https://github.com/DataDog/dd-trace-rb/issues/5587
5318
5371
  [#5594]: https://github.com/DataDog/dd-trace-rb/issues/5594
5372
+ [#5599]: https://github.com/DataDog/dd-trace-rb/issues/5599
5319
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
5320
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
5321
5387
  [#5753]: https://github.com/DataDog/dd-trace-rb/issues/5753
5322
5388
  [#5754]: https://github.com/DataDog/dd-trace-rb/issues/5754
5323
5389
  [#5762]: https://github.com/DataDog/dd-trace-rb/issues/5762
5324
5390
  [#5768]: https://github.com/DataDog/dd-trace-rb/issues/5768
5325
5391
  [#5773]: https://github.com/DataDog/dd-trace-rb/issues/5773
5326
- [#5776]: https://github.com/DataDog/dd-trace-rb/issues/5776
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
5327
5396
  [@AdrianLC]: https://github.com/AdrianLC
5328
5397
  [@Azure7111]: https://github.com/Azure7111
5329
5398
  [@BabyGroot]: https://github.com/BabyGroot
@@ -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 = 0;
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 (state->timeline_enabled && current_monotonic_wall_time_ns != INVALID_TIME) {
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) {
@@ -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
 
@@ -71,6 +71,7 @@ module Datadog
71
71
  option :app_key do |o|
72
72
  o.type :string, nilable: true
73
73
  o.env Ext::ENV_APP_KEY
74
+ o.skip_telemetry true
74
75
  end
75
76
 
76
77
  # Request timeout in milliseconds.
@@ -11,71 +11,85 @@ module Datadog
11
11
  module Rack
12
12
  # AI Guard Rack middleware.
13
13
  #
14
- # Inserted into the middleware stack right after
15
- # Datadog::Tracing::Contrib::Rack::TraceMiddleware (i.e. nested inside
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
- # So while the span is still active, we tag `http.client_ip` and
24
- # `network.client.ip` on it — but only when an AI Guard span was
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
- tag_client_ip(env) if consume_ai_guard_executed_flag
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 consume_ai_guard_executed_flag
51
- trace = Datadog::Tracing.active_trace
52
- return false unless trace
53
- return false unless trace.get_tag(Datadog::AIGuard::Ext::SERVICE_ENTRY_EXECUTED_TAG) == "1"
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::SERVICE_ENTRY_EXECUTED_TAG)
56
- true
65
+ trace.clear_tag(Datadog::AIGuard::Ext::TRACE_EXECUTED_TAG)
57
66
  end
58
67
  # steep:ignore:end
59
68
 
60
- def tag_client_ip(env)
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
- if span.get_tag(Datadog::Tracing::Metadata::Ext::HTTP::TAG_CLIENT_IP).nil?
65
- headers = Datadog::Tracing::Contrib::Rack::Header::RequestHeaderCollection.new(env)
66
- Datadog::Tracing::ClientIp.set_client_ip_tag!(
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
- if env["REMOTE_ADDR"] && span.get_tag(NETWORK_CLIENT_IP_TAG).nil?
74
- span.set_tag(NETWORK_CLIENT_IP_TAG, env["REMOTE_ADDR"])
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::SERVICE_ENTRY_EXECUTED_TAG, "1")
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
@@ -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
- SERVICE_ENTRY_EXECUTED_TAG = "_dd.ai_guard.executed"
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)
@@ -9,9 +9,11 @@ require_relative 'gateway/response'
9
9
  require_relative '../../event'
10
10
  require_relative '../../response'
11
11
  require_relative '../../api_security'
12
+ require_relative '../../default_header_tags'
12
13
  require_relative '../../security_event'
13
14
  require_relative '../../instrumentation/gateway'
14
15
 
16
+ require_relative '../../../core/header_collection'
15
17
  require_relative '../../../tracing/client_ip'
16
18
  require_relative '../../../tracing/contrib/rack/header_collection'
17
19
 
@@ -19,24 +21,6 @@ module Datadog
19
21
  module AppSec
20
22
  module Contrib
21
23
  module Rack
22
- RESPONSE_HEADERS_TAGS = %w[
23
- content-length
24
- content-type
25
- content-encoding
26
- content-language
27
- ].freeze
28
-
29
- WAF_VENDOR_HEADERS_TAGS = %w[
30
- X-Amzn-Trace-Id
31
- Cloudfront-Viewer-Ja3-Fingerprint
32
- Cf-Ray
33
- X-Cloud-Trace-Context
34
- X-Appgw-Trace-id
35
- X-SigSci-RequestID
36
- X-SigSci-Tags
37
- Akamai-User-Risk
38
- ].map(&:downcase).freeze
39
-
40
24
  # Topmost Rack middleware for AppSec
41
25
  # This should be inserted just below Datadog::Tracing::Contrib::Rack::TraceMiddleware
42
26
  class RequestMiddleware
@@ -44,7 +28,6 @@ module Datadog
44
28
  @app = app
45
29
 
46
30
  @oneshot_tags_sent = false
47
- @rack_headers = {}
48
31
  end
49
32
 
50
33
  # rubocop:disable Metrics/MethodLength
@@ -186,38 +169,28 @@ module Datadog
186
169
  end
187
170
  # standard:enable Metrics/MethodLength
188
171
 
189
- # standard:disable Metrics/MethodLength
190
172
  def add_request_tags(context, env)
191
173
  span = context.span
192
174
  return unless span
193
175
 
194
- # Always add WAF vendors headers
195
- WAF_VENDOR_HEADERS_TAGS.each do |lowercase_header|
196
- rack_header = to_rack_header(lowercase_header)
197
- span.set_tag("http.request.headers.#{lowercase_header}", env[rack_header]) if env[rack_header]
198
- end
199
-
200
- if span && span.get_tag(Tracing::Metadata::Ext::HTTP::TAG_CLIENT_IP).nil?
201
- request_header_collection = Datadog::Tracing::Contrib::Rack::Header::RequestHeaderCollection.new(env)
176
+ headers = Tracing::Contrib::Rack::Header::RequestHeaderCollection.new(env)
177
+ AppSec::DefaultHeaderTags.tag_request(span, headers)
202
178
 
179
+ if span.get_tag(Tracing::Metadata::Ext::HTTP::TAG_CLIENT_IP).nil?
203
180
  # always collect client ip, as this is part of AppSec provided functionality
204
181
  Datadog::Tracing::ClientIp.set_client_ip_tag!(
205
- span,
206
- headers: request_header_collection,
207
- remote_ip: env['REMOTE_ADDR']
182
+ span, headers: headers, remote_ip: env['REMOTE_ADDR']
208
183
  )
209
184
  end
210
185
  end
211
- # standard:enable Metrics/MethodLength
212
186
 
213
187
  def add_response_tags(context, response)
214
188
  span = context.span
215
189
  return unless span
216
190
 
217
- RESPONSE_HEADERS_TAGS.each do |name|
218
- value = response.headers[name]
219
- span.set_tag("http.response.headers.#{name}", value.to_s) if value
220
- end
191
+ AppSec::DefaultHeaderTags.tag_response(
192
+ span, Datadog::Core::HeaderCollection.from_hash(response.headers)
193
+ )
221
194
 
222
195
  unless response.headers.key?('content-length')
223
196
  length = ResponseBody.content_length(response.body)
@@ -228,10 +201,6 @@ module Datadog
228
201
  def oneshot_tags_sent?
229
202
  @oneshot_tags_sent
230
203
  end
231
-
232
- def to_rack_header(header)
233
- @rack_headers[header] ||= Datadog::Tracing::Contrib::Rack::Header.to_rack_header(header)
234
- end
235
204
  end
236
205
  end
237
206
  end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module AppSec
5
+ # Sets the always-on span tags built from a fixed allowlist of request and
6
+ # response headers.
7
+ #
8
+ # NOTE: Unlike {Event#record}, which reports request headers only when
9
+ # a security event fires, these are tagged on every request/response
10
+ #
11
+ # @api private
12
+ module DefaultHeaderTags
13
+ WAF_VENDOR_HEADERS_TAGS = %w[
14
+ x-amzn-trace-id
15
+ cloudfront-viewer-ja3-fingerprint
16
+ cf-ray
17
+ x-cloud-trace-context
18
+ x-appgw-trace-id
19
+ x-sigsci-requestid
20
+ x-sigsci-tags
21
+ akamai-user-risk
22
+ ].freeze
23
+
24
+ RESPONSE_HEADERS_TAGS = %w[
25
+ content-length
26
+ content-type
27
+ content-encoding
28
+ content-language
29
+ ].freeze
30
+
31
+ module_function
32
+
33
+ def tag_request(span, headers)
34
+ WAF_VENDOR_HEADERS_TAGS.each do |name|
35
+ value = headers.get(name)
36
+ span.set_tag("http.request.headers.#{name}", value) if value
37
+ end
38
+ end
39
+
40
+ def tag_response(span, headers)
41
+ RESPONSE_HEADERS_TAGS.each do |name|
42
+ value = headers.get(name)
43
+ span.set_tag("http.response.headers.#{name}", value.to_s) if value
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -107,6 +107,7 @@ module Datadog
107
107
  option :api_key do |o|
108
108
  o.type :string, nilable: true
109
109
  o.env Core::Environment::Ext::ENV_API_KEY
110
+ o.skip_telemetry true
110
111
  end
111
112
 
112
113
  # Datadog diagnostic settings.
@@ -394,16 +395,19 @@ module Datadog
394
395
  o.default false
395
396
  end
396
397
 
397
- # Controls data collection for the timeline feature.
398
- #
399
- # If you needed to disable this, please tell us why on <https://github.com/DataDog/dd-trace-rb/issues/new>,
400
- # so we can fix it!
401
- #
402
- # @default `DD_PROFILING_TIMELINE_ENABLED` environment variable as a boolean, otherwise `true`
398
+ # DEV-3.0: Remove `timeline_enabled` option
403
399
  option :timeline_enabled do |o|
404
400
  o.type :bool
405
401
  o.env 'DD_PROFILING_TIMELINE_ENABLED'
406
402
  o.default true
403
+ o.after_set do |_, _, precedence|
404
+ unless precedence == Datadog::Core::Configuration::Option::Precedence::DEFAULT
405
+ Core.log_deprecation(key: :timeline_enabled) do
406
+ 'The profiling.advanced.timeline_enabled setting has been deprecated for removal and no longer does anything. ' \
407
+ 'Please remove it from your Datadog.configure block and do not set DD_PROFILING_TIMELINE_ENABLED.'
408
+ end
409
+ end
410
+ end
407
411
  end
408
412
 
409
413
  # The profiler gathers data by sending `SIGPROF` unix signals to Ruby application threads.