datadog 2.30.0 → 2.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +44 -1
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +18 -0
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +10 -0
- data/ext/datadog_profiling_native_extension/extconf.rb +2 -0
- data/ext/libdatadog_api/crashtracker.c +5 -8
- data/ext/libdatadog_api/datadog_ruby_common.c +18 -0
- data/ext/libdatadog_api/datadog_ruby_common.h +10 -0
- data/ext/libdatadog_api/di.c +79 -0
- data/ext/libdatadog_api/extconf.rb +2 -0
- data/ext/libdatadog_api/init.c +5 -2
- data/ext/libdatadog_extconf_helpers.rb +9 -1
- data/lib/datadog/ai_guard/component.rb +2 -0
- data/lib/datadog/ai_guard/contrib/ruby_llm/chat_instrumentation.rb +41 -3
- data/lib/datadog/ai_guard/evaluation/content_builder.rb +31 -0
- data/lib/datadog/ai_guard/evaluation/content_part.rb +36 -0
- data/lib/datadog/ai_guard/evaluation/no_op_result.rb +3 -1
- data/lib/datadog/ai_guard/evaluation/request.rb +14 -9
- data/lib/datadog/ai_guard/evaluation/result.rb +3 -1
- data/lib/datadog/ai_guard/evaluation.rb +36 -7
- data/lib/datadog/ai_guard.rb +26 -8
- data/lib/datadog/appsec/autoload.rb +1 -1
- data/lib/datadog/appsec/component.rb +11 -7
- data/lib/datadog/appsec/contrib/rack/gateway/request.rb +1 -1
- data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +6 -7
- data/lib/datadog/appsec/contrib/rails/patcher.rb +2 -2
- data/lib/datadog/appsec/instrumentation/gateway.rb +0 -13
- data/lib/datadog/appsec/monitor/gateway/watcher.rb +2 -0
- data/lib/datadog/appsec/utils/http/media_type.rb +1 -2
- data/lib/datadog/appsec/utils/http/url_encoded.rb +2 -2
- data/lib/datadog/appsec.rb +5 -9
- data/lib/datadog/core/configuration/base.rb +17 -5
- data/lib/datadog/core/configuration/components.rb +21 -8
- data/lib/datadog/core/configuration/config_helper.rb +9 -0
- data/lib/datadog/core/configuration/option.rb +30 -5
- data/lib/datadog/core/configuration/option_definition.rb +38 -12
- data/lib/datadog/core/configuration/options.rb +40 -6
- data/lib/datadog/core/configuration/settings.rb +15 -0
- data/lib/datadog/core/configuration/supported_configurations.rb +1 -0
- data/lib/datadog/core/contrib/rails/railtie.rb +32 -0
- data/lib/datadog/core/contrib/rails/utils.rb +7 -3
- data/lib/datadog/core/crashtracking/component.rb +3 -3
- data/lib/datadog/core/environment/container.rb +2 -2
- data/lib/datadog/core/environment/ext.rb +1 -0
- data/lib/datadog/core/environment/identity.rb +25 -3
- data/lib/datadog/core/environment/process.rb +12 -0
- data/lib/datadog/core/metrics/client.rb +5 -5
- data/lib/datadog/core/remote/component.rb +38 -21
- data/lib/datadog/core/runtime/metrics.rb +1 -1
- data/lib/datadog/core/telemetry/component.rb +3 -0
- data/lib/datadog/core/telemetry/event/app_client_configuration_change.rb +2 -3
- data/lib/datadog/core/telemetry/event/app_extended_heartbeat.rb +32 -0
- data/lib/datadog/core/telemetry/event/app_started.rb +151 -169
- data/lib/datadog/core/telemetry/event.rb +1 -7
- data/lib/datadog/core/telemetry/ext.rb +1 -0
- data/lib/datadog/core/telemetry/transport/http/telemetry.rb +5 -0
- data/lib/datadog/core/telemetry/worker.rb +20 -0
- data/lib/datadog/core/utils/only_once.rb +1 -1
- data/lib/datadog/core/utils/spawn_monkey_patch.rb +36 -0
- data/lib/datadog/core/workers/async.rb +1 -1
- data/lib/datadog/core.rb +0 -1
- data/lib/datadog/data_streams/pathway_context.rb +1 -1
- data/lib/datadog/di/boot.rb +2 -4
- data/lib/datadog/di/component.rb +4 -0
- data/lib/datadog/di/instrumenter.rb +10 -4
- data/lib/datadog/di/probe_notification_builder.rb +109 -1
- data/lib/datadog/di/serializer.rb +1 -1
- data/lib/datadog/di.rb +81 -0
- data/lib/datadog/kit/enable_core_dumps.rb +1 -1
- data/lib/datadog/open_feature/evaluation_engine.rb +1 -1
- data/lib/datadog/open_feature/exposures/reporter.rb +1 -1
- data/lib/datadog/open_feature/exposures/worker.rb +1 -1
- data/lib/datadog/open_feature/remote.rb +1 -1
- data/lib/datadog/open_feature/transport.rb +1 -1
- data/lib/datadog/opentelemetry/configuration/settings.rb +2 -0
- data/lib/datadog/profiling/collectors/code_provenance.rb +2 -3
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +1 -1
- data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -1
- data/lib/datadog/profiling/component.rb +11 -1
- data/lib/datadog/profiling/load_native_extension.rb +1 -1
- data/lib/datadog/profiling/profiler.rb +0 -4
- data/lib/datadog/profiling/scheduler.rb +2 -2
- data/lib/datadog/profiling/tasks/exec.rb +2 -2
- data/lib/datadog/profiling/tasks/setup.rb +2 -2
- data/lib/datadog/profiling.rb +1 -2
- data/lib/datadog/single_step_instrument.rb +1 -1
- data/lib/datadog/tracing/buffer.rb +3 -3
- data/lib/datadog/tracing/component.rb +11 -0
- data/lib/datadog/tracing/configuration/settings.rb +2 -1
- data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +2 -2
- data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +20 -0
- data/lib/datadog/tracing/contrib/action_pack/action_dispatch/patcher.rb +3 -1
- data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/discard.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/perform.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +1 -1
- data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +1 -1
- data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +1 -1
- data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +1 -1
- data/lib/datadog/tracing/contrib/active_record/events/sql.rb +1 -1
- data/lib/datadog/tracing/contrib/active_record/utils.rb +1 -1
- data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +1 -1
- data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +2 -2
- data/lib/datadog/tracing/contrib/aws/instrumentation.rb +1 -1
- data/lib/datadog/tracing/contrib/configurable.rb +18 -3
- data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +1 -1
- data/lib/datadog/tracing/contrib/excon/middleware.rb +2 -2
- data/lib/datadog/tracing/contrib/faraday/middleware.rb +2 -2
- data/lib/datadog/tracing/contrib/grape/endpoint.rb +5 -5
- data/lib/datadog/tracing/contrib/http/instrumentation.rb +1 -1
- data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +1 -1
- data/lib/datadog/tracing/contrib/opensearch/patcher.rb +1 -1
- data/lib/datadog/tracing/contrib/rails/log_injection.rb +1 -1
- data/lib/datadog/tracing/contrib/rails/patcher.rb +0 -1
- data/lib/datadog/tracing/contrib/rails/runner.rb +1 -1
- data/lib/datadog/tracing/contrib/rake/instrumentation.rb +2 -2
- data/lib/datadog/tracing/contrib/redis/tags.rb +1 -1
- data/lib/datadog/tracing/contrib/status_range_matcher.rb +4 -0
- data/lib/datadog/tracing/contrib/stripe/request.rb +1 -1
- data/lib/datadog/tracing/distributed/datadog.rb +4 -2
- data/lib/datadog/tracing/event.rb +1 -1
- data/lib/datadog/tracing/remote.rb +1 -1
- data/lib/datadog/tracing/sampling/ext.rb +2 -0
- data/lib/datadog/tracing/sampling/priority_sampler.rb +13 -0
- data/lib/datadog/tracing/sampling/rule.rb +1 -1
- data/lib/datadog/tracing/sampling/rule_sampler.rb +54 -25
- data/lib/datadog/tracing/sampling/span/rule_parser.rb +1 -1
- data/lib/datadog/tracing/span_operation.rb +1 -1
- data/lib/datadog/tracing/trace_operation.rb +50 -6
- data/lib/datadog/tracing/tracer.rb +25 -0
- data/lib/datadog/tracing/transport/io/client.rb +1 -1
- data/lib/datadog/tracing/transport/trace_formatter.rb +1 -1
- data/lib/datadog/version.rb +1 -1
- metadata +13 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1328115b16393f9d96152ad1d94cb6190c307d8dd5df341087ffe21e2212ec9b
|
|
4
|
+
data.tar.gz: f408b1437ae55de02488433b637c868bce087289ea5fc8af4aa808cfc4ee3b73
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: faf8598916d674b5da3b3ab57c013060f9ecf92b939d69a51528a81e495d07340e9a8a0ba70bf9498f6d8c303a32c08d3fbf971759199ff35b47f34a25d88b0a
|
|
7
|
+
data.tar.gz: f7629d50a2537e08c9ceb5da1e81c25b788401c2992a3a70a2f2b29e2a44a3cbda7d2e621a316953202f5a068fa3ec201a4810d01b1b27ee4bab5bd1592a9bbd
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,34 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [2.31.0] - 2026-04-20
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
* Core: Add `app-extended-heartbeat` telemetry event to resend full configuration every 24h for long-running services. ([#5531][])
|
|
10
|
+
* AI Guard: Add multi-model messages support to AI Guard, enabling text and image URL content parts in evaluation SDK ([#5564][])
|
|
11
|
+
* AI Guard: Add evaluation of images in user prompt and tool output for RubyLLM contrib. ([#5573][])
|
|
12
|
+
* AI Guard: Expose Sensitive Data Scanner findings in AI Guard results via `AIGuard::Evaluation::Result#sds_findings`. ([#5579][])
|
|
13
|
+
* AI Guard: Expose tag probabilities in AI Guard results via `AIGuard::Evaluation::Result#tag_probabilities`. ([#5580][])
|
|
14
|
+
* Tracing: Integrations: Add `rails.application` to the process tags. ([#5468][])
|
|
15
|
+
* Tracing: Report and display all configurations used by a service in the 'SDK & Agent Configurations' tab of a Ruby service page in Datadog UI ([#5483][])
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
* Core: Upgrade `libdatadog` dependency to version 30.0.0 ([#5574][])
|
|
20
|
+
* AI Guard: **Breaking change:** AIGuard.evaluate now defaults to `allow_raise: true`. Pass `allow_raise: false` to keep the previous non-raising behavior. ([#5587][])
|
|
21
|
+
* Dynamic Instrumentation: Report exception messages in method probe snapshots. Require the libdatadog_api C extension for Dynamic Instrumentation. ([#5111][])
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
* Profiling: Detect when `libdatadog` version does not match expected version. ([#5484][])
|
|
26
|
+
* Profiling: Log profiler initialization errors as warnings instead of raising them. ([#5509][])
|
|
27
|
+
* Tracing: Fix `NoMethodError` in `extract_trace_id!` caused by `extract_tags` returning `true`. ([#5499][])
|
|
28
|
+
|
|
29
|
+
### Removed
|
|
30
|
+
|
|
31
|
+
* Core: Removed JRuby from the CI test matrix ([#5594][])
|
|
32
|
+
|
|
5
33
|
## [2.30.0] - 2026-03-19
|
|
6
34
|
|
|
7
35
|
### Added
|
|
@@ -3539,7 +3567,8 @@ Release notes: https://github.com/DataDog/dd-trace-rb/releases/tag/v0.3.1
|
|
|
3539
3567
|
Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
|
|
3540
3568
|
|
|
3541
3569
|
|
|
3542
|
-
[Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v2.
|
|
3570
|
+
[Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v2.31.0...master
|
|
3571
|
+
[2.31.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.30.0...v2.31.0
|
|
3543
3572
|
[2.30.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.29.0...v2.30.0
|
|
3544
3573
|
[2.29.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.28.0...v2.29.0
|
|
3545
3574
|
[2.28.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.27.0...v2.28.0
|
|
@@ -5193,6 +5222,7 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
|
|
|
5193
5222
|
[#5076]: https://github.com/DataDog/dd-trace-rb/issues/5076
|
|
5194
5223
|
[#5086]: https://github.com/DataDog/dd-trace-rb/issues/5086
|
|
5195
5224
|
[#5091]: https://github.com/DataDog/dd-trace-rb/issues/5091
|
|
5225
|
+
[#5111]: https://github.com/DataDog/dd-trace-rb/issues/5111
|
|
5196
5226
|
[#5122]: https://github.com/DataDog/dd-trace-rb/issues/5122
|
|
5197
5227
|
[#5144]: https://github.com/DataDog/dd-trace-rb/issues/5144
|
|
5198
5228
|
[#5145]: https://github.com/DataDog/dd-trace-rb/issues/5145
|
|
@@ -5251,7 +5281,20 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
|
|
|
5251
5281
|
[#5448]: https://github.com/DataDog/dd-trace-rb/issues/5448
|
|
5252
5282
|
[#5449]: https://github.com/DataDog/dd-trace-rb/issues/5449
|
|
5253
5283
|
[#5461]: https://github.com/DataDog/dd-trace-rb/issues/5461
|
|
5284
|
+
[#5468]: https://github.com/DataDog/dd-trace-rb/issues/5468
|
|
5254
5285
|
[#5469]: https://github.com/DataDog/dd-trace-rb/issues/5469
|
|
5286
|
+
[#5483]: https://github.com/DataDog/dd-trace-rb/issues/5483
|
|
5287
|
+
[#5484]: https://github.com/DataDog/dd-trace-rb/issues/5484
|
|
5288
|
+
[#5499]: https://github.com/DataDog/dd-trace-rb/issues/5499
|
|
5289
|
+
[#5509]: https://github.com/DataDog/dd-trace-rb/issues/5509
|
|
5290
|
+
[#5531]: https://github.com/DataDog/dd-trace-rb/issues/5531
|
|
5291
|
+
[#5564]: https://github.com/DataDog/dd-trace-rb/issues/5564
|
|
5292
|
+
[#5573]: https://github.com/DataDog/dd-trace-rb/issues/5573
|
|
5293
|
+
[#5574]: https://github.com/DataDog/dd-trace-rb/issues/5574
|
|
5294
|
+
[#5579]: https://github.com/DataDog/dd-trace-rb/issues/5579
|
|
5295
|
+
[#5580]: https://github.com/DataDog/dd-trace-rb/issues/5580
|
|
5296
|
+
[#5587]: https://github.com/DataDog/dd-trace-rb/issues/5587
|
|
5297
|
+
[#5594]: https://github.com/DataDog/dd-trace-rb/issues/5594
|
|
5255
5298
|
[@AdrianLC]: https://github.com/AdrianLC
|
|
5256
5299
|
[@Azure7111]: https://github.com/Azure7111
|
|
5257
5300
|
[@BabyGroot]: https://github.com/BabyGroot
|
|
@@ -122,6 +122,24 @@ size_t read_ddogerr_string_and_drop(ddog_Error *error, char *string, size_t capa
|
|
|
122
122
|
return error_msg_size;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
static void verify_libdatadog_version(void) {
|
|
126
|
+
rb_eval_string(
|
|
127
|
+
"require 'libdatadog';"
|
|
128
|
+
"expected = '" EXPECTED_LIBDATADOG_VERSION "';"
|
|
129
|
+
"if expected != Libdatadog::VERSION;"
|
|
130
|
+
"raise(LoadError, <<MSG\n"
|
|
131
|
+
"The `datadog` gem needs to be reinstalled whenever the `libdatadog` gem version is changed. "
|
|
132
|
+
"The currently-installed version of `datadog` was built to work with `libdatadog` gem version #{expected} "
|
|
133
|
+
"but the currently-loaded version of `libdatadog` is #{Libdatadog::VERSION}. "
|
|
134
|
+
"To fix this, reinstall the `datadog` gem (e.g. `bundle exec gem pristine datadog`) "
|
|
135
|
+
"or contact Datadog support for help at <https://docs.datadoghq.com/help/>.\n"
|
|
136
|
+
"MSG\n"
|
|
137
|
+
");"
|
|
138
|
+
"end"
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
125
142
|
void datadog_ruby_common_init(void) {
|
|
126
143
|
telemetry_message_id = rb_intern("@telemetry_message");
|
|
144
|
+
verify_libdatadog_version();
|
|
127
145
|
}
|
|
@@ -102,3 +102,13 @@ static inline VALUE get_error_details_and_drop(ddog_Error *error) {
|
|
|
102
102
|
// Returns the amount of characters written to string (which are necessarily
|
|
103
103
|
// bounded by capacity - 1 since the string will be null-terminated).
|
|
104
104
|
size_t read_ddogerr_string_and_drop(ddog_Error *error, char *string, size_t capacity);
|
|
105
|
+
|
|
106
|
+
#define IMEMO_MASK 0x0f
|
|
107
|
+
|
|
108
|
+
// Returns the imemo type of an imemo object.
|
|
109
|
+
// This mask is the same between Ruby 2.5 and 3.3-preview3. Furthermore, the intention of this method is to be used
|
|
110
|
+
// to call `rb_imemo_name` which correctly handles invalid numbers so even if the mask changes in the future, at most
|
|
111
|
+
// we'll get incorrect results (and never a VM crash)
|
|
112
|
+
static inline int ddtrace_imemo_type(VALUE imemo) {
|
|
113
|
+
return (RBASIC(imemo)->flags >> FL_USHIFT) & IMEMO_MASK;
|
|
114
|
+
}
|
|
@@ -217,6 +217,8 @@ unless Datadog::LibdatadogExtconfHelpers.configure_libdatadog(extconf_folder: __
|
|
|
217
217
|
skip_building_extension!(Datadog::Profiling::NativeExtensionHelpers::Supported::FAILED_TO_CONFIGURE_LIBDATADOG)
|
|
218
218
|
end
|
|
219
219
|
|
|
220
|
+
Datadog::LibdatadogExtconfHelpers.add_libdatadog_version_define
|
|
221
|
+
|
|
220
222
|
unless have_type("atomic_int", ["stdatomic.h"])
|
|
221
223
|
skip_building_extension!(Datadog::Profiling::NativeExtensionHelpers::Supported::COMPILER_ATOMIC_MISSING)
|
|
222
224
|
end
|
|
@@ -51,12 +51,8 @@ static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUS
|
|
|
51
51
|
|
|
52
52
|
VALUE version = datadog_gem_version();
|
|
53
53
|
|
|
54
|
-
// Tags
|
|
54
|
+
// Tags are heap-allocated, so after here we can't raise exceptions otherwise we'll leak this memory
|
|
55
55
|
// Start of exception-free zone to prevent leaks {{
|
|
56
|
-
ddog_Endpoint *endpoint = ddog_endpoint_from_url(char_slice_from_ruby_string(agent_base_url));
|
|
57
|
-
if (endpoint == NULL) {
|
|
58
|
-
raise_error(rb_eRuntimeError, "Failed to create endpoint from agent_base_url: %"PRIsVALUE, agent_base_url);
|
|
59
|
-
}
|
|
60
56
|
ddog_Vec_Tag tags = convert_tags(tags_as_array);
|
|
61
57
|
|
|
62
58
|
ddog_crasht_Config config = {
|
|
@@ -73,7 +69,7 @@ static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUS
|
|
|
73
69
|
// overriding what Ruby set up seems a saner default to keep anyway.
|
|
74
70
|
.create_alt_stack = false,
|
|
75
71
|
.use_alt_stack = true,
|
|
76
|
-
.endpoint =
|
|
72
|
+
.endpoint = {.url = char_slice_from_ruby_string(agent_base_url)},
|
|
77
73
|
.resolve_frames = DDOG_CRASHT_STACKTRACE_COLLECTION_ENABLED_WITH_SYMBOLS_IN_RECEIVER,
|
|
78
74
|
.timeout_ms = FIX2INT(upload_timeout_seconds) * 1000,
|
|
79
75
|
};
|
|
@@ -106,11 +102,12 @@ static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUS
|
|
|
106
102
|
) :
|
|
107
103
|
ddog_crasht_update_on_fork(config, receiver_config, metadata);
|
|
108
104
|
|
|
109
|
-
first_init
|
|
105
|
+
// We use first_init to know which of [init, reconfigure] needs to be called. BUT if init failed we actually need
|
|
106
|
+
// to call init next time again, not reconfigure.
|
|
107
|
+
if (result.tag == DDOG_VOID_RESULT_OK) first_init = false;
|
|
110
108
|
|
|
111
109
|
// Clean up before potentially raising any exceptions
|
|
112
110
|
ddog_Vec_Tag_drop(tags);
|
|
113
|
-
ddog_endpoint_drop(endpoint);
|
|
114
111
|
// }} End of exception-free zone to prevent leaks
|
|
115
112
|
|
|
116
113
|
CHECK_VOID_RESULT("Failed to start/update the crash tracker", result);
|
|
@@ -122,6 +122,24 @@ size_t read_ddogerr_string_and_drop(ddog_Error *error, char *string, size_t capa
|
|
|
122
122
|
return error_msg_size;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
static void verify_libdatadog_version(void) {
|
|
126
|
+
rb_eval_string(
|
|
127
|
+
"require 'libdatadog';"
|
|
128
|
+
"expected = '" EXPECTED_LIBDATADOG_VERSION "';"
|
|
129
|
+
"if expected != Libdatadog::VERSION;"
|
|
130
|
+
"raise(LoadError, <<MSG\n"
|
|
131
|
+
"The `datadog` gem needs to be reinstalled whenever the `libdatadog` gem version is changed. "
|
|
132
|
+
"The currently-installed version of `datadog` was built to work with `libdatadog` gem version #{expected} "
|
|
133
|
+
"but the currently-loaded version of `libdatadog` is #{Libdatadog::VERSION}. "
|
|
134
|
+
"To fix this, reinstall the `datadog` gem (e.g. `bundle exec gem pristine datadog`) "
|
|
135
|
+
"or contact Datadog support for help at <https://docs.datadoghq.com/help/>.\n"
|
|
136
|
+
"MSG\n"
|
|
137
|
+
");"
|
|
138
|
+
"end"
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
125
142
|
void datadog_ruby_common_init(void) {
|
|
126
143
|
telemetry_message_id = rb_intern("@telemetry_message");
|
|
144
|
+
verify_libdatadog_version();
|
|
127
145
|
}
|
|
@@ -102,3 +102,13 @@ static inline VALUE get_error_details_and_drop(ddog_Error *error) {
|
|
|
102
102
|
// Returns the amount of characters written to string (which are necessarily
|
|
103
103
|
// bounded by capacity - 1 since the string will be null-terminated).
|
|
104
104
|
size_t read_ddogerr_string_and_drop(ddog_Error *error, char *string, size_t capacity);
|
|
105
|
+
|
|
106
|
+
#define IMEMO_MASK 0x0f
|
|
107
|
+
|
|
108
|
+
// Returns the imemo type of an imemo object.
|
|
109
|
+
// This mask is the same between Ruby 2.5 and 3.3-preview3. Furthermore, the intention of this method is to be used
|
|
110
|
+
// to call `rb_imemo_name` which correctly handles invalid numbers so even if the mask changes in the future, at most
|
|
111
|
+
// we'll get incorrect results (and never a VM crash)
|
|
112
|
+
static inline int ddtrace_imemo_type(VALUE imemo) {
|
|
113
|
+
return (RBASIC(imemo)->flags >> FL_USHIFT) & IMEMO_MASK;
|
|
114
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#include <stdbool.h>
|
|
2
|
+
|
|
3
|
+
#include "datadog_ruby_common.h"
|
|
4
|
+
|
|
5
|
+
// Prototypes for Ruby functions declared in internal Ruby headers.
|
|
6
|
+
VALUE rb_iseqw_new(const void *iseq);
|
|
7
|
+
int rb_objspace_internal_object_p(VALUE obj);
|
|
8
|
+
void rb_objspace_each_objects(
|
|
9
|
+
int (*callback)(void *start, void *end, size_t stride, void *data),
|
|
10
|
+
void *data);
|
|
11
|
+
|
|
12
|
+
#define IMEMO_TYPE_ISEQ 7
|
|
13
|
+
|
|
14
|
+
// The ID value of the string "mesg" which is used in Ruby source as
|
|
15
|
+
// id_mesg or idMesg, and is used to set and retrieve the exception message
|
|
16
|
+
// from standard library exception classes like NameError.
|
|
17
|
+
static ID id_mesg;
|
|
18
|
+
|
|
19
|
+
// Returns whether the argument is an IMEMO of type ISEQ.
|
|
20
|
+
static bool ddtrace_imemo_iseq_p(VALUE v) {
|
|
21
|
+
return rb_objspace_internal_object_p(v) && RB_TYPE_P(v, T_IMEMO) && ddtrace_imemo_type(v) == IMEMO_TYPE_ISEQ;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
static int ddtrace_di_os_obj_of_i(void *vstart, void *vend, size_t stride, void *data)
|
|
25
|
+
{
|
|
26
|
+
VALUE *array = (VALUE *)data;
|
|
27
|
+
|
|
28
|
+
VALUE v = (VALUE)vstart;
|
|
29
|
+
for (; v != (VALUE)vend; v += stride) {
|
|
30
|
+
if (ddtrace_imemo_iseq_p(v)) {
|
|
31
|
+
VALUE iseq = rb_iseqw_new((void *) v);
|
|
32
|
+
rb_ary_push(*array, iseq);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/*
|
|
40
|
+
Returns all RubyVM::InstructionSequence objects existing in the current process.
|
|
41
|
+
|
|
42
|
+
This uses the same approach as ruby/debug's iseq_collector.c:
|
|
43
|
+
https://github.com/ruby/debug/blob/master/ext/debug/iseq_collector.c
|
|
44
|
+
*/
|
|
45
|
+
static VALUE all_iseqs(DDTRACE_UNUSED VALUE _self) {
|
|
46
|
+
VALUE array = rb_ary_new();
|
|
47
|
+
rb_objspace_each_objects(ddtrace_di_os_obj_of_i, &array);
|
|
48
|
+
return array;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/*
|
|
52
|
+
* call-seq:
|
|
53
|
+
* DI.exception_message(exception) -> String | Object
|
|
54
|
+
*
|
|
55
|
+
* Returns the exception message associated with the exception via the
|
|
56
|
+
* exception's constructor.
|
|
57
|
+
*
|
|
58
|
+
* This method does not invoke Ruby code and as such will not call
|
|
59
|
+
* the +message+ method, if one is defined on the exception object.
|
|
60
|
+
*
|
|
61
|
+
* Normally, the exception message is a string, however there is no
|
|
62
|
+
* type enforcement done by Ruby for the messages and objects of arbitrary
|
|
63
|
+
* classes can be passed to exception constructors and will, subsequently,
|
|
64
|
+
* be returned by this method.
|
|
65
|
+
*
|
|
66
|
+
* @param exception [Exception] The exception object
|
|
67
|
+
* @return [String | Object] The exception message
|
|
68
|
+
*/
|
|
69
|
+
static VALUE exception_message(DDTRACE_UNUSED VALUE _self, VALUE exception) {
|
|
70
|
+
return rb_ivar_get(exception, id_mesg);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
void di_init(VALUE datadog_module) {
|
|
74
|
+
id_mesg = rb_intern("mesg");
|
|
75
|
+
|
|
76
|
+
VALUE di_module = rb_define_module_under(datadog_module, "DI");
|
|
77
|
+
rb_define_singleton_method(di_module, "all_iseqs", all_iseqs, 0);
|
|
78
|
+
rb_define_singleton_method(di_module, "exception_message", exception_message, 1);
|
|
79
|
+
}
|
|
@@ -81,6 +81,8 @@ unless Datadog::LibdatadogExtconfHelpers.configure_libdatadog(extconf_folder: __
|
|
|
81
81
|
skip_building_extension!('there was a problem in setting up the `libdatadog` dependency')
|
|
82
82
|
end
|
|
83
83
|
|
|
84
|
+
Datadog::LibdatadogExtconfHelpers.add_libdatadog_version_define
|
|
85
|
+
|
|
84
86
|
# Tag the native extension library with the Ruby version and Ruby platform.
|
|
85
87
|
# This makes it easier for development (avoids "oops I forgot to rebuild when I switched my Ruby") and ensures that
|
|
86
88
|
# the wrong library is never loaded.
|
data/ext/libdatadog_api/init.c
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
#include <ruby.h>
|
|
2
2
|
|
|
3
3
|
#include "datadog_ruby_common.h"
|
|
4
|
+
|
|
4
5
|
#include "crashtracker.h"
|
|
5
|
-
#include "process_discovery.h"
|
|
6
|
-
#include "library_config.h"
|
|
7
6
|
#include "feature_flags.h"
|
|
7
|
+
#include "library_config.h"
|
|
8
|
+
#include "process_discovery.h"
|
|
8
9
|
|
|
9
10
|
void ddsketch_init(VALUE core_module);
|
|
11
|
+
void di_init(VALUE datadog_module);
|
|
10
12
|
|
|
11
13
|
void DDTRACE_EXPORT Init_libdatadog_api(void) {
|
|
12
14
|
VALUE datadog_module = rb_define_module("Datadog");
|
|
@@ -21,4 +23,5 @@ void DDTRACE_EXPORT Init_libdatadog_api(void) {
|
|
|
21
23
|
library_config_init(core_module);
|
|
22
24
|
ddsketch_init(core_module);
|
|
23
25
|
feature_flags_init(core_module);
|
|
26
|
+
di_init(datadog_module);
|
|
24
27
|
}
|
|
@@ -10,7 +10,7 @@ module Datadog
|
|
|
10
10
|
module LibdatadogExtconfHelpers
|
|
11
11
|
# Used to make sure the correct gem version gets loaded, as extconf.rb does not get run with "bundle exec" and thus
|
|
12
12
|
# may see multiple libdatadog versions. See https://github.com/DataDog/dd-trace-rb/pull/2531 for the horror story.
|
|
13
|
-
LIBDATADOG_VERSION = '~>
|
|
13
|
+
LIBDATADOG_VERSION = '~> 30.0.0.1.0'
|
|
14
14
|
|
|
15
15
|
# Used as an workaround for a limitation with how dynamic linking works in environments where the datadog gem and
|
|
16
16
|
# libdatadog are moved after the extension gets compiled.
|
|
@@ -162,6 +162,14 @@ module Datadog
|
|
|
162
162
|
end
|
|
163
163
|
end
|
|
164
164
|
|
|
165
|
+
# Adds a C preprocessor define with the libdatadog version used at compile time.
|
|
166
|
+
# This allows runtime verification that the loaded libdatadog matches what was compiled against.
|
|
167
|
+
# rubocop:disable Style/GlobalVars
|
|
168
|
+
def self.add_libdatadog_version_define
|
|
169
|
+
$defs << %(-DEXPECTED_LIBDATADOG_VERSION=\\"#{Libdatadog::VERSION}\\")
|
|
170
|
+
end
|
|
171
|
+
# rubocop:enable Style/GlobalVars
|
|
172
|
+
|
|
165
173
|
# Note: This helper is currently only used in the `libdatadog_api/extconf.rb` BUT still lives here to enable testing.
|
|
166
174
|
def self.load_libdatadog_or_get_issue
|
|
167
175
|
try_loading_libdatadog do |exception|
|
|
@@ -7,6 +7,8 @@ require_relative 'evaluation/result'
|
|
|
7
7
|
require_relative 'evaluation/no_op_result'
|
|
8
8
|
require_relative 'evaluation/message'
|
|
9
9
|
require_relative 'evaluation/tool_call'
|
|
10
|
+
require_relative 'evaluation/content_part'
|
|
11
|
+
require_relative 'evaluation/content_builder'
|
|
10
12
|
require_relative 'ext'
|
|
11
13
|
|
|
12
14
|
module Datadog
|
|
@@ -14,13 +14,51 @@ module Datadog
|
|
|
14
14
|
AIGuard.assistant(id: tool_call_id, tool_name: tool_call.name, arguments: tool_call.arguments.to_s)
|
|
15
15
|
end
|
|
16
16
|
elsif message.tool_result?
|
|
17
|
-
|
|
17
|
+
build_ai_guard_tool(message)
|
|
18
18
|
else
|
|
19
|
-
|
|
19
|
+
build_ai_guard_message(message)
|
|
20
20
|
end
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
AIGuard.evaluate(*ai_guard_messages
|
|
23
|
+
AIGuard.evaluate(*ai_guard_messages)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def build_ai_guard_message(message)
|
|
29
|
+
content = message.content
|
|
30
|
+
|
|
31
|
+
case content
|
|
32
|
+
when ::RubyLLM::Content
|
|
33
|
+
AIGuard.message(role: message.role) do |m|
|
|
34
|
+
m.text(content.text.to_s) if content.text
|
|
35
|
+
|
|
36
|
+
# Calling attachment.for_llm triggers lazy loading of file contents.
|
|
37
|
+
# The result is memoized, so providers won't re-read.
|
|
38
|
+
content.attachments.each do |attachment|
|
|
39
|
+
case attachment.type
|
|
40
|
+
when :image
|
|
41
|
+
m.image_url(attachment.for_llm)
|
|
42
|
+
when :text
|
|
43
|
+
m.text(attachment.for_llm)
|
|
44
|
+
end
|
|
45
|
+
# Skip :pdf, :audio, :video, :unknown — not supported by AIGuard
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
else
|
|
49
|
+
AIGuard.message(role: message.role, content: content)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def build_ai_guard_tool(message)
|
|
54
|
+
content = message.content
|
|
55
|
+
# Tools can return Content or Content::Raw objects (e.g. with attachments),
|
|
56
|
+
# but AIGuard.tool expects a String. Extract text when content is a Content object.
|
|
57
|
+
case content
|
|
58
|
+
when ::RubyLLM::Content
|
|
59
|
+
content = content.text.to_s
|
|
60
|
+
end
|
|
61
|
+
AIGuard.tool(tool_call_id: message.tool_call_id, content: content)
|
|
24
62
|
end
|
|
25
63
|
end
|
|
26
64
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Datadog
|
|
4
|
+
module AIGuard
|
|
5
|
+
module Evaluation
|
|
6
|
+
# Builder for collecting content parts inside a message block.
|
|
7
|
+
#
|
|
8
|
+
# Used via the block form of {Datadog::AIGuard.message}:
|
|
9
|
+
#
|
|
10
|
+
# Datadog::AIGuard.message(role: :user) do |m|
|
|
11
|
+
# m.text("What's in this image?")
|
|
12
|
+
# m.image_url("https://example.com/img.png")
|
|
13
|
+
# end
|
|
14
|
+
class ContentBuilder
|
|
15
|
+
attr_reader :parts
|
|
16
|
+
|
|
17
|
+
def initialize
|
|
18
|
+
@parts = []
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def text(text)
|
|
22
|
+
@parts << ContentPart::Text.new(text)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def image_url(url)
|
|
26
|
+
@parts << ContentPart::ImageURL.new(url)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Datadog
|
|
4
|
+
module AIGuard
|
|
5
|
+
module Evaluation
|
|
6
|
+
# Namespace for content part types used in multi-modal messages.
|
|
7
|
+
module ContentPart
|
|
8
|
+
# A text content part.
|
|
9
|
+
class Text
|
|
10
|
+
attr_reader :text
|
|
11
|
+
|
|
12
|
+
def initialize(text)
|
|
13
|
+
@text = text
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def type
|
|
17
|
+
:text
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# An image URL content part. Accepts an absolute URL or a base64 data URI.
|
|
22
|
+
class ImageURL
|
|
23
|
+
attr_reader :url
|
|
24
|
+
|
|
25
|
+
def initialize(url)
|
|
26
|
+
@url = url
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def type
|
|
30
|
+
:image_url
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -5,12 +5,14 @@ module Datadog
|
|
|
5
5
|
module Evaluation
|
|
6
6
|
# Class for emulating AI Guard evaluation result when AI Guard is disabled.
|
|
7
7
|
class NoOpResult
|
|
8
|
-
attr_reader :action, :reason, :tags
|
|
8
|
+
attr_reader :action, :reason, :tags, :sds_findings, :tag_probabilities
|
|
9
9
|
|
|
10
10
|
def initialize
|
|
11
11
|
@action = Result::ALLOW_ACTION
|
|
12
12
|
@reason = "AI Guard is disabled"
|
|
13
13
|
@tags = []
|
|
14
|
+
@sds_findings = []
|
|
15
|
+
@tag_probabilities = {}
|
|
14
16
|
end
|
|
15
17
|
|
|
16
18
|
def allow?
|
|
@@ -44,15 +44,7 @@ module Datadog
|
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
def serialize_messages(messages)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
messages.each do |message|
|
|
50
|
-
serialized_messages << serialize_message(message)
|
|
51
|
-
|
|
52
|
-
break if serialized_messages.count == Datadog.configuration.ai_guard.max_messages_length
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
serialized_messages
|
|
47
|
+
messages.map { |message| serialize_message(message) }
|
|
56
48
|
end
|
|
57
49
|
|
|
58
50
|
def serialize_message(message)
|
|
@@ -69,12 +61,25 @@ module Datadog
|
|
|
69
61
|
}
|
|
70
62
|
]
|
|
71
63
|
}
|
|
64
|
+
elsif message.content.is_a?(::Array)
|
|
65
|
+
{role: message.role, content: serialize_content_parts(message.content)}
|
|
72
66
|
elsif message.tool_call_id
|
|
73
67
|
{role: message.role, tool_call_id: message.tool_call_id, content: message.content}
|
|
74
68
|
else
|
|
75
69
|
{role: message.role, content: message.content}
|
|
76
70
|
end
|
|
77
71
|
end
|
|
72
|
+
|
|
73
|
+
def serialize_content_parts(parts)
|
|
74
|
+
parts.map do |part|
|
|
75
|
+
case part
|
|
76
|
+
when ContentPart::Text
|
|
77
|
+
{type: "text", text: part.text}
|
|
78
|
+
when ContentPart::ImageURL
|
|
79
|
+
{type: "image_url", image_url: {url: part.url}}
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
78
83
|
end
|
|
79
84
|
end
|
|
80
85
|
end
|
|
@@ -9,7 +9,7 @@ module Datadog
|
|
|
9
9
|
DENY_ACTION = "DENY"
|
|
10
10
|
ABORT_ACTION = "ABORT"
|
|
11
11
|
|
|
12
|
-
attr_reader :action, :reason, :tags
|
|
12
|
+
attr_reader :action, :reason, :tags, :sds_findings, :tag_probabilities
|
|
13
13
|
|
|
14
14
|
def initialize(raw_response)
|
|
15
15
|
attributes = raw_response.fetch("data").fetch("attributes")
|
|
@@ -17,7 +17,9 @@ module Datadog
|
|
|
17
17
|
@action = attributes.fetch("action")
|
|
18
18
|
@reason = attributes.fetch("reason")
|
|
19
19
|
@tags = attributes.fetch("tags")
|
|
20
|
+
@tag_probabilities = attributes.fetch("tag_probs")
|
|
20
21
|
@is_blocking_enabled = attributes.fetch("is_blocking_enabled")
|
|
22
|
+
@sds_findings = attributes.fetch("sds_findings", [])
|
|
21
23
|
rescue KeyError => e
|
|
22
24
|
raise AIGuardClientError, "Missing key: \"#{e.key}\""
|
|
23
25
|
end
|
|
@@ -6,10 +6,16 @@ module Datadog
|
|
|
6
6
|
# and creating `ai_guard` span with required tags
|
|
7
7
|
module Evaluation
|
|
8
8
|
class << self
|
|
9
|
-
def perform(messages, allow_raise:
|
|
9
|
+
def perform(messages, allow_raise: true)
|
|
10
10
|
raise ArgumentError, "Messages must not be empty" if messages&.empty?
|
|
11
11
|
|
|
12
12
|
Tracing.trace(Ext::SPAN_NAME) do |span, trace|
|
|
13
|
+
trace.keep!
|
|
14
|
+
trace.set_tag(
|
|
15
|
+
Tracing::Metadata::Ext::Distributed::TAG_DECISION_MAKER,
|
|
16
|
+
Tracing::Sampling::Ext::Decision::AI_GUARD
|
|
17
|
+
)
|
|
18
|
+
|
|
13
19
|
if (last_message = messages.last)
|
|
14
20
|
if last_message.tool_call
|
|
15
21
|
span.set_tag(Ext::TARGET_TAG, "tool")
|
|
@@ -34,8 +40,10 @@ module Datadog
|
|
|
34
40
|
span.set_metastruct_tag(
|
|
35
41
|
Ext::METASTRUCT_TAG,
|
|
36
42
|
{
|
|
37
|
-
messages: truncate_content(request.serialized_messages),
|
|
38
|
-
attack_categories: result.tags
|
|
43
|
+
messages: truncate_content(truncate_messages(request.serialized_messages)),
|
|
44
|
+
attack_categories: result.tags,
|
|
45
|
+
sds: result.sds_findings,
|
|
46
|
+
tag_probs: result.tag_probabilities
|
|
39
47
|
}
|
|
40
48
|
)
|
|
41
49
|
|
|
@@ -56,14 +64,35 @@ module Datadog
|
|
|
56
64
|
|
|
57
65
|
private
|
|
58
66
|
|
|
67
|
+
def truncate_messages(serialized_messages)
|
|
68
|
+
max_length = Datadog.configuration.ai_guard.max_messages_length
|
|
69
|
+
serialized_messages.first(max_length)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Truncates content in serialized messages to stay within the configured byte limit.
|
|
73
|
+
# For multi-modal messages, only text parts are truncated; image URLs are left intact.
|
|
59
74
|
def truncate_content(serialized_messages)
|
|
75
|
+
max_bytes = Datadog.configuration.ai_guard.max_content_size_bytes
|
|
76
|
+
|
|
60
77
|
serialized_messages.map do |message| # steep:ignore
|
|
61
78
|
next message unless message[:content]
|
|
62
79
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
80
|
+
if message[:content].is_a?(::Array)
|
|
81
|
+
serialized_content = message[:content].map do |part|
|
|
82
|
+
if part[:text]
|
|
83
|
+
{**part, text: part[:text].to_s.byteslice(0, max_bytes)}
|
|
84
|
+
else
|
|
85
|
+
part
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
{**message, content: serialized_content}
|
|
90
|
+
else
|
|
91
|
+
{
|
|
92
|
+
**message,
|
|
93
|
+
content: message[:content].byteslice(0, max_bytes)
|
|
94
|
+
}
|
|
95
|
+
end
|
|
67
96
|
end
|
|
68
97
|
end
|
|
69
98
|
end
|