datadog 2.44.0 → 2.46.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 +50 -2
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +114 -141
- 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 +13 -13
- data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.h +5 -1
- data/ext/datadog_profiling_native_extension/collectors_stack.c +1 -0
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +106 -103
- data/ext/datadog_profiling_native_extension/collectors_thread_context.h +3 -3
- data/ext/datadog_profiling_native_extension/extconf.rb +2 -0
- data/ext/datadog_profiling_native_extension/heap_recorder.c +2 -2
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +102 -54
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +7 -2
- data/ext/datadog_profiling_native_extension/profiling.c +27 -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 +6 -24
- 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 +10 -11
- data/ext/libdatadog_api/di.c +30 -5
- data/ext/libdatadog_api/extconf.rb +2 -0
- data/ext/libdatadog_api/otel_thread_context.c +20 -14
- data/ext/libdatadog_api/process_discovery.c +15 -1
- data/ext/libdatadog_api/trace_exporter.c +182 -36
- data/ext/libdatadog_extconf_helpers.rb +1 -1
- data/lib/datadog/core/configuration/components.rb +29 -8
- data/lib/datadog/core/configuration/supported_configurations.rb +5 -2
- data/lib/datadog/core/process_discovery.rb +5 -1
- data/lib/datadog/core/remote/client/capabilities.rb +34 -29
- data/lib/datadog/core/remote/component.rb +28 -12
- data/lib/datadog/core/remote/dispatcher.rb +13 -3
- data/lib/datadog/di/boot.rb +1 -0
- data/lib/datadog/di/capture_expression_evaluator/result.rb +30 -0
- data/lib/datadog/di/capture_expression_evaluator.rb +39 -9
- data/lib/datadog/di/component.rb +38 -3
- data/lib/datadog/di/configuration.rb +53 -2
- data/lib/datadog/di/context.rb +7 -2
- data/lib/datadog/di/contrib/active_record.rb +8 -2
- data/lib/datadog/di/evaluation_failure.rb +28 -0
- data/lib/datadog/di/guardrails_telemetry/reason.rb +87 -0
- data/lib/datadog/di/guardrails_telemetry.rb +203 -0
- data/lib/datadog/di/instrumenter.rb +65 -15
- data/lib/datadog/di/logger.rb +30 -8
- data/lib/datadog/di/probe_manager.rb +48 -5
- data/lib/datadog/di/probe_notification_builder.rb +28 -26
- data/lib/datadog/di/probe_notifier_worker.rb +56 -6
- data/lib/datadog/di/serializer.rb +130 -54
- data/lib/datadog/di/snapshot_event.rb +38 -0
- data/lib/datadog/di/transport/http.rb +13 -3
- data/lib/datadog/di/transport/input.rb +32 -7
- data/lib/datadog/open_feature/activation.rb +268 -0
- data/lib/datadog/open_feature/component.rb +31 -15
- data/lib/datadog/open_feature/provider.rb +282 -5
- data/lib/datadog/open_feature.rb +31 -1
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +5 -0
- data/lib/datadog/profiling/component.rb +2 -3
- data/lib/datadog/profiling/exporter.rb +7 -5
- data/lib/datadog/tracing/component.rb +15 -1
- data/lib/datadog/tracing/configuration/settings.rb +6 -3
- 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/otel_thread_context.rb +3 -5
- data/lib/datadog/tracing/transport/http/telemetry.rb +94 -0
- data/lib/datadog/tracing/transport/http/traces.rb +2 -0
- data/lib/datadog/tracing/transport/native/telemetry.rb +17 -0
- data/lib/datadog/tracing/transport/native.rb +38 -22
- data/lib/datadog/tracing/transport/telemetry.rb +62 -0
- data/lib/datadog/tracing/transport/traces.rb +82 -15
- data/lib/datadog/version.rb +1 -1
- metadata +16 -7
data/ext/libdatadog_api/di.c
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#include <stdbool.h>
|
|
2
|
+
#include <string.h>
|
|
2
3
|
|
|
3
4
|
#include "datadog_ruby_common.h"
|
|
4
5
|
|
|
@@ -12,15 +13,19 @@ VALUE rb_iseqw_new(const void *iseq);
|
|
|
12
13
|
// rb_iseqw_to_iseq unwraps a RubyVM::InstructionSequence object back
|
|
13
14
|
// to its internal iseq pointer.
|
|
14
15
|
const void *rb_iseqw_to_iseq(VALUE iseqw);
|
|
16
|
+
#ifdef HAVE_RB_IMEMO_NAME
|
|
17
|
+
const char *rb_imemo_name(int imemo_type);
|
|
18
|
+
#endif
|
|
15
19
|
int rb_objspace_internal_object_p(VALUE obj);
|
|
16
20
|
void rb_objspace_each_objects(
|
|
17
21
|
int (*callback)(void *start, void *end, size_t stride, void *data),
|
|
18
22
|
void *data);
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
#define IMEMO_MASK 0x0f
|
|
24
|
+
#ifdef HAVE_SHIFTED_IMEMO_MASK // Ruby 4.1+
|
|
25
|
+
#define IMEMO_MASK (FL_USER0 | FL_USER1 | FL_USER2 | FL_USER3 | FL_USER4)
|
|
26
|
+
#else
|
|
27
|
+
#define IMEMO_MASK 0x0f
|
|
28
|
+
#endif
|
|
24
29
|
#define IMEMO_TYPE_ISEQ 7
|
|
25
30
|
|
|
26
31
|
// The ID value of the string "mesg" which is used in Ruby source as
|
|
@@ -37,7 +42,11 @@ static ID id_datadog_di_in_probe;
|
|
|
37
42
|
// Returns the imemo type of an imemo object, that is `imemo_type()` from CRuby's internal/imemo.h.
|
|
38
43
|
// The caller must have already checked the object is a T_IMEMO, otherwise the result is meaningless.
|
|
39
44
|
static inline int ddtrace_imemo_type(VALUE imemo) {
|
|
40
|
-
|
|
45
|
+
#ifdef HAVE_SHIFTED_IMEMO_MASK // Ruby 4.1+
|
|
46
|
+
return (RBASIC(imemo)->flags & IMEMO_MASK) >> FL_USHIFT;
|
|
47
|
+
#else
|
|
48
|
+
return (RBASIC(imemo)->flags >> FL_USHIFT) & IMEMO_MASK;
|
|
49
|
+
#endif
|
|
41
50
|
}
|
|
42
51
|
|
|
43
52
|
// Returns whether the argument is an IMEMO of type ISEQ.
|
|
@@ -199,7 +208,20 @@ static VALUE iseq_type(DDTRACE_UNUSED VALUE _self, VALUE iseq_val) {
|
|
|
199
208
|
}
|
|
200
209
|
#endif
|
|
201
210
|
|
|
211
|
+
static VALUE _native_imemo_mask(DDTRACE_UNUSED VALUE _self) {
|
|
212
|
+
return INT2NUM(IMEMO_MASK);
|
|
213
|
+
}
|
|
214
|
+
|
|
202
215
|
void di_init(VALUE datadog_module) {
|
|
216
|
+
#ifdef HAVE_RB_IMEMO_NAME
|
|
217
|
+
const char *iseq_name = rb_imemo_name(IMEMO_TYPE_ISEQ);
|
|
218
|
+
if (strcmp(iseq_name, "iseq") != 0) {
|
|
219
|
+
rb_raise(rb_eRuntimeError,
|
|
220
|
+
"IMEMO_TYPE_ISEQ self-test failed: rb_imemo_name(%d) returned '%s' instead of 'iseq'",
|
|
221
|
+
IMEMO_TYPE_ISEQ, iseq_name);
|
|
222
|
+
}
|
|
223
|
+
#endif
|
|
224
|
+
|
|
203
225
|
id_mesg = rb_intern("mesg");
|
|
204
226
|
id_datadog_di_in_probe = rb_intern("datadog_di_in_probe");
|
|
205
227
|
|
|
@@ -213,6 +235,9 @@ void di_init(VALUE datadog_module) {
|
|
|
213
235
|
#ifdef HAVE_RB_ISEQ_TYPE
|
|
214
236
|
rb_define_singleton_method(di_module, "iseq_type", iseq_type, 1);
|
|
215
237
|
#endif
|
|
238
|
+
|
|
239
|
+
VALUE testing_module = rb_define_module_under(di_module, "Testing");
|
|
240
|
+
rb_define_singleton_method(testing_module, "_native_imemo_mask", _native_imemo_mask, 0);
|
|
216
241
|
}
|
|
217
242
|
|
|
218
243
|
#endif // TRUFFLERUBY
|
|
@@ -98,6 +98,8 @@ EXTENSION_NAME = "libdatadog_api.#{RUBY_VERSION[/\d+.\d+/]}_#{RUBY_PLATFORM}".fr
|
|
|
98
98
|
|
|
99
99
|
have_func("rb_iseq_type")
|
|
100
100
|
|
|
101
|
+
$defs << "-DHAVE_RB_IMEMO_NAME" if RUBY_VERSION >= "3.0"
|
|
102
|
+
$defs << "-DHAVE_SHIFTED_IMEMO_MASK" if RUBY_VERSION >= "4.1"
|
|
101
103
|
$defs << "-DHAVE_RUBY_THREAD_STORAGE_API" if RUBY_VERSION >= "3.3"
|
|
102
104
|
$defs << "-DHAVE_RUBY_THREAD_HAS_GVL_P" if have_func("ruby_thread_has_gvl_p")
|
|
103
105
|
|
|
@@ -30,7 +30,6 @@ DDTRACE_UNUSED static bool otel_context_enabled = false;
|
|
|
30
30
|
|
|
31
31
|
static VALUE native_set(VALUE _self, VALUE trace_id, VALUE span_id, VALUE local_root_span_id);
|
|
32
32
|
static VALUE native_clear(VALUE _self);
|
|
33
|
-
static VALUE native_supported_p(VALUE _self);
|
|
34
33
|
static VALUE native_enable(VALUE _self);
|
|
35
34
|
static VALUE native_read(VALUE _self);
|
|
36
35
|
|
|
@@ -42,7 +41,6 @@ void otel_thread_context_init(VALUE tracing_module) {
|
|
|
42
41
|
rb_define_method(otel_thread_context_class, "_native_enable", native_enable, 0);
|
|
43
42
|
rb_define_method(otel_thread_context_class, "_native_set", native_set, 3);
|
|
44
43
|
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);
|
|
46
44
|
|
|
47
45
|
VALUE testing_module = rb_define_module_under(otel_thread_context_class, "Testing");
|
|
48
46
|
rb_define_singleton_method(testing_module, "_native_read", native_read, 0);
|
|
@@ -100,6 +98,19 @@ void otel_thread_context_init(VALUE tracing_module) {
|
|
|
100
98
|
) {
|
|
101
99
|
ddog_otel_thread_ctx_detach();
|
|
102
100
|
}
|
|
101
|
+
|
|
102
|
+
// CRuby fires RUBY_EVENT_THREAD_END only after the thread body returns,
|
|
103
|
+
// and Thread#kill unwinds the body via EC_JUMP_TAG(TAG_FATAL):
|
|
104
|
+
// https://github.com/ruby/ruby/blob/v3_1_4/thread.c#L744-L775
|
|
105
|
+
static void on_thread_begin(
|
|
106
|
+
DDTRACE_UNUSED rb_event_flag_t evflag,
|
|
107
|
+
DDTRACE_UNUSED VALUE data,
|
|
108
|
+
DDTRACE_UNUSED VALUE self,
|
|
109
|
+
DDTRACE_UNUSED ID mid,
|
|
110
|
+
DDTRACE_UNUSED VALUE klass
|
|
111
|
+
) {
|
|
112
|
+
ddog_otel_thread_ctx_detach();
|
|
113
|
+
}
|
|
103
114
|
#endif
|
|
104
115
|
|
|
105
116
|
#ifdef HAVE_RUBY_THREAD_STORAGE_API
|
|
@@ -166,6 +177,11 @@ static VALUE native_enable(DDTRACE_UNUSED VALUE _self) {
|
|
|
166
177
|
rb_internal_thread_add_event_hook(on_thread_exited, RUBY_INTERNAL_THREAD_EVENT_EXITED, NULL);
|
|
167
178
|
#else
|
|
168
179
|
rb_add_event_hook(on_thread_end, RUBY_EVENT_THREAD_END, Qnil);
|
|
180
|
+
// RUBY_EVENT_THREAD_END does not fire for threads terminated via Thread#kill
|
|
181
|
+
// on Ruby < 3.2, so a killed thread's otel_thread_ctx_v1 TLS stays attached
|
|
182
|
+
// on a native thread CRuby may recycle for a later Ruby thread. Detach on
|
|
183
|
+
// thread begin to drop that stale record before the new thread runs.
|
|
184
|
+
rb_add_event_hook(on_thread_begin, RUBY_EVENT_THREAD_BEGIN, Qnil);
|
|
169
185
|
#endif
|
|
170
186
|
|
|
171
187
|
// These hooks only matter under the M:N thread scheduler (Ruby 3.3+),
|
|
@@ -179,15 +195,7 @@ static VALUE native_enable(DDTRACE_UNUSED VALUE _self) {
|
|
|
179
195
|
|
|
180
196
|
return Qtrue;
|
|
181
197
|
#else
|
|
182
|
-
|
|
183
|
-
#endif
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
static VALUE native_supported_p(DDTRACE_UNUSED VALUE _self) {
|
|
187
|
-
#ifdef OTEL_THREAD_CONTEXT_SUPPORTED
|
|
188
|
-
return Qtrue;
|
|
189
|
-
#else
|
|
190
|
-
return Qfalse;
|
|
198
|
+
raise_error(rb_eRuntimeError, "OTel thread context is not supported on this platform");
|
|
191
199
|
#endif
|
|
192
200
|
}
|
|
193
201
|
|
|
@@ -216,9 +224,7 @@ static VALUE native_set(
|
|
|
216
224
|
store_current_fiber_handle(handle);
|
|
217
225
|
ddog_otel_thread_ctx_attach(handle);
|
|
218
226
|
} else {
|
|
219
|
-
|
|
220
|
-
ddog_otel_thread_ctx_attach(handle);
|
|
221
|
-
ddog_otel_thread_ctx_update(&trace_id_bytes, &span_id_bytes, 0, &local_root_span_id_bytes);
|
|
227
|
+
ddog_otel_thread_ctx_update_and_attach(handle, &trace_id_bytes, &span_id_bytes, 0, &local_root_span_id_bytes);
|
|
222
228
|
}
|
|
223
229
|
|
|
224
230
|
return Qtrue;
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
#include <datadog/library-config.h>
|
|
5
5
|
|
|
6
6
|
#include "datadog_ruby_common.h"
|
|
7
|
+
#include "helpers.h"
|
|
7
8
|
|
|
8
9
|
static VALUE _native_store_tracer_metadata(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _self);
|
|
9
10
|
static VALUE _native_to_rb_int(DDTRACE_UNUSED VALUE _self, VALUE tracer_memfd);
|
|
@@ -38,8 +39,9 @@ void process_discovery_init(VALUE core_module) {
|
|
|
38
39
|
|
|
39
40
|
static VALUE _native_store_tracer_metadata(int argc, VALUE *argv, VALUE self) {
|
|
40
41
|
VALUE logger;
|
|
42
|
+
VALUE otel_thread_context_enabled;
|
|
41
43
|
VALUE options;
|
|
42
|
-
rb_scan_args(argc, argv, "
|
|
44
|
+
rb_scan_args(argc, argv, "2:", &logger, &otel_thread_context_enabled, &options);
|
|
43
45
|
if (options == Qnil) options = rb_hash_new();
|
|
44
46
|
|
|
45
47
|
VALUE runtime_id = rb_hash_fetch(options, ID2SYM(rb_intern("runtime_id")));
|
|
@@ -61,6 +63,7 @@ static VALUE _native_store_tracer_metadata(int argc, VALUE *argv, VALUE self) {
|
|
|
61
63
|
ENFORCE_TYPE(service_version, T_STRING);
|
|
62
64
|
ENFORCE_TYPE(process_tags, T_STRING);
|
|
63
65
|
ENFORCE_TYPE(container_id, T_STRING);
|
|
66
|
+
ENFORCE_BOOLEAN(otel_thread_context_enabled);
|
|
64
67
|
|
|
65
68
|
void* builder = ddog_tracer_metadata_new();
|
|
66
69
|
|
|
@@ -74,6 +77,16 @@ static VALUE _native_store_tracer_metadata(int argc, VALUE *argv, VALUE self) {
|
|
|
74
77
|
ddog_tracer_metadata_set(builder, DDOG_METADATA_KIND_PROCESS_TAGS, StringValueCStr(process_tags));
|
|
75
78
|
ddog_tracer_metadata_set(builder, DDOG_METADATA_KIND_CONTAINER_ID, StringValueCStr(container_id));
|
|
76
79
|
|
|
80
|
+
if (RTEST(otel_thread_context_enabled)) {
|
|
81
|
+
ddog_VoidResult include_result = ddog_tracer_metadata_include_otel_thread_context(builder);
|
|
82
|
+
|
|
83
|
+
if (include_result.tag == DDOG_VOID_RESULT_ERR) {
|
|
84
|
+
ddog_tracer_metadata_free(builder);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
CHECK_VOID_RESULT("Failed to include OTel thread context metadata", include_result);
|
|
88
|
+
}
|
|
89
|
+
|
|
77
90
|
ddog_Result_TracerMemfdHandle result = ddog_tracer_metadata_store(builder);
|
|
78
91
|
ddog_tracer_metadata_free(builder);
|
|
79
92
|
|
|
@@ -89,6 +102,7 @@ static VALUE _native_store_tracer_metadata(int argc, VALUE *argv, VALUE self) {
|
|
|
89
102
|
*fd = result.ok.fd;
|
|
90
103
|
VALUE tracer_memfd_class = rb_const_get(self, rb_intern("TracerMemfd"));
|
|
91
104
|
VALUE tracer_memfd = TypedData_Wrap_Struct(tracer_memfd_class, &tracer_memfd_type, fd);
|
|
105
|
+
|
|
92
106
|
return tracer_memfd;
|
|
93
107
|
}
|
|
94
108
|
|
|
@@ -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);
|
|
@@ -103,6 +104,8 @@ static VALUE serializable_backtrace_class = Qnil;
|
|
|
103
104
|
/* Response class (loaded from Ruby) */
|
|
104
105
|
static VALUE response_class = Qnil;
|
|
105
106
|
static ID id_new;
|
|
107
|
+
static ID id_handle_interrupt;
|
|
108
|
+
static VALUE observation_interrupt_mask = Qnil;
|
|
106
109
|
|
|
107
110
|
/* Response keyword-argument IDs, cached to avoid re-interning per send */
|
|
108
111
|
static ID kw_ok;
|
|
@@ -1410,9 +1413,10 @@ static VALUE create_ok_response(long trace_count, VALUE payload) {
|
|
|
1410
1413
|
* TraceExporter._native_new(
|
|
1411
1414
|
* url:, tracer_version: nil, language: nil, language_version: nil,
|
|
1412
1415
|
* language_interpreter: nil, hostname: nil, env: nil,
|
|
1413
|
-
* service: nil, version: nil
|
|
1416
|
+
* service: nil, version: nil, client_computed_stats: false,
|
|
1417
|
+
* timeout_milliseconds: nil) -> TraceExporter
|
|
1414
1418
|
*
|
|
1415
|
-
* +url+ is required (String).
|
|
1419
|
+
* +url+ is required (String). Metadata arguments may be nil.
|
|
1416
1420
|
* ======================================================================== */
|
|
1417
1421
|
|
|
1418
1422
|
static VALUE _native_exporter_new(
|
|
@@ -1431,6 +1435,8 @@ static VALUE _native_exporter_new(
|
|
|
1431
1435
|
VALUE rb_env = rb_hash_fetch(options, ID2SYM(rb_intern("env")));
|
|
1432
1436
|
VALUE rb_service = rb_hash_fetch(options, ID2SYM(rb_intern("service")));
|
|
1433
1437
|
VALUE rb_version = rb_hash_fetch(options, ID2SYM(rb_intern("version")));
|
|
1438
|
+
VALUE rb_client_computed_stats = rb_hash_lookup2(options, ID2SYM(rb_intern("client_computed_stats")), Qfalse);
|
|
1439
|
+
VALUE rb_timeout_milliseconds = rb_hash_lookup2(options, ID2SYM(rb_intern("timeout_milliseconds")), Qnil);
|
|
1434
1440
|
|
|
1435
1441
|
/* Phase 1: validate types (may raise, no Rust resources yet) */
|
|
1436
1442
|
ENFORCE_TYPE(rb_url, T_STRING);
|
|
@@ -1442,10 +1448,29 @@ static VALUE _native_exporter_new(
|
|
|
1442
1448
|
if (rb_env != Qnil) ENFORCE_TYPE(rb_env, T_STRING);
|
|
1443
1449
|
if (rb_service != Qnil) ENFORCE_TYPE(rb_service, T_STRING);
|
|
1444
1450
|
if (rb_version != Qnil) ENFORCE_TYPE(rb_version, T_STRING);
|
|
1451
|
+
if (rb_client_computed_stats != Qtrue && rb_client_computed_stats != Qfalse) {
|
|
1452
|
+
rb_raise(rb_eTypeError, "client_computed_stats must be true or false");
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
long long timeout_milliseconds = 0;
|
|
1456
|
+
if (rb_timeout_milliseconds != Qnil) {
|
|
1457
|
+
if (!RB_TYPE_P(rb_timeout_milliseconds, T_FIXNUM) && !RB_TYPE_P(rb_timeout_milliseconds, T_BIGNUM)) {
|
|
1458
|
+
rb_raise(rb_eTypeError, "timeout_milliseconds must be an Integer or nil");
|
|
1459
|
+
}
|
|
1460
|
+
timeout_milliseconds = NUM2LL(rb_timeout_milliseconds);
|
|
1461
|
+
if (timeout_milliseconds < 0) {
|
|
1462
|
+
rb_raise(rb_eRangeError, "timeout_milliseconds must be non-negative");
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1445
1465
|
|
|
1446
1466
|
/* Phase 2: configure before creating the separately-owned runtime. */
|
|
1447
1467
|
ddog_TraceExporterConfig *config = NULL;
|
|
1448
1468
|
ddog_trace_exporter_config_new(&config);
|
|
1469
|
+
ddog_TraceExporterError *observation_error = ddog_trace_exporter_config_enable_observations(config);
|
|
1470
|
+
if (observation_error != NULL) {
|
|
1471
|
+
ddog_trace_exporter_config_free(config);
|
|
1472
|
+
check_exporter_error("Failed to select exporter observations", observation_error);
|
|
1473
|
+
}
|
|
1449
1474
|
|
|
1450
1475
|
set_config_field(config, ddog_trace_exporter_config_set_url, rb_url, "url");
|
|
1451
1476
|
set_config_field(config, ddog_trace_exporter_config_set_tracer_version, rb_tracer_version, "tracer_version");
|
|
@@ -1457,6 +1482,22 @@ static VALUE _native_exporter_new(
|
|
|
1457
1482
|
set_config_field(config, ddog_trace_exporter_config_set_service, rb_service, "service");
|
|
1458
1483
|
set_config_field(config, ddog_trace_exporter_config_set_version, rb_version, "version");
|
|
1459
1484
|
|
|
1485
|
+
if (rb_timeout_milliseconds != Qnil) {
|
|
1486
|
+
ddog_TraceExporterError *timeout_err = ddog_trace_exporter_config_set_connection_timeout(
|
|
1487
|
+
config, (uint64_t)timeout_milliseconds);
|
|
1488
|
+
if (timeout_err != NULL) {
|
|
1489
|
+
ddog_trace_exporter_config_free(config);
|
|
1490
|
+
check_exporter_error("Failed to configure native transport timeout", timeout_err);
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
ddog_TraceExporterError *stats_err = ddog_trace_exporter_config_set_client_computed_stats(
|
|
1495
|
+
config, rb_client_computed_stats == Qtrue);
|
|
1496
|
+
if (stats_err != NULL) {
|
|
1497
|
+
ddog_trace_exporter_config_free(config);
|
|
1498
|
+
check_exporter_error("Failed to configure client-computed stats", stats_err);
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1460
1501
|
/*
|
|
1461
1502
|
* Create a SharedRuntime and attach it to the config before building the
|
|
1462
1503
|
* exporter. The exporter holds a clone of the runtime's Arc; we keep our
|
|
@@ -1511,12 +1552,47 @@ static VALUE _native_exporter_new(
|
|
|
1511
1552
|
* (Puma, Unicorn, Passenger).
|
|
1512
1553
|
* ======================================================================== */
|
|
1513
1554
|
|
|
1555
|
+
static void *close_exporter_without_gvl(void *ptr) {
|
|
1556
|
+
trace_exporter_t *resources = ptr;
|
|
1557
|
+
if (resources->exporter != NULL) {
|
|
1558
|
+
ddog_trace_exporter_free(resources->exporter);
|
|
1559
|
+
resources->exporter = NULL;
|
|
1560
|
+
}
|
|
1561
|
+
if (resources->runtime != NULL) {
|
|
1562
|
+
ddog_shared_runtime_free(resources->runtime);
|
|
1563
|
+
resources->runtime = NULL;
|
|
1564
|
+
}
|
|
1565
|
+
return NULL;
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
static VALUE close_exporter_body(VALUE ptr) {
|
|
1569
|
+
rb_thread_call_without_gvl(close_exporter_without_gvl, (void *)ptr, NULL, NULL);
|
|
1570
|
+
return Qnil;
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
static VALUE close_exporter_ensure(VALUE ptr) {
|
|
1574
|
+
/* An interrupt may arrive before the no-GVL call starts. */
|
|
1575
|
+
close_exporter_without_gvl((void *)ptr);
|
|
1576
|
+
return Qnil;
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
static VALUE _native_close(VALUE self) {
|
|
1580
|
+
trace_exporter_t *wrapper;
|
|
1581
|
+
TypedData_Get_Struct(self, trace_exporter_t, &trace_exporter_typed_data, wrapper);
|
|
1582
|
+
if (wrapper == NULL) return Qnil;
|
|
1583
|
+
|
|
1584
|
+
trace_exporter_t resources = *wrapper;
|
|
1585
|
+
wrapper->exporter = NULL;
|
|
1586
|
+
wrapper->runtime = NULL;
|
|
1587
|
+
rb_ensure(close_exporter_body, (VALUE)&resources, close_exporter_ensure, (VALUE)&resources);
|
|
1588
|
+
RB_GC_GUARD(self);
|
|
1589
|
+
return Qnil;
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1514
1592
|
static VALUE _native_before_fork(VALUE self) {
|
|
1515
1593
|
trace_exporter_t *wrapper;
|
|
1516
1594
|
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
|
-
}
|
|
1595
|
+
if (wrapper == NULL || wrapper->runtime == NULL) return Qnil;
|
|
1520
1596
|
ddog_SharedRuntimeFFIError *err = ddog_shared_runtime_before_fork(wrapper->runtime);
|
|
1521
1597
|
check_shared_runtime_error("Failed to prepare for fork", err);
|
|
1522
1598
|
return Qnil;
|
|
@@ -1525,9 +1601,7 @@ static VALUE _native_before_fork(VALUE self) {
|
|
|
1525
1601
|
static VALUE _native_after_fork_in_parent(VALUE self) {
|
|
1526
1602
|
trace_exporter_t *wrapper;
|
|
1527
1603
|
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
|
-
}
|
|
1604
|
+
if (wrapper == NULL || wrapper->runtime == NULL) return Qnil;
|
|
1531
1605
|
ddog_SharedRuntimeFFIError *err = ddog_shared_runtime_after_fork_parent(wrapper->runtime);
|
|
1532
1606
|
check_shared_runtime_error("Failed to restore after fork in parent", err);
|
|
1533
1607
|
return Qnil;
|
|
@@ -1536,9 +1610,7 @@ static VALUE _native_after_fork_in_parent(VALUE self) {
|
|
|
1536
1610
|
static VALUE _native_after_fork_in_child(VALUE self) {
|
|
1537
1611
|
trace_exporter_t *wrapper;
|
|
1538
1612
|
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
|
-
}
|
|
1613
|
+
if (wrapper == NULL || wrapper->runtime == NULL) return Qnil;
|
|
1542
1614
|
ddog_SharedRuntimeFFIError *err = ddog_shared_runtime_after_fork_child(wrapper->runtime);
|
|
1543
1615
|
check_shared_runtime_error("Failed to restore after fork in child", err);
|
|
1544
1616
|
return Qnil;
|
|
@@ -1556,6 +1628,7 @@ typedef struct {
|
|
|
1556
1628
|
const ddog_TraceExporter *exporter;
|
|
1557
1629
|
ddog_TracerTraceChunks *chunks;
|
|
1558
1630
|
ddog_TraceExporterResponse *response;
|
|
1631
|
+
ddog_TraceExporterObservations observations;
|
|
1559
1632
|
ddog_TraceExporterCancelToken *cancel_token; /* borrowed, not owned */
|
|
1560
1633
|
ddog_TraceExporterErrorCode error_code;
|
|
1561
1634
|
bool failed;
|
|
@@ -1564,8 +1637,8 @@ typedef struct {
|
|
|
1564
1637
|
|
|
1565
1638
|
static void *send_chunks_without_gvl(void *data) {
|
|
1566
1639
|
send_chunks_args_t *args = (send_chunks_args_t *)data;
|
|
1567
|
-
ddog_TraceExporterError *err =
|
|
1568
|
-
args->exporter, args->chunks, &args->response, args->cancel_token);
|
|
1640
|
+
ddog_TraceExporterError *err = ddog_trace_exporter_send_trace_chunks_observed(
|
|
1641
|
+
args->exporter, args->chunks, &args->response, &args->observations, args->cancel_token);
|
|
1569
1642
|
if (err != NULL) {
|
|
1570
1643
|
args->error_code = err->code;
|
|
1571
1644
|
args->failed = true;
|
|
@@ -1633,8 +1706,73 @@ typedef struct {
|
|
|
1633
1706
|
long trace_count;
|
|
1634
1707
|
raw_span_owner span_owner;
|
|
1635
1708
|
ddog_TracerTraceChunks *chunks; /* NULL after send consumes it */
|
|
1709
|
+
ddog_TraceExporterResponse *response;
|
|
1636
1710
|
} send_traces_ctx;
|
|
1637
1711
|
|
|
1712
|
+
typedef struct {
|
|
1713
|
+
const ddog_TraceExporterObservations *report;
|
|
1714
|
+
VALUE block;
|
|
1715
|
+
} observations_callback_args;
|
|
1716
|
+
|
|
1717
|
+
static VALUE yield_observations(VALUE arg) {
|
|
1718
|
+
observations_callback_args *callback = (observations_callback_args *)arg;
|
|
1719
|
+
const ddog_TraceExporterObservations *report = callback->report;
|
|
1720
|
+
VALUE values = rb_hash_new();
|
|
1721
|
+
#define ADD_OBSERVATION(field) if (report->field > 0) rb_hash_aset(values, ID2SYM(rb_intern_const(#field)), ULL2NUM(report->field))
|
|
1722
|
+
ADD_OBSERVATION(requests_count);
|
|
1723
|
+
ADD_OBSERVATION(errors_network);
|
|
1724
|
+
ADD_OBSERVATION(errors_timeout);
|
|
1725
|
+
ADD_OBSERVATION(errors_status_code);
|
|
1726
|
+
ADD_OBSERVATION(bytes_sent);
|
|
1727
|
+
ADD_OBSERVATION(chunks_sent);
|
|
1728
|
+
ADD_OBSERVATION(chunks_dropped_serialization_error);
|
|
1729
|
+
ADD_OBSERVATION(chunks_dropped_send_failure);
|
|
1730
|
+
ADD_OBSERVATION(spans_enqueued_for_serialization);
|
|
1731
|
+
ADD_OBSERVATION(spans_dropped_serialization_error);
|
|
1732
|
+
ADD_OBSERVATION(spans_dropped_api_error);
|
|
1733
|
+
ADD_OBSERVATION(responses_count);
|
|
1734
|
+
ADD_OBSERVATION(status_code);
|
|
1735
|
+
#undef ADD_OBSERVATION
|
|
1736
|
+
return rb_proc_call_with_block(callback->block, 1, &values, Qnil);
|
|
1737
|
+
}
|
|
1738
|
+
|
|
1739
|
+
static VALUE ignore_observation_error(DDTRACE_UNUSED VALUE arg, DDTRACE_UNUSED VALUE error) {
|
|
1740
|
+
return Qnil;
|
|
1741
|
+
}
|
|
1742
|
+
|
|
1743
|
+
static VALUE protected_observations(RB_BLOCK_CALL_FUNC_ARGLIST(yielded, arg)) {
|
|
1744
|
+
(void)yielded;
|
|
1745
|
+
(void)argc;
|
|
1746
|
+
(void)argv;
|
|
1747
|
+
(void)blockarg;
|
|
1748
|
+
return rb_rescue2(yield_observations, arg, ignore_observation_error, Qnil,
|
|
1749
|
+
rb_eStandardError, (VALUE)0);
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
static VALUE forward_observations(VALUE arg) {
|
|
1753
|
+
observations_callback_args callback = {
|
|
1754
|
+
.report = (const ddog_TraceExporterObservations *)arg,
|
|
1755
|
+
.block = rb_block_proc(),
|
|
1756
|
+
};
|
|
1757
|
+
/* Deliver fresh interrupts after the callback's StandardError rescue has ended. */
|
|
1758
|
+
VALUE result = rb_block_call(rb_cThread, id_handle_interrupt, 1, &observation_interrupt_mask,
|
|
1759
|
+
protected_observations, (VALUE)&callback);
|
|
1760
|
+
RB_GC_GUARD(callback.block);
|
|
1761
|
+
return result;
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1764
|
+
static VALUE ensure_observations(VALUE arg) {
|
|
1765
|
+
int ignored_state = 0;
|
|
1766
|
+
/* The original interruption wins over any callback failure or non-local exit. */
|
|
1767
|
+
rb_protect(forward_observations, arg, &ignored_state);
|
|
1768
|
+
return Qnil;
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
static VALUE rethrow_interrupt(VALUE tag) {
|
|
1772
|
+
rb_jump_tag(FIX2INT(tag));
|
|
1773
|
+
return Qnil;
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1638
1776
|
/*
|
|
1639
1777
|
* Body: build trace chunks from Ruby spans, then send them.
|
|
1640
1778
|
* Passed to rb_ensure as the "try" block.
|
|
@@ -1712,40 +1850,37 @@ static VALUE build_and_send_traces(VALUE arg) {
|
|
|
1712
1850
|
if (args.send_ran) {
|
|
1713
1851
|
ctx->chunks = NULL;
|
|
1714
1852
|
}
|
|
1853
|
+
ctx->response = args.response;
|
|
1854
|
+
|
|
1855
|
+
/* A completed send can race with cancellation; retain its report while unwinding. */
|
|
1856
|
+
if (!pending_exception) {
|
|
1857
|
+
pending_exception = check_if_pending_exception();
|
|
1858
|
+
}
|
|
1859
|
+
if (args.send_ran && rb_block_given_p()) {
|
|
1860
|
+
if (pending_exception) {
|
|
1861
|
+
/* rb_ensure preserves Thread#kill's fatal sentinel, which rb_set_errinfo cannot restore. */
|
|
1862
|
+
rb_ensure(rethrow_interrupt, INT2FIX(pending_exception),
|
|
1863
|
+
ensure_observations, (VALUE)&args.observations);
|
|
1864
|
+
} else {
|
|
1865
|
+
forward_observations((VALUE)&args.observations);
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
if (pending_exception) {
|
|
1869
|
+
rb_jump_tag(pending_exception);
|
|
1870
|
+
}
|
|
1715
1871
|
|
|
1716
1872
|
/* Extract the response body as a Ruby string before freeing. */
|
|
1717
1873
|
VALUE payload = Qnil;
|
|
1718
1874
|
if (args.response != NULL) {
|
|
1719
|
-
ddog_ByteSlice body =
|
|
1720
|
-
ddog_trace_exporter_response_get_body(args.response);
|
|
1875
|
+
ddog_ByteSlice body = ddog_trace_exporter_response_get_body(args.response);
|
|
1721
1876
|
if (body.len > 0) {
|
|
1722
1877
|
payload = rb_str_new((const char *)body.ptr, (long)body.len);
|
|
1723
1878
|
}
|
|
1724
1879
|
ddog_trace_exporter_response_free(args.response);
|
|
1880
|
+
ctx->response = NULL;
|
|
1725
1881
|
args.response = NULL;
|
|
1726
1882
|
}
|
|
1727
1883
|
|
|
1728
|
-
/*
|
|
1729
|
-
* Re-check for a pending interrupt unconditionally before deciding the
|
|
1730
|
-
* outcome. In a race, the unblock function (interrupt_exporter_call) can
|
|
1731
|
-
* fire -- cancelling the token -- while the send still completes, so
|
|
1732
|
-
* rb_thread_call_without_gvl2 returns with args.send_ran == true. In that
|
|
1733
|
-
* case the loop above exits without ever calling check_if_pending_exception(),
|
|
1734
|
-
* leaving the interrupt pending. If we did not check here, the cancelled
|
|
1735
|
-
* send would fall through and be reported as an ordinary transport error
|
|
1736
|
-
* response, swallowing the interrupt (e.g. Thread#kill / shutdown).
|
|
1737
|
-
*
|
|
1738
|
-
* This runs after the response has already been extracted and freed and
|
|
1739
|
-
* after chunks have been handed off to the ensure handler, so re-raising
|
|
1740
|
-
* here leaks nothing.
|
|
1741
|
-
*/
|
|
1742
|
-
if (!pending_exception) {
|
|
1743
|
-
pending_exception = check_if_pending_exception();
|
|
1744
|
-
}
|
|
1745
|
-
if (pending_exception) {
|
|
1746
|
-
rb_jump_tag(pending_exception);
|
|
1747
|
-
}
|
|
1748
|
-
|
|
1749
1884
|
if (args.failed) {
|
|
1750
1885
|
VALUE err_resp = create_error_response(args.error_code, ctx->trace_count);
|
|
1751
1886
|
return rb_ary_new_from_args(1, err_resp);
|
|
@@ -1762,6 +1897,10 @@ static VALUE build_and_send_traces(VALUE arg) {
|
|
|
1762
1897
|
static VALUE free_send_resources(VALUE arg) {
|
|
1763
1898
|
send_traces_ctx *ctx = (send_traces_ctx *)arg;
|
|
1764
1899
|
free_raw_span((VALUE)&ctx->span_owner);
|
|
1900
|
+
if (ctx->response != NULL) {
|
|
1901
|
+
ddog_trace_exporter_response_free(ctx->response);
|
|
1902
|
+
ctx->response = NULL;
|
|
1903
|
+
}
|
|
1765
1904
|
if (ctx->chunks != NULL) {
|
|
1766
1905
|
ddog_tracer_trace_chunks_free(ctx->chunks);
|
|
1767
1906
|
ctx->chunks = NULL;
|
|
@@ -1818,6 +1957,12 @@ static VALUE _native_send_traces(VALUE self, VALUE traces, VALUE native_events_s
|
|
|
1818
1957
|
* ======================================================================== */
|
|
1819
1958
|
|
|
1820
1959
|
void trace_exporter_init(VALUE tracing_module) {
|
|
1960
|
+
id_handle_interrupt = rb_intern("handle_interrupt");
|
|
1961
|
+
observation_interrupt_mask = rb_hash_new();
|
|
1962
|
+
rb_hash_aset(observation_interrupt_mask, rb_cObject, ID2SYM(rb_intern("never")));
|
|
1963
|
+
rb_obj_freeze(observation_interrupt_mask);
|
|
1964
|
+
rb_global_variable(&observation_interrupt_mask);
|
|
1965
|
+
|
|
1821
1966
|
/* -- Module hierarchy -- */
|
|
1822
1967
|
VALUE transport_module = rb_define_module_under(tracing_module, "Transport");
|
|
1823
1968
|
VALUE native_module =
|
|
@@ -1848,6 +1993,7 @@ void trace_exporter_init(VALUE tracing_module) {
|
|
|
1848
1993
|
/* Instance: _native_send_traces(traces, native_events_supported) -> Array[Response] */
|
|
1849
1994
|
rb_define_method(trace_exporter_class, "_native_send_traces",
|
|
1850
1995
|
_native_send_traces, 2);
|
|
1996
|
+
rb_define_method(trace_exporter_class, "_native_close", _native_close, 0);
|
|
1851
1997
|
|
|
1852
1998
|
/* Instance: fork safety hooks */
|
|
1853
1999
|
rb_define_method(trace_exporter_class, "_native_before_fork",
|
|
@@ -11,7 +11,7 @@ module Datadog
|
|
|
11
11
|
module LibdatadogExtconfHelpers
|
|
12
12
|
# Used to make sure the correct gem version gets loaded, as extconf.rb does not get run with "bundle exec" and thus
|
|
13
13
|
# may see multiple libdatadog versions. See https://github.com/DataDog/dd-trace-rb/pull/2531 for the horror story.
|
|
14
|
-
LIBDATADOG_VERSION = "~>
|
|
14
|
+
LIBDATADOG_VERSION = "~> 46.0.0.1.1"
|
|
15
15
|
|
|
16
16
|
# Used as an workaround for a limitation with how dynamic linking works in environments where the datadog gem and
|
|
17
17
|
# libdatadog are moved after the extension gets compiled.
|
|
@@ -21,7 +21,7 @@ require_relative "../../ai_guard/component"
|
|
|
21
21
|
require_relative "../../di/component"
|
|
22
22
|
require_relative "../../symbol_database"
|
|
23
23
|
require_relative "../../symbol_database/component"
|
|
24
|
-
require_relative "../../open_feature/
|
|
24
|
+
require_relative "../../open_feature/activation"
|
|
25
25
|
require_relative "../../error_tracking/component"
|
|
26
26
|
require_relative "../crashtracking/component"
|
|
27
27
|
require_relative "../environment/agent_info"
|
|
@@ -154,8 +154,7 @@ module Datadog
|
|
|
154
154
|
:ai_guard,
|
|
155
155
|
:agent_info,
|
|
156
156
|
:data_streams,
|
|
157
|
-
:symbol_database
|
|
158
|
-
:open_feature
|
|
157
|
+
:symbol_database
|
|
159
158
|
|
|
160
159
|
def initialize(settings)
|
|
161
160
|
@settings = settings
|
|
@@ -188,15 +187,14 @@ module Datadog
|
|
|
188
187
|
|
|
189
188
|
@telemetry = self.class.build_telemetry(settings, agent_settings, @logger)
|
|
190
189
|
|
|
191
|
-
# Bind Remote Configuration dispatch to this tree, which starts before it becomes the global Components instance.
|
|
192
190
|
@remote = Remote::Component.build(
|
|
193
191
|
settings,
|
|
194
192
|
agent_settings,
|
|
195
193
|
logger: @logger,
|
|
196
194
|
telemetry: telemetry,
|
|
197
|
-
open_feature_component_provider: -> { @open_feature },
|
|
198
195
|
)
|
|
199
196
|
@tracer = Datadog::Tracing::Component.build_tracer(settings, agent_settings, logger: @logger)
|
|
197
|
+
Datadog::Tracing::Component.bind_transport_telemetry(@tracer, telemetry)
|
|
200
198
|
@crashtracker = self.class.build_crashtracker(settings, agent_settings, logger: @logger)
|
|
201
199
|
|
|
202
200
|
@profiler, profiler_logger_extra = Datadog::Profiling::Component.build_profiler_component(
|
|
@@ -211,7 +209,13 @@ module Datadog
|
|
|
211
209
|
@health_metrics = self.class.build_health_metrics(settings, @logger, telemetry)
|
|
212
210
|
@appsec = Datadog::AppSec::Component.build_appsec_component(settings, telemetry: telemetry)
|
|
213
211
|
@ai_guard = Datadog::AIGuard::Component.build(settings, logger: @logger, telemetry: telemetry)
|
|
214
|
-
@
|
|
212
|
+
@open_feature_activation = OpenFeature::Activation.new(
|
|
213
|
+
settings,
|
|
214
|
+
agent_settings,
|
|
215
|
+
remote,
|
|
216
|
+
logger: @logger,
|
|
217
|
+
telemetry: telemetry,
|
|
218
|
+
)
|
|
215
219
|
@dynamic_instrumentation = Datadog::DI::Component.build(settings, agent_settings, @logger, telemetry: telemetry)
|
|
216
220
|
# Only build symbol database when enabled, so a disabled component is
|
|
217
221
|
# never constructed.
|
|
@@ -257,6 +261,7 @@ module Datadog
|
|
|
257
261
|
ProcessDiscovery.after_fork
|
|
258
262
|
symbol_database&.after_fork!
|
|
259
263
|
data_streams&.restart_flush_thread
|
|
264
|
+
@open_feature_activation.after_fork
|
|
260
265
|
end
|
|
261
266
|
|
|
262
267
|
# Hot-swaps with a new sampler.
|
|
@@ -271,6 +276,8 @@ module Datadog
|
|
|
271
276
|
def startup!(settings, old_state: nil)
|
|
272
277
|
telemetry.start(old_state&.telemetry_enabled?, components: self)
|
|
273
278
|
|
|
279
|
+
@open_feature_activation.startup!
|
|
280
|
+
|
|
274
281
|
if settings.profiling.enabled
|
|
275
282
|
if profiler
|
|
276
283
|
profiler.start
|
|
@@ -329,8 +336,14 @@ module Datadog
|
|
|
329
336
|
# Shutdown Symbol Database
|
|
330
337
|
symbol_database&.shutdown!
|
|
331
338
|
|
|
332
|
-
|
|
333
|
-
|
|
339
|
+
begin
|
|
340
|
+
@open_feature_activation.shutdown!
|
|
341
|
+
rescue => e
|
|
342
|
+
# Feature Flags is optional and must never interrupt library shutdown.
|
|
343
|
+
description = "Feature Flags delivery failed to shut down"
|
|
344
|
+
logger.error("#{description}: #{e.class}: #{e.message}")
|
|
345
|
+
telemetry.report(e, description: description)
|
|
346
|
+
end
|
|
334
347
|
|
|
335
348
|
# Decommission AppSec
|
|
336
349
|
appsec&.shutdown!
|
|
@@ -407,6 +420,14 @@ module Datadog
|
|
|
407
420
|
di_implicitly_enabled: di_implicit || false,
|
|
408
421
|
)
|
|
409
422
|
end
|
|
423
|
+
|
|
424
|
+
def open_feature
|
|
425
|
+
@open_feature_activation.component
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
private
|
|
429
|
+
|
|
430
|
+
attr_reader :open_feature_activation
|
|
410
431
|
end
|
|
411
432
|
end
|
|
412
433
|
end
|