datadog 2.43.0 → 2.45.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 +69 -1
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +136 -132
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +1 -5
- data/ext/datadog_profiling_native_extension/collectors_gc_profiling_helper.c +0 -2
- data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +19 -20
- data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.h +5 -1
- data/ext/datadog_profiling_native_extension/collectors_stack.c +7 -7
- data/ext/datadog_profiling_native_extension/collectors_stack.h +1 -1
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +146 -109
- data/ext/datadog_profiling_native_extension/collectors_thread_context.h +3 -2
- data/ext/datadog_profiling_native_extension/extconf.rb +6 -2
- data/ext/datadog_profiling_native_extension/heap_recorder.c +2 -2
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +122 -53
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +6 -2
- data/ext/datadog_profiling_native_extension/profiling.c +22 -23
- data/ext/datadog_profiling_native_extension/ruby_helpers.c +3 -1
- data/ext/datadog_profiling_native_extension/ruby_helpers.h +4 -0
- data/ext/datadog_profiling_native_extension/setup_signal_handler.c +0 -2
- data/ext/datadog_profiling_native_extension/stack_recorder.c +10 -28
- data/ext/datadog_profiling_native_extension/stack_recorder.h +0 -1
- data/ext/datadog_profiling_native_extension/time_helpers.c +2 -4
- data/ext/datadog_profiling_native_extension/time_helpers.h +13 -14
- data/ext/libdatadog_api/di.c +8 -3
- data/ext/libdatadog_api/extconf.rb +1 -1
- data/ext/libdatadog_api/feature_flags.c +8 -0
- data/ext/libdatadog_api/init.c +3 -1
- data/ext/libdatadog_api/otel_thread_context.c +38 -17
- data/ext/libdatadog_api/trace_exporter.c +77 -11
- data/ext/libdatadog_extconf_helpers.rb +14 -9
- data/lib/datadog/ai_guard/component.rb +17 -8
- data/lib/datadog/ai_guard/configuration/ext.rb +2 -0
- data/lib/datadog/ai_guard/configuration.rb +13 -0
- data/lib/datadog/ai_guard/contrib/ruby_llm/chat_instrumentation.rb +48 -55
- data/lib/datadog/ai_guard/contrib/ruby_llm/integration.rb +1 -1
- data/lib/datadog/ai_guard/contrib/ruby_llm/message_adapter.rb +76 -0
- data/lib/datadog/ai_guard/contrib/ruby_llm/patcher.rb +1 -0
- data/lib/datadog/ai_guard/contrib/ruby_llm/redaction_change_set.rb +135 -0
- data/lib/datadog/ai_guard/evaluation/client.rb +50 -0
- data/lib/datadog/ai_guard/evaluation/content_part.rb +12 -0
- data/lib/datadog/ai_guard/evaluation/message.rb +25 -7
- data/lib/datadog/ai_guard/evaluation/message_builder.rb +31 -0
- data/lib/datadog/ai_guard/evaluation/no_op_result.rb +6 -6
- data/lib/datadog/ai_guard/evaluation/outcome.rb +24 -0
- data/lib/datadog/ai_guard/evaluation/request.rb +4 -60
- data/lib/datadog/ai_guard/evaluation/response.rb +33 -0
- data/lib/datadog/ai_guard/evaluation/result.rb +11 -18
- data/lib/datadog/ai_guard/evaluation/tool_call.rb +15 -2
- data/lib/datadog/ai_guard/evaluation.rb +25 -11
- data/lib/datadog/ai_guard/ext.rb +1 -0
- data/lib/datadog/ai_guard/{api_client.rb → http_client.rb} +6 -4
- data/lib/datadog/ai_guard/metrics/telemetry.rb +43 -0
- data/lib/datadog/ai_guard/redaction/replacements.rb +97 -0
- data/lib/datadog/ai_guard/redaction/result.rb +29 -0
- data/lib/datadog/ai_guard/redaction.rb +80 -0
- data/lib/datadog/ai_guard.rb +58 -57
- data/lib/datadog/core/configuration/components.rb +9 -1
- data/lib/datadog/core/configuration/settings.rb +5 -2
- data/lib/datadog/core/configuration/supported_configurations.rb +8 -0
- data/lib/datadog/core/remote/client/capabilities.rb +10 -4
- data/lib/datadog/core/remote/component.rb +9 -2
- data/lib/datadog/core/remote/worker.rb +8 -0
- data/lib/datadog/di/instrumenter.rb +1 -1
- data/lib/datadog/di/probe_manager.rb +10 -5
- data/lib/datadog/di/proc_responder.rb +1 -1
- data/lib/datadog/open_feature/agentless/configuration_source.rb +246 -0
- data/lib/datadog/open_feature/agentless/parser.rb +43 -0
- data/lib/datadog/open_feature/agentless/response.rb +19 -0
- data/lib/datadog/open_feature/agentless/transport.rb +103 -0
- data/lib/datadog/open_feature/component.rb +57 -1
- data/lib/datadog/open_feature/configuration/agentless_endpoint.rb +90 -0
- data/lib/datadog/open_feature/configuration/source/resolution.rb +22 -0
- data/lib/datadog/open_feature/configuration/source.rb +43 -0
- data/lib/datadog/open_feature/configuration.rb +118 -0
- data/lib/datadog/open_feature/native_evaluator.rb +1 -14
- data/lib/datadog/open_feature/remote.rb +5 -5
- data/lib/datadog/opentelemetry/signal_configuration.rb +13 -9
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +7 -2
- data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +5 -7
- data/lib/datadog/profiling/component.rb +2 -3
- data/lib/datadog/profiling/exporter.rb +7 -5
- data/lib/datadog/symbol_database/extractor.rb +84 -102
- data/lib/datadog/tracing/component.rb +3 -1
- data/lib/datadog/tracing/configuration/ext.rb +1 -0
- data/lib/datadog/tracing/configuration/settings.rb +9 -2
- data/lib/datadog/tracing/context.rb +24 -2
- data/lib/datadog/tracing/context_provider.rb +9 -5
- data/lib/datadog/tracing/contrib/grape/endpoint.rb +32 -5
- data/lib/datadog/tracing/contrib/utils/quantization/http.rb +6 -3
- data/lib/datadog/tracing/diagnostics/environment_logger.rb +1 -0
- data/lib/datadog/tracing/metadata/ext.rb +3 -0
- data/lib/datadog/tracing/otel_thread_context.rb +76 -11
- data/lib/datadog/tracing/tracer.rb +15 -2
- data/lib/datadog/tracing/transport/native.rb +23 -22
- data/lib/datadog/tracing/transport/trace_formatter.rb +13 -3
- data/lib/datadog/tracing/transport/traces.rb +1 -1
- data/lib/datadog/version.rb +1 -1
- metadata +23 -7
- data/lib/datadog/ai_guard/evaluation/content_builder.rb +0 -31
|
@@ -28,10 +28,8 @@ long monotonic_to_system_epoch_ns(monotonic_to_system_epoch_state *state, long m
|
|
|
28
28
|
(state->delta_to_epoch_ns + monotonic_wall_time_ns > state->system_epoch_ns_reference + SECONDS_AS_NS(60));
|
|
29
29
|
|
|
30
30
|
if (reference_needs_update) {
|
|
31
|
-
state->system_epoch_ns_reference = system_epoch_time_now_ns(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
state->delta_to_epoch_ns = state->system_epoch_ns_reference - current_monotonic_wall_time_ns;
|
|
31
|
+
state->system_epoch_ns_reference = system_epoch_time_now_ns();
|
|
32
|
+
state->delta_to_epoch_ns = state->system_epoch_ns_reference - monotonic_wall_time_now_ns();
|
|
35
33
|
}
|
|
36
34
|
|
|
37
35
|
return state->delta_to_epoch_ns + monotonic_wall_time_ns;
|
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
|
-
#include <
|
|
3
|
+
#include <ruby.h>
|
|
4
4
|
#include <errno.h>
|
|
5
|
+
#include <stdbool.h>
|
|
5
6
|
#include <time.h>
|
|
6
7
|
|
|
7
8
|
#include "extconf.h"
|
|
8
|
-
#include "ruby_helpers.h"
|
|
9
|
-
|
|
10
|
-
#define SECONDS_AS_NS(value) (value * 1000 * 1000 * 1000L)
|
|
11
|
-
#define MILLIS_AS_NS(value) (value * 1000 * 1000L)
|
|
12
|
-
#define MICROS_AS_NS(value) (value * 1000L)
|
|
13
9
|
|
|
14
|
-
|
|
10
|
+
#define SECONDS_AS_NS(value) ((value) * 1000L * 1000L * 1000L)
|
|
11
|
+
#define MILLIS_AS_NS(value) ((value) * 1000L * 1000L)
|
|
12
|
+
#define MICROS_AS_NS(value) ((value) * 1000L)
|
|
15
13
|
|
|
16
14
|
#define INVALID_TIME -1
|
|
17
15
|
|
|
@@ -23,12 +21,13 @@ typedef struct {
|
|
|
23
21
|
|
|
24
22
|
#define MONOTONIC_TO_SYSTEM_EPOCH_INITIALIZER {.system_epoch_ns_reference = INVALID_TIME}
|
|
25
23
|
|
|
26
|
-
|
|
24
|
+
// clock_id MUST be one of CLOCK_REALTIME/CLOCK_MONOTONIC_FOR_PROFILING/CLOCK_MONOTONIC_COARSE_FOR_PROFILING
|
|
25
|
+
// as for those we're guaranteed to never see failures here unless something is really off
|
|
26
|
+
static inline long retrieve_clock_as_ns(clockid_t clock_id) {
|
|
27
27
|
struct timespec clock_value;
|
|
28
28
|
|
|
29
|
-
if (clock_gettime(clock_id, &clock_value) != 0) {
|
|
30
|
-
|
|
31
|
-
return 0;
|
|
29
|
+
if (RB_UNLIKELY(clock_gettime(clock_id, &clock_value) != 0)) {
|
|
30
|
+
rb_bug("[ddtrace] clock_gettime(%d) unexpectedly failed (errno=%d) but it should always succeed, libc/kernel bug?", (int) clock_id, errno);
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
return clock_value.tv_nsec + SECONDS_AS_NS(clock_value.tv_sec);
|
|
@@ -43,8 +42,8 @@ static inline long retrieve_clock_as_ns(clockid_t clock_id, raise_on_failure_set
|
|
|
43
42
|
#define CLOCK_MONOTONIC_COARSE_FOR_PROFILING CLOCK_MONOTONIC_COARSE
|
|
44
43
|
#endif
|
|
45
44
|
|
|
46
|
-
static inline long monotonic_wall_time_now_ns(
|
|
47
|
-
static inline long system_epoch_time_now_ns(
|
|
45
|
+
static inline long monotonic_wall_time_now_ns(void) { return retrieve_clock_as_ns(CLOCK_MONOTONIC_FOR_PROFILING); }
|
|
46
|
+
static inline long system_epoch_time_now_ns(void) { return retrieve_clock_as_ns(CLOCK_REALTIME); }
|
|
48
47
|
|
|
49
48
|
// Coarse instants use CLOCK_MONOTONIC_COARSE on Linux which is expected to provide resolution in the millisecond range:
|
|
50
49
|
// https://docs.redhat.com/en/documentation/red_hat_enterprise_linux_for_real_time/7/html/reference_guide/sect-posix_clocks#Using_clock_getres_to_compare_clock_resolution
|
|
@@ -57,7 +56,7 @@ typedef struct {
|
|
|
57
56
|
static inline coarse_instant to_coarse_instant(long timestamp_ns) { return (coarse_instant) {.timestamp_ns = timestamp_ns}; }
|
|
58
57
|
|
|
59
58
|
static inline coarse_instant monotonic_coarse_wall_time_now_ns(void) {
|
|
60
|
-
return to_coarse_instant(retrieve_clock_as_ns(CLOCK_MONOTONIC_COARSE_FOR_PROFILING
|
|
59
|
+
return to_coarse_instant(retrieve_clock_as_ns(CLOCK_MONOTONIC_COARSE_FOR_PROFILING));
|
|
61
60
|
}
|
|
62
61
|
|
|
63
62
|
long monotonic_to_system_epoch_ns(monotonic_to_system_epoch_state *state, long monotonic_wall_time_ns);
|
data/ext/libdatadog_api/di.c
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
#include "datadog_ruby_common.h"
|
|
4
4
|
|
|
5
|
+
// rb_iseq is CRuby-specific
|
|
6
|
+
#ifndef TRUFFLERUBY
|
|
7
|
+
|
|
5
8
|
// Prototypes for Ruby functions declared in internal Ruby headers.
|
|
6
9
|
// rb_iseqw_new wraps an internal iseq pointer into a Ruby-visible
|
|
7
10
|
// RubyVM::InstructionSequence object.
|
|
@@ -207,7 +210,9 @@ void di_init(VALUE datadog_module) {
|
|
|
207
210
|
rb_define_singleton_method(di_module, "enter_probe", enter_probe, 0);
|
|
208
211
|
rb_define_singleton_method(di_module, "leave_probe", leave_probe, 0);
|
|
209
212
|
rb_define_singleton_method(di_module, "hash?", is_hash, 1);
|
|
210
|
-
#ifdef HAVE_RB_ISEQ_TYPE
|
|
211
|
-
|
|
212
|
-
#endif
|
|
213
|
+
#ifdef HAVE_RB_ISEQ_TYPE
|
|
214
|
+
rb_define_singleton_method(di_module, "iseq_type", iseq_type, 1);
|
|
215
|
+
#endif
|
|
213
216
|
}
|
|
217
|
+
|
|
218
|
+
#endif // TRUFFLERUBY
|
|
@@ -24,7 +24,7 @@ end
|
|
|
24
24
|
if ENV["DD_NO_EXTENSION"].to_s.strip.downcase == "true"
|
|
25
25
|
skip_building_extension!("the `DD_NO_EXTENSION` environment variable is/was set to `true` during installation")
|
|
26
26
|
end
|
|
27
|
-
skip_building_extension!("current Ruby VM is not supported")
|
|
27
|
+
skip_building_extension!("current Ruby VM is not supported") unless %w[ruby truffleruby].include?(RUBY_ENGINE)
|
|
28
28
|
skip_building_extension!("Microsoft Windows is not supported") if Gem.win_platform?
|
|
29
29
|
|
|
30
30
|
libdatadog_issue = Datadog::LibdatadogExtconfHelpers.load_libdatadog_or_get_issue
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
// Forward declarations
|
|
14
14
|
static VALUE configuration_new(VALUE klass, VALUE json_str);
|
|
15
15
|
static void configuration_free(void *ptr);
|
|
16
|
+
static VALUE configuration_get_observe_full_evaluation_data(VALUE self);
|
|
16
17
|
static VALUE configuration_get_assignment(
|
|
17
18
|
VALUE self, VALUE flag_key, VALUE expected_type, VALUE context);
|
|
18
19
|
|
|
@@ -94,6 +95,7 @@ void feature_flags_init(VALUE core_module) {
|
|
|
94
95
|
rb_undef_alloc_func(configuration_class);
|
|
95
96
|
rb_define_singleton_method(configuration_class, "new", configuration_new, 1);
|
|
96
97
|
rb_define_method(configuration_class, "get_assignment", configuration_get_assignment, 3);
|
|
98
|
+
rb_define_method(configuration_class, "observe_full_evaluation_data", configuration_get_observe_full_evaluation_data, 0);
|
|
97
99
|
|
|
98
100
|
rb_gc_register_address(&resolution_details_class);
|
|
99
101
|
resolution_details_class = rb_define_class_under(feature_flags_module, "ResolutionDetails", rb_cObject);
|
|
@@ -137,6 +139,12 @@ static VALUE configuration_new(VALUE klass, VALUE json_str) {
|
|
|
137
139
|
return TypedData_Wrap_Struct(klass, &configuration_data_type, result.ok);
|
|
138
140
|
}
|
|
139
141
|
|
|
142
|
+
static VALUE configuration_get_observe_full_evaluation_data(VALUE self) {
|
|
143
|
+
ddog_ffe_Handle_Configuration configuration =
|
|
144
|
+
(ddog_ffe_Handle_Configuration)rb_check_typeddata(self, &configuration_data_type);
|
|
145
|
+
return ddog_ffe_configuration_get_observe_full_evaluation_data(configuration) ? Qtrue : Qfalse;
|
|
146
|
+
}
|
|
147
|
+
|
|
140
148
|
static void configuration_free(void *ptr) {
|
|
141
149
|
ddog_ffe_Handle_Configuration config = (ddog_ffe_Handle_Configuration)ptr;
|
|
142
150
|
ddog_ffe_configuration_drop(&config);
|
data/ext/libdatadog_api/init.c
CHANGED
|
@@ -25,7 +25,9 @@ void DDTRACE_EXPORT Init_libdatadog_api(void) {
|
|
|
25
25
|
library_config_init(core_module);
|
|
26
26
|
ddsketch_init(core_module);
|
|
27
27
|
feature_flags_init(core_module);
|
|
28
|
-
|
|
28
|
+
#ifndef TRUFFLERUBY
|
|
29
|
+
di_init(datadog_module);
|
|
30
|
+
#endif
|
|
29
31
|
|
|
30
32
|
VALUE tracing_module = rb_define_module_under(datadog_module, "Tracing");
|
|
31
33
|
trace_exporter_init(tracing_module);
|
|
@@ -4,7 +4,12 @@
|
|
|
4
4
|
#include "datadog_ruby_common.h"
|
|
5
5
|
#include "otel_thread_context.h"
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
// Relies on a :fiber_switch TracePoint that TruffleRuby does not support yet
|
|
8
|
+
#if defined(__linux__) && !defined(TRUFFLERUBY)
|
|
9
|
+
#define OTEL_THREAD_CONTEXT_SUPPORTED
|
|
10
|
+
#endif
|
|
11
|
+
|
|
12
|
+
#ifdef OTEL_THREAD_CONTEXT_SUPPORTED
|
|
8
13
|
#include <datadog/otel-thread-ctx.h>
|
|
9
14
|
extern __thread const uint8_t *otel_thread_ctx_v1;
|
|
10
15
|
|
|
@@ -32,18 +37,18 @@ static VALUE native_read(VALUE _self);
|
|
|
32
37
|
void otel_thread_context_init(VALUE tracing_module) {
|
|
33
38
|
fiber_context_slot = rb_intern("__dd_otel_fiber_context");
|
|
34
39
|
|
|
35
|
-
VALUE
|
|
40
|
+
VALUE otel_thread_context_class = rb_define_class_under(tracing_module, "OTelThreadContext", rb_cObject);
|
|
36
41
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
rb_define_method(otel_thread_context_class, "_native_enable", native_enable, 0);
|
|
43
|
+
rb_define_method(otel_thread_context_class, "_native_set", native_set, 3);
|
|
44
|
+
rb_define_method(otel_thread_context_class, "_native_clear", native_clear, 0);
|
|
45
|
+
rb_define_method(otel_thread_context_class, "_native_supported?", native_supported_p, 0);
|
|
41
46
|
|
|
42
|
-
VALUE testing_module = rb_define_module_under(
|
|
47
|
+
VALUE testing_module = rb_define_module_under(otel_thread_context_class, "Testing");
|
|
43
48
|
rb_define_singleton_method(testing_module, "_native_read", native_read, 0);
|
|
44
49
|
}
|
|
45
50
|
|
|
46
|
-
#ifdef
|
|
51
|
+
#ifdef OTEL_THREAD_CONTEXT_SUPPORTED
|
|
47
52
|
static ddog_ThreadContextHandle *get_fiber_handle_for(VALUE thread) {
|
|
48
53
|
VALUE obj = rb_thread_local_aref(thread, fiber_context_slot);
|
|
49
54
|
|
|
@@ -56,6 +61,22 @@ void otel_thread_context_init(VALUE tracing_module) {
|
|
|
56
61
|
return get_fiber_handle_for(rb_thread_current());
|
|
57
62
|
}
|
|
58
63
|
|
|
64
|
+
static void pack_span_id(VALUE id, uint8_t bytes[8]) {
|
|
65
|
+
// Tracing::Utils.next_id caps generated span IDs at (1 << 62) - 1, which fits
|
|
66
|
+
// in a Fixnum on 64-bit Ruby. Packing them directly avoids rb_integer_pack's
|
|
67
|
+
// generic conversion and format checks on each context update.
|
|
68
|
+
// https://github.com/DataDog/dd-trace-rb/blob/0b10368e0a2940c1f4d50c9739b9973b4f8bc32a/lib/datadog/tracing/utils.rb#L20-L37
|
|
69
|
+
if (FIXNUM_P(id)) {
|
|
70
|
+
uint64_t number = (uint64_t) FIX2LONG(id);
|
|
71
|
+
for (int i = 7; i >= 0; i--) {
|
|
72
|
+
bytes[i] = (uint8_t) number;
|
|
73
|
+
number >>= 8;
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
rb_integer_pack(id, bytes, 1, sizeof(uint64_t), 0, INTEGER_PACK_BIG_ENDIAN);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
59
80
|
static void store_current_fiber_handle(ddog_ThreadContextHandle *handle) {
|
|
60
81
|
VALUE obj = TypedData_Wrap_Struct(rb_cObject, &otel_ctx_handle_t, handle);
|
|
61
82
|
rb_thread_local_aset(rb_thread_current(), fiber_context_slot, obj);
|
|
@@ -137,7 +158,7 @@ void otel_thread_context_init(VALUE tracing_module) {
|
|
|
137
158
|
#endif
|
|
138
159
|
|
|
139
160
|
static VALUE native_enable(DDTRACE_UNUSED VALUE _self) {
|
|
140
|
-
#ifdef
|
|
161
|
+
#ifdef OTEL_THREAD_CONTEXT_SUPPORTED
|
|
141
162
|
if (otel_context_enabled) return Qtrue;
|
|
142
163
|
otel_context_enabled = true;
|
|
143
164
|
|
|
@@ -163,7 +184,7 @@ static VALUE native_enable(DDTRACE_UNUSED VALUE _self) {
|
|
|
163
184
|
}
|
|
164
185
|
|
|
165
186
|
static VALUE native_supported_p(DDTRACE_UNUSED VALUE _self) {
|
|
166
|
-
#ifdef
|
|
187
|
+
#ifdef OTEL_THREAD_CONTEXT_SUPPORTED
|
|
167
188
|
return Qtrue;
|
|
168
189
|
#else
|
|
169
190
|
return Qfalse;
|
|
@@ -176,17 +197,17 @@ static VALUE native_set(
|
|
|
176
197
|
DDTRACE_UNUSED VALUE span_id,
|
|
177
198
|
DDTRACE_UNUSED VALUE local_root_span_id
|
|
178
199
|
) {
|
|
179
|
-
#ifdef
|
|
200
|
+
#ifdef OTEL_THREAD_CONTEXT_SUPPORTED
|
|
180
201
|
if (!otel_context_enabled) return Qfalse;
|
|
181
202
|
|
|
182
203
|
uint8_t trace_id_bytes[16];
|
|
183
204
|
uint8_t span_id_bytes[8];
|
|
184
205
|
uint8_t local_root_span_id_bytes[8];
|
|
185
206
|
|
|
186
|
-
|
|
187
|
-
rb_integer_pack(trace_id, trace_id_bytes, sizeof(
|
|
188
|
-
|
|
189
|
-
|
|
207
|
+
// Note: We use 2 words of size 8, as `rb_integer_pack` has a fast path for it (vs 16 bytes of size 1)
|
|
208
|
+
rb_integer_pack(trace_id, trace_id_bytes, 2, sizeof(uint64_t), 0, INTEGER_PACK_BIG_ENDIAN);
|
|
209
|
+
pack_span_id(span_id, span_id_bytes);
|
|
210
|
+
pack_span_id(local_root_span_id, local_root_span_id_bytes);
|
|
190
211
|
|
|
191
212
|
ddog_ThreadContextHandle *handle = get_current_fiber_handle();
|
|
192
213
|
|
|
@@ -207,7 +228,7 @@ static VALUE native_set(
|
|
|
207
228
|
}
|
|
208
229
|
|
|
209
230
|
static VALUE native_clear(DDTRACE_UNUSED VALUE _self) {
|
|
210
|
-
#ifdef
|
|
231
|
+
#ifdef OTEL_THREAD_CONTEXT_SUPPORTED
|
|
211
232
|
if (!otel_context_enabled) return Qfalse;
|
|
212
233
|
|
|
213
234
|
ddog_ThreadContextHandle *detached = ddog_otel_thread_ctx_detach();
|
|
@@ -220,7 +241,7 @@ static VALUE native_clear(DDTRACE_UNUSED VALUE _self) {
|
|
|
220
241
|
}
|
|
221
242
|
|
|
222
243
|
static VALUE native_read(DDTRACE_UNUSED VALUE _self) {
|
|
223
|
-
#ifdef
|
|
244
|
+
#ifdef OTEL_THREAD_CONTEXT_SUPPORTED
|
|
224
245
|
const uint8_t *raw = otel_thread_ctx_v1;
|
|
225
246
|
if (!raw) return Qnil;
|
|
226
247
|
|
|
@@ -34,6 +34,7 @@ static VALUE _native_from_span(VALUE klass, VALUE span);
|
|
|
34
34
|
|
|
35
35
|
/* TraceExporter methods */
|
|
36
36
|
static VALUE _native_exporter_new(int argc, VALUE *argv, VALUE klass);
|
|
37
|
+
static VALUE _native_close(VALUE self);
|
|
37
38
|
static VALUE _native_send_traces(VALUE self, VALUE traces, VALUE native_events_supported);
|
|
38
39
|
static VALUE _native_before_fork(VALUE self);
|
|
39
40
|
static VALUE _native_after_fork_in_parent(VALUE self);
|
|
@@ -1410,9 +1411,10 @@ static VALUE create_ok_response(long trace_count, VALUE payload) {
|
|
|
1410
1411
|
* TraceExporter._native_new(
|
|
1411
1412
|
* url:, tracer_version: nil, language: nil, language_version: nil,
|
|
1412
1413
|
* language_interpreter: nil, hostname: nil, env: nil,
|
|
1413
|
-
* service: nil, version: nil
|
|
1414
|
+
* service: nil, version: nil, client_computed_stats: false,
|
|
1415
|
+
* timeout_milliseconds: nil) -> TraceExporter
|
|
1414
1416
|
*
|
|
1415
|
-
* +url+ is required (String).
|
|
1417
|
+
* +url+ is required (String). Metadata arguments may be nil.
|
|
1416
1418
|
* ======================================================================== */
|
|
1417
1419
|
|
|
1418
1420
|
static VALUE _native_exporter_new(
|
|
@@ -1431,6 +1433,8 @@ static VALUE _native_exporter_new(
|
|
|
1431
1433
|
VALUE rb_env = rb_hash_fetch(options, ID2SYM(rb_intern("env")));
|
|
1432
1434
|
VALUE rb_service = rb_hash_fetch(options, ID2SYM(rb_intern("service")));
|
|
1433
1435
|
VALUE rb_version = rb_hash_fetch(options, ID2SYM(rb_intern("version")));
|
|
1436
|
+
VALUE rb_client_computed_stats = rb_hash_lookup2(options, ID2SYM(rb_intern("client_computed_stats")), Qfalse);
|
|
1437
|
+
VALUE rb_timeout_milliseconds = rb_hash_lookup2(options, ID2SYM(rb_intern("timeout_milliseconds")), Qnil);
|
|
1434
1438
|
|
|
1435
1439
|
/* Phase 1: validate types (may raise, no Rust resources yet) */
|
|
1436
1440
|
ENFORCE_TYPE(rb_url, T_STRING);
|
|
@@ -1442,6 +1446,20 @@ static VALUE _native_exporter_new(
|
|
|
1442
1446
|
if (rb_env != Qnil) ENFORCE_TYPE(rb_env, T_STRING);
|
|
1443
1447
|
if (rb_service != Qnil) ENFORCE_TYPE(rb_service, T_STRING);
|
|
1444
1448
|
if (rb_version != Qnil) ENFORCE_TYPE(rb_version, T_STRING);
|
|
1449
|
+
if (rb_client_computed_stats != Qtrue && rb_client_computed_stats != Qfalse) {
|
|
1450
|
+
rb_raise(rb_eTypeError, "client_computed_stats must be true or false");
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
long long timeout_milliseconds = 0;
|
|
1454
|
+
if (rb_timeout_milliseconds != Qnil) {
|
|
1455
|
+
if (!RB_TYPE_P(rb_timeout_milliseconds, T_FIXNUM) && !RB_TYPE_P(rb_timeout_milliseconds, T_BIGNUM)) {
|
|
1456
|
+
rb_raise(rb_eTypeError, "timeout_milliseconds must be an Integer or nil");
|
|
1457
|
+
}
|
|
1458
|
+
timeout_milliseconds = NUM2LL(rb_timeout_milliseconds);
|
|
1459
|
+
if (timeout_milliseconds < 0) {
|
|
1460
|
+
rb_raise(rb_eRangeError, "timeout_milliseconds must be non-negative");
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1445
1463
|
|
|
1446
1464
|
/* Phase 2: configure before creating the separately-owned runtime. */
|
|
1447
1465
|
ddog_TraceExporterConfig *config = NULL;
|
|
@@ -1457,6 +1475,22 @@ static VALUE _native_exporter_new(
|
|
|
1457
1475
|
set_config_field(config, ddog_trace_exporter_config_set_service, rb_service, "service");
|
|
1458
1476
|
set_config_field(config, ddog_trace_exporter_config_set_version, rb_version, "version");
|
|
1459
1477
|
|
|
1478
|
+
if (rb_timeout_milliseconds != Qnil) {
|
|
1479
|
+
ddog_TraceExporterError *timeout_err = ddog_trace_exporter_config_set_connection_timeout(
|
|
1480
|
+
config, (uint64_t)timeout_milliseconds);
|
|
1481
|
+
if (timeout_err != NULL) {
|
|
1482
|
+
ddog_trace_exporter_config_free(config);
|
|
1483
|
+
check_exporter_error("Failed to configure native transport timeout", timeout_err);
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
ddog_TraceExporterError *stats_err = ddog_trace_exporter_config_set_client_computed_stats(
|
|
1488
|
+
config, rb_client_computed_stats == Qtrue);
|
|
1489
|
+
if (stats_err != NULL) {
|
|
1490
|
+
ddog_trace_exporter_config_free(config);
|
|
1491
|
+
check_exporter_error("Failed to configure client-computed stats", stats_err);
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1460
1494
|
/*
|
|
1461
1495
|
* Create a SharedRuntime and attach it to the config before building the
|
|
1462
1496
|
* exporter. The exporter holds a clone of the runtime's Arc; we keep our
|
|
@@ -1511,12 +1545,47 @@ static VALUE _native_exporter_new(
|
|
|
1511
1545
|
* (Puma, Unicorn, Passenger).
|
|
1512
1546
|
* ======================================================================== */
|
|
1513
1547
|
|
|
1548
|
+
static void *close_exporter_without_gvl(void *ptr) {
|
|
1549
|
+
trace_exporter_t *resources = ptr;
|
|
1550
|
+
if (resources->exporter != NULL) {
|
|
1551
|
+
ddog_trace_exporter_free(resources->exporter);
|
|
1552
|
+
resources->exporter = NULL;
|
|
1553
|
+
}
|
|
1554
|
+
if (resources->runtime != NULL) {
|
|
1555
|
+
ddog_shared_runtime_free(resources->runtime);
|
|
1556
|
+
resources->runtime = NULL;
|
|
1557
|
+
}
|
|
1558
|
+
return NULL;
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
static VALUE close_exporter_body(VALUE ptr) {
|
|
1562
|
+
rb_thread_call_without_gvl(close_exporter_without_gvl, (void *)ptr, NULL, NULL);
|
|
1563
|
+
return Qnil;
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
static VALUE close_exporter_ensure(VALUE ptr) {
|
|
1567
|
+
/* An interrupt may arrive before the no-GVL call starts. */
|
|
1568
|
+
close_exporter_without_gvl((void *)ptr);
|
|
1569
|
+
return Qnil;
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
static VALUE _native_close(VALUE self) {
|
|
1573
|
+
trace_exporter_t *wrapper;
|
|
1574
|
+
TypedData_Get_Struct(self, trace_exporter_t, &trace_exporter_typed_data, wrapper);
|
|
1575
|
+
if (wrapper == NULL) return Qnil;
|
|
1576
|
+
|
|
1577
|
+
trace_exporter_t resources = *wrapper;
|
|
1578
|
+
wrapper->exporter = NULL;
|
|
1579
|
+
wrapper->runtime = NULL;
|
|
1580
|
+
rb_ensure(close_exporter_body, (VALUE)&resources, close_exporter_ensure, (VALUE)&resources);
|
|
1581
|
+
RB_GC_GUARD(self);
|
|
1582
|
+
return Qnil;
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1514
1585
|
static VALUE _native_before_fork(VALUE self) {
|
|
1515
1586
|
trace_exporter_t *wrapper;
|
|
1516
1587
|
TypedData_Get_Struct(self, trace_exporter_t, &trace_exporter_typed_data, wrapper);
|
|
1517
|
-
if (wrapper == NULL || wrapper->runtime == NULL)
|
|
1518
|
-
raise_error(rb_eRuntimeError, "TraceExporter has not been initialized or was already freed");
|
|
1519
|
-
}
|
|
1588
|
+
if (wrapper == NULL || wrapper->runtime == NULL) return Qnil;
|
|
1520
1589
|
ddog_SharedRuntimeFFIError *err = ddog_shared_runtime_before_fork(wrapper->runtime);
|
|
1521
1590
|
check_shared_runtime_error("Failed to prepare for fork", err);
|
|
1522
1591
|
return Qnil;
|
|
@@ -1525,9 +1594,7 @@ static VALUE _native_before_fork(VALUE self) {
|
|
|
1525
1594
|
static VALUE _native_after_fork_in_parent(VALUE self) {
|
|
1526
1595
|
trace_exporter_t *wrapper;
|
|
1527
1596
|
TypedData_Get_Struct(self, trace_exporter_t, &trace_exporter_typed_data, wrapper);
|
|
1528
|
-
if (wrapper == NULL || wrapper->runtime == NULL)
|
|
1529
|
-
raise_error(rb_eRuntimeError, "TraceExporter has not been initialized or was already freed");
|
|
1530
|
-
}
|
|
1597
|
+
if (wrapper == NULL || wrapper->runtime == NULL) return Qnil;
|
|
1531
1598
|
ddog_SharedRuntimeFFIError *err = ddog_shared_runtime_after_fork_parent(wrapper->runtime);
|
|
1532
1599
|
check_shared_runtime_error("Failed to restore after fork in parent", err);
|
|
1533
1600
|
return Qnil;
|
|
@@ -1536,9 +1603,7 @@ static VALUE _native_after_fork_in_parent(VALUE self) {
|
|
|
1536
1603
|
static VALUE _native_after_fork_in_child(VALUE self) {
|
|
1537
1604
|
trace_exporter_t *wrapper;
|
|
1538
1605
|
TypedData_Get_Struct(self, trace_exporter_t, &trace_exporter_typed_data, wrapper);
|
|
1539
|
-
if (wrapper == NULL || wrapper->runtime == NULL)
|
|
1540
|
-
raise_error(rb_eRuntimeError, "TraceExporter has not been initialized or was already freed");
|
|
1541
|
-
}
|
|
1606
|
+
if (wrapper == NULL || wrapper->runtime == NULL) return Qnil;
|
|
1542
1607
|
ddog_SharedRuntimeFFIError *err = ddog_shared_runtime_after_fork_child(wrapper->runtime);
|
|
1543
1608
|
check_shared_runtime_error("Failed to restore after fork in child", err);
|
|
1544
1609
|
return Qnil;
|
|
@@ -1848,6 +1913,7 @@ void trace_exporter_init(VALUE tracing_module) {
|
|
|
1848
1913
|
/* Instance: _native_send_traces(traces, native_events_supported) -> Array[Response] */
|
|
1849
1914
|
rb_define_method(trace_exporter_class, "_native_send_traces",
|
|
1850
1915
|
_native_send_traces, 2);
|
|
1916
|
+
rb_define_method(trace_exporter_class, "_native_close", _native_close, 0);
|
|
1851
1917
|
|
|
1852
1918
|
/* Instance: fork safety hooks */
|
|
1853
1919
|
rb_define_method(trace_exporter_class, "_native_before_fork",
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "rubygems"
|
|
4
4
|
require "pathname"
|
|
5
|
+
require "shellwords"
|
|
5
6
|
|
|
6
7
|
module Datadog
|
|
7
8
|
# Contains a bunch of shared helpers that get used during building of extensions that link to libdatadog
|
|
@@ -10,7 +11,7 @@ module Datadog
|
|
|
10
11
|
module LibdatadogExtconfHelpers
|
|
11
12
|
# Used to make sure the correct gem version gets loaded, as extconf.rb does not get run with "bundle exec" and thus
|
|
12
13
|
# may see multiple libdatadog versions. See https://github.com/DataDog/dd-trace-rb/pull/2531 for the horror story.
|
|
13
|
-
LIBDATADOG_VERSION = "~>
|
|
14
|
+
LIBDATADOG_VERSION = "~> 45.0.0.1.0"
|
|
14
15
|
|
|
15
16
|
# Used as an workaround for a limitation with how dynamic linking works in environments where the datadog gem and
|
|
16
17
|
# libdatadog are moved after the extension gets compiled.
|
|
@@ -116,7 +117,10 @@ module Datadog
|
|
|
116
117
|
extconf_folder:,
|
|
117
118
|
libdatadog_pkgconfig_folder: Libdatadog.pkgconfig_folder,
|
|
118
119
|
gem_dir: Gem.dir,
|
|
119
|
-
logger: Logging
|
|
120
|
+
logger: Logging,
|
|
121
|
+
target_incflags: $INCFLAGS,
|
|
122
|
+
target_ldflags: $LDFLAGS,
|
|
123
|
+
target_libs: $libs
|
|
120
124
|
)
|
|
121
125
|
return unless libdatadog_pkgconfig_folder
|
|
122
126
|
|
|
@@ -124,10 +128,9 @@ module Datadog
|
|
|
124
128
|
libdir = "#{libdatadog_pkgconfig_folder}/../../lib"
|
|
125
129
|
includedir = "#{libdatadog_pkgconfig_folder}/../../include"
|
|
126
130
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
$libs << " -ldatadog_profiling"
|
|
131
|
+
target_incflags << " -I#{Shellwords.escape(includedir)}"
|
|
132
|
+
target_ldflags << " -L#{Shellwords.escape(libdir)} -Wl,-rpath,#{Shellwords.escape(libdir)}"
|
|
133
|
+
target_libs << " -ldatadog_profiling"
|
|
131
134
|
|
|
132
135
|
# Add extra relative rpaths using $ORIGIN to handle environments where gems are moved after installation.
|
|
133
136
|
# The excessive escaping is needed to get these special characters through Make and the shell untouched.
|
|
@@ -141,10 +144,12 @@ module Datadog
|
|
|
141
144
|
libdatadog_pkgconfig_folder: libdatadog_pkgconfig_folder,
|
|
142
145
|
),
|
|
143
146
|
]
|
|
144
|
-
extra_relative_rpaths.each { |folder|
|
|
147
|
+
extra_relative_rpaths.each { |folder| target_ldflags << " -Wl,-rpath,$$$\\\\{ORIGIN\\}/#{Shellwords.escape(folder)}" }
|
|
145
148
|
|
|
146
|
-
logger.message
|
|
147
|
-
|
|
149
|
+
# includedir/libdir/ldflags might have a `%` in there and `logger.message` bottoms out on `printf` so we need to
|
|
150
|
+
# use the `message("%s", ...)` format otherwise the logger will complain
|
|
151
|
+
logger.message("%s", "linking with libdatadog (include=#{includedir}, lib=#{libdir})\n")
|
|
152
|
+
logger.message("%s", "[datadog] $LDFLAGS were set to: #{target_ldflags.inspect}\n")
|
|
148
153
|
|
|
149
154
|
true
|
|
150
155
|
end
|
|
@@ -1,37 +1,46 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
require_relative "ext"
|
|
6
|
+
require_relative "http_client"
|
|
4
7
|
require_relative "evaluation"
|
|
5
8
|
require_relative "evaluation/request"
|
|
9
|
+
require_relative "evaluation/response"
|
|
10
|
+
require_relative "evaluation/client"
|
|
6
11
|
require_relative "evaluation/result"
|
|
12
|
+
require_relative "evaluation/outcome"
|
|
7
13
|
require_relative "evaluation/no_op_result"
|
|
8
14
|
require_relative "evaluation/message"
|
|
9
15
|
require_relative "evaluation/tool_call"
|
|
10
16
|
require_relative "evaluation/content_part"
|
|
11
|
-
require_relative "evaluation/
|
|
12
|
-
require_relative "
|
|
17
|
+
require_relative "evaluation/message_builder"
|
|
18
|
+
require_relative "redaction"
|
|
19
|
+
require_relative "redaction/result"
|
|
20
|
+
require_relative "redaction/replacements"
|
|
21
|
+
require_relative "metrics/telemetry"
|
|
13
22
|
|
|
14
23
|
module Datadog
|
|
15
24
|
module AIGuard
|
|
16
25
|
# Component for API Guard product
|
|
17
26
|
class Component
|
|
18
|
-
attr_reader :
|
|
27
|
+
attr_reader :http_client, :logger, :telemetry
|
|
19
28
|
|
|
20
29
|
def self.build(settings, logger:, telemetry:)
|
|
21
30
|
return unless settings.respond_to?(:ai_guard) && settings.ai_guard.enabled
|
|
22
31
|
|
|
23
|
-
|
|
32
|
+
http_client = HTTPClient.new(
|
|
24
33
|
endpoint: settings.ai_guard.endpoint,
|
|
25
34
|
api_key: settings.api_key,
|
|
26
35
|
application_key: settings.ai_guard.app_key,
|
|
27
36
|
timeout: settings.ai_guard.timeout_ms / 1_000
|
|
28
37
|
)
|
|
29
38
|
|
|
30
|
-
new(
|
|
39
|
+
new(http_client, logger: logger, telemetry: telemetry)
|
|
31
40
|
end
|
|
32
41
|
|
|
33
|
-
def initialize(
|
|
34
|
-
@
|
|
42
|
+
def initialize(http_client, logger:, telemetry:)
|
|
43
|
+
@http_client = http_client
|
|
35
44
|
@logger = logger
|
|
36
45
|
@telemetry = telemetry
|
|
37
46
|
end
|
|
@@ -6,6 +6,8 @@ module Datadog
|
|
|
6
6
|
# This module contains constants for AI Guard component
|
|
7
7
|
module Ext
|
|
8
8
|
ENV_AI_GUARD_ENABLED = "DD_AI_GUARD_ENABLED"
|
|
9
|
+
ENV_AI_GUARD_REDACTION_ENABLED = "DD_AI_GUARD_REDACTION_ENABLED"
|
|
10
|
+
|
|
9
11
|
ENV_AI_GUARD_ENDPOINT = "DD_AI_GUARD_ENDPOINT"
|
|
10
12
|
ENV_AI_GUARD_TIMEOUT = "DD_AI_GUARD_TIMEOUT"
|
|
11
13
|
ENV_AI_GUARD_MAX_CONTENT_SIZE = "DD_AI_GUARD_MAX_CONTENT_SIZE"
|
|
@@ -34,6 +34,19 @@ module Datadog
|
|
|
34
34
|
o.default false
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
+
# Redact sensitive data from evaluated messages.
|
|
38
|
+
#
|
|
39
|
+
# When enabled, values flagged by AI Guard are replaced before the messages are
|
|
40
|
+
# stored in the span or returned to the caller. Disable to skip redaction entirely.
|
|
41
|
+
#
|
|
42
|
+
# @default `DD_AI_GUARD_REDACTION_ENABLED`, otherwise `true`
|
|
43
|
+
# @return [Boolean]
|
|
44
|
+
option :redaction_enabled do |o|
|
|
45
|
+
o.type :bool
|
|
46
|
+
o.env Ext::ENV_AI_GUARD_REDACTION_ENABLED
|
|
47
|
+
o.default true
|
|
48
|
+
end
|
|
49
|
+
|
|
37
50
|
define_method(:instrument) do |integration_name|
|
|
38
51
|
return unless enabled # steep:ignore
|
|
39
52
|
|