datadog 2.42.0 → 2.43.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 +24 -2
- data/README.md +3 -0
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +10 -0
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +15 -5
- data/ext/datadog_profiling_native_extension/heap_recorder.c +13 -0
- data/ext/datadog_profiling_native_extension/heap_recorder.h +4 -0
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +9 -2
- data/ext/datadog_profiling_native_extension/stack_recorder.c +13 -0
- data/ext/datadog_profiling_native_extension/time_helpers.h +2 -1
- data/ext/libdatadog_api/otel_thread_context.c +15 -0
- data/ext/libdatadog_api/trace_exporter.c +642 -22
- data/lib/datadog/open_feature/transport.rb +5 -0
- data/lib/datadog/profiling/collectors/info.rb +2 -6
- data/lib/datadog/tracing/contrib/sequel/jdbc_connection_string.rb +140 -0
- data/lib/datadog/tracing/contrib/sequel/utils.rb +49 -62
- data/lib/datadog/tracing/otel_thread_context.rb +4 -0
- data/lib/datadog/tracing/transport/native.rb +17 -25
- data/lib/datadog/tracing/transport/span_events_negotiation.rb +41 -0
- data/lib/datadog/tracing/transport/traces.rb +2 -22
- data/lib/datadog/version.rb +1 -1
- metadata +5 -3
|
@@ -27,14 +27,14 @@ typedef struct {
|
|
|
27
27
|
} raw_span_owner;
|
|
28
28
|
|
|
29
29
|
/* Internal: convert a Ruby Span into the supplied raw Rust span owner */
|
|
30
|
-
static void convert_ruby_span_to_rust(VALUE span, raw_span_owner *owner);
|
|
30
|
+
static void convert_ruby_span_to_rust(VALUE span, VALUE native_events_supported, raw_span_owner *owner);
|
|
31
31
|
|
|
32
32
|
/* TracerSpan methods */
|
|
33
33
|
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_send_traces(VALUE self, VALUE traces);
|
|
37
|
+
static VALUE _native_send_traces(VALUE self, VALUE traces, VALUE native_events_supported);
|
|
38
38
|
static VALUE _native_before_fork(VALUE self);
|
|
39
39
|
static VALUE _native_after_fork_in_parent(VALUE self);
|
|
40
40
|
static VALUE _native_after_fork_in_child(VALUE self);
|
|
@@ -65,13 +65,38 @@ static ID at_duration_id;
|
|
|
65
65
|
static ID at_status_id;
|
|
66
66
|
static ID at_meta_id;
|
|
67
67
|
static ID at_metrics_id;
|
|
68
|
+
static ID at_events_id;
|
|
69
|
+
static ID at_links_id;
|
|
68
70
|
static ID at_metastruct_id;
|
|
69
71
|
|
|
70
72
|
/* Method IDs for time / integer operations */
|
|
71
73
|
static ID id_duration_method;
|
|
74
|
+
static ID id_to_a;
|
|
75
|
+
static ID id_to_native_format;
|
|
76
|
+
static ID id_to_hash;
|
|
72
77
|
static ID id_to_h;
|
|
73
78
|
static ID id_negative_p;
|
|
74
79
|
|
|
80
|
+
/* SpanEvent native-format hash keys */
|
|
81
|
+
static VALUE event_name_key = Qnil;
|
|
82
|
+
static VALUE event_time_key = Qnil;
|
|
83
|
+
static VALUE event_attributes_key = Qnil;
|
|
84
|
+
static ID event_type_id;
|
|
85
|
+
static ID event_string_value_id;
|
|
86
|
+
static ID event_bool_value_id;
|
|
87
|
+
static ID event_int_value_id;
|
|
88
|
+
static ID event_double_value_id;
|
|
89
|
+
static ID event_array_value_id;
|
|
90
|
+
static ID event_values_id;
|
|
91
|
+
|
|
92
|
+
/* Canonical SpanLink#to_hash field IDs */
|
|
93
|
+
static ID link_trace_id_id;
|
|
94
|
+
static ID link_trace_id_high_id;
|
|
95
|
+
static ID link_span_id_id;
|
|
96
|
+
static ID link_attributes_id;
|
|
97
|
+
static ID link_tracestate_id;
|
|
98
|
+
static ID link_flags_id;
|
|
99
|
+
|
|
75
100
|
/* Resolved lazily because AppSec may load after this extension. */
|
|
76
101
|
static VALUE serializable_backtrace_class = Qnil;
|
|
77
102
|
|
|
@@ -233,6 +258,126 @@ static inline int64_t time_to_nanos(VALUE time) {
|
|
|
233
258
|
return (int64_t)ts.tv_sec * 1000000000LL + (int64_t)ts.tv_nsec;
|
|
234
259
|
}
|
|
235
260
|
|
|
261
|
+
enum span_event_attribute_type {
|
|
262
|
+
SPAN_EVENT_STRING = 0,
|
|
263
|
+
SPAN_EVENT_BOOL = 1,
|
|
264
|
+
SPAN_EVENT_INT = 2,
|
|
265
|
+
SPAN_EVENT_DOUBLE = 3,
|
|
266
|
+
SPAN_EVENT_ARRAY = 4,
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
static inline VALUE event_hash_value(VALUE hash, ID key) {
|
|
270
|
+
return rb_hash_fetch(hash, ID2SYM(key));
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/* Validate one scalar native attribute in place, proving integer values fit
|
|
274
|
+
* the libdatadog wire type before any Rust allocation. */
|
|
275
|
+
static void validate_event_scalar(VALUE key, VALUE attribute, int type) {
|
|
276
|
+
ENFORCE_TYPE(attribute, T_HASH);
|
|
277
|
+
|
|
278
|
+
switch (type) {
|
|
279
|
+
case SPAN_EVENT_STRING:
|
|
280
|
+
ENFORCE_TYPE(event_hash_value(attribute, event_string_value_id), T_STRING);
|
|
281
|
+
break;
|
|
282
|
+
case SPAN_EVENT_BOOL: {
|
|
283
|
+
VALUE value = event_hash_value(attribute, event_bool_value_id);
|
|
284
|
+
if (value != Qtrue && value != Qfalse) {
|
|
285
|
+
private_raise_exception(
|
|
286
|
+
rb_exc_new_str(rb_eTypeError, rb_sprintf(
|
|
287
|
+
"span event attribute '%"PRIsVALUE"' must be true or false, got %s",
|
|
288
|
+
key, rb_obj_classname(value))),
|
|
289
|
+
"span event attribute has invalid boolean value");
|
|
290
|
+
}
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
case SPAN_EVENT_INT:
|
|
294
|
+
NUM2LL(event_hash_value(attribute, event_int_value_id));
|
|
295
|
+
break;
|
|
296
|
+
case SPAN_EVENT_DOUBLE:
|
|
297
|
+
NUM2DBL(event_hash_value(attribute, event_double_value_id));
|
|
298
|
+
break;
|
|
299
|
+
default:
|
|
300
|
+
rb_raise(rb_eArgError, "unsupported span event attribute type: %d", type);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/*
|
|
305
|
+
* Build a validated, Ruby-owned snapshot of a span's events for the native
|
|
306
|
+
* exporter. Each SpanEvent is converted via to_native_format, its attributes
|
|
307
|
+
* flattened to [key, hash] pairs, and every scalar/array value type- and
|
|
308
|
+
* range-checked here so a validation failure raises before any Rust
|
|
309
|
+
* allocation. Returns an array of [name, time, pairs] triples consumed by
|
|
310
|
+
* build_native_events; *max_array_length is set to the largest array
|
|
311
|
+
* attribute length, for sizing the conversion scratch buffer.
|
|
312
|
+
*/
|
|
313
|
+
static VALUE normalize_span_events(VALUE span, size_t *max_array_length) {
|
|
314
|
+
VALUE events = rb_ivar_get(span, at_events_id);
|
|
315
|
+
ENFORCE_TYPE(events, T_ARRAY);
|
|
316
|
+
|
|
317
|
+
long event_count = RARRAY_LEN(events);
|
|
318
|
+
VALUE normalized = rb_ary_new_capa(event_count);
|
|
319
|
+
|
|
320
|
+
for (long i = 0; i < event_count; i++) {
|
|
321
|
+
VALUE event = rb_ary_entry(events, i);
|
|
322
|
+
VALUE hash = rb_funcall(event, id_to_native_format, 0);
|
|
323
|
+
ENFORCE_TYPE(hash, T_HASH);
|
|
324
|
+
|
|
325
|
+
VALUE name = rb_hash_fetch(hash, event_name_key);
|
|
326
|
+
VALUE time = rb_hash_fetch(hash, event_time_key);
|
|
327
|
+
ENFORCE_TYPE(name, T_STRING);
|
|
328
|
+
NUM2ULL(time);
|
|
329
|
+
|
|
330
|
+
VALUE attributes = rb_hash_aref(hash, event_attributes_key);
|
|
331
|
+
VALUE pairs = rb_ary_new();
|
|
332
|
+
if (attributes != Qnil) {
|
|
333
|
+
ENFORCE_TYPE(attributes, T_HASH);
|
|
334
|
+
pairs = rb_funcall(attributes, id_to_a, 0);
|
|
335
|
+
|
|
336
|
+
for (long j = 0; j < RARRAY_LEN(pairs); j++) {
|
|
337
|
+
VALUE pair = rb_ary_entry(pairs, j);
|
|
338
|
+
ENFORCE_TYPE(pair, T_ARRAY);
|
|
339
|
+
VALUE key = rb_ary_entry(pair, 0);
|
|
340
|
+
VALUE attribute = rb_ary_entry(pair, 1);
|
|
341
|
+
ENFORCE_TYPE(key, T_STRING);
|
|
342
|
+
ENFORCE_TYPE(attribute, T_HASH);
|
|
343
|
+
|
|
344
|
+
int type = NUM2INT(event_hash_value(attribute, event_type_id));
|
|
345
|
+
if (type != SPAN_EVENT_ARRAY) {
|
|
346
|
+
validate_event_scalar(key, attribute, type);
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
VALUE array_value = event_hash_value(attribute, event_array_value_id);
|
|
351
|
+
ENFORCE_TYPE(array_value, T_HASH);
|
|
352
|
+
VALUE values = event_hash_value(array_value, event_values_id);
|
|
353
|
+
ENFORCE_TYPE(values, T_ARRAY);
|
|
354
|
+
long length = RARRAY_LEN(values);
|
|
355
|
+
if ((size_t)length > *max_array_length) *max_array_length = (size_t)length;
|
|
356
|
+
|
|
357
|
+
int element_type = SPAN_EVENT_STRING;
|
|
358
|
+
if (length > 0) {
|
|
359
|
+
VALUE first = rb_ary_entry(values, 0);
|
|
360
|
+
ENFORCE_TYPE(first, T_HASH);
|
|
361
|
+
element_type = NUM2INT(event_hash_value(first, event_type_id));
|
|
362
|
+
}
|
|
363
|
+
if (element_type == SPAN_EVENT_ARRAY) rb_raise(rb_eArgError, "nested span event arrays are not supported");
|
|
364
|
+
|
|
365
|
+
for (long k = 0; k < length; k++) {
|
|
366
|
+
VALUE value = rb_ary_entry(values, k);
|
|
367
|
+
ENFORCE_TYPE(value, T_HASH);
|
|
368
|
+
int value_type = NUM2INT(event_hash_value(value, event_type_id));
|
|
369
|
+
if (value_type != element_type) rb_raise(rb_eArgError, "span event arrays must be homogeneous");
|
|
370
|
+
validate_event_scalar(key, value, value_type);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
rb_ary_push(normalized, rb_ary_new_from_args(3, name, time, pairs));
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
return normalized;
|
|
379
|
+
}
|
|
380
|
+
|
|
236
381
|
/* 128-bit trace ID split into two 64-bit halves */
|
|
237
382
|
typedef struct {
|
|
238
383
|
uint64_t low;
|
|
@@ -325,6 +470,352 @@ static int metrics_iter_cb(VALUE key, VALUE value, VALUE arg) {
|
|
|
325
470
|
return ST_CONTINUE;
|
|
326
471
|
}
|
|
327
472
|
|
|
473
|
+
typedef struct {
|
|
474
|
+
VALUE span;
|
|
475
|
+
VALUE normalized_events;
|
|
476
|
+
raw_span_owner *owner;
|
|
477
|
+
void *allocation;
|
|
478
|
+
void *array_scratch;
|
|
479
|
+
ddog_TracerSpanEvent **events;
|
|
480
|
+
long event_count;
|
|
481
|
+
size_t max_array_length;
|
|
482
|
+
} span_conversion_ctx;
|
|
483
|
+
|
|
484
|
+
typedef union {
|
|
485
|
+
ddog_CharSlice string;
|
|
486
|
+
bool boolean;
|
|
487
|
+
int64_t integer;
|
|
488
|
+
double double_value;
|
|
489
|
+
} span_event_array_scratch;
|
|
490
|
+
|
|
491
|
+
typedef struct {
|
|
492
|
+
char padding;
|
|
493
|
+
span_event_array_scratch value;
|
|
494
|
+
} span_event_array_scratch_alignment;
|
|
495
|
+
|
|
496
|
+
/* Free event resources that the conversion context still owns at cleanup
|
|
497
|
+
* time. build_native_events allocates each event into ctx->events[i];
|
|
498
|
+
* build_rust_span transfers ownership to the Rust span via add_event and
|
|
499
|
+
* NULLs the slot. Events left non-NULL here were never attached (an
|
|
500
|
+
* exception interrupted build_rust_span) and are freed now. The raw span
|
|
501
|
+
* itself is owned by the caller's separate ensure handler. */
|
|
502
|
+
static VALUE free_span_conversion(VALUE arg) {
|
|
503
|
+
span_conversion_ctx *ctx = (span_conversion_ctx *)arg;
|
|
504
|
+
if (ctx->events != NULL) {
|
|
505
|
+
for (long i = 0; i < ctx->event_count; i++) {
|
|
506
|
+
if (ctx->events[i] != NULL) {
|
|
507
|
+
ddog_tracer_span_event_free(ctx->events[i]);
|
|
508
|
+
ctx->events[i] = NULL;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
if (ctx->allocation != NULL) {
|
|
513
|
+
ruby_xfree(ctx->allocation);
|
|
514
|
+
ctx->allocation = NULL;
|
|
515
|
+
ctx->events = NULL;
|
|
516
|
+
ctx->array_scratch = NULL;
|
|
517
|
+
}
|
|
518
|
+
return Qnil;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/* Attach one normalized Ruby attribute to an event. Unknown type codes are
|
|
522
|
+
* unreachable because normalize_span_events already rejected them. */
|
|
523
|
+
static ddog_TraceExporterError *set_native_event_attribute(
|
|
524
|
+
ddog_TracerSpanEvent *event,
|
|
525
|
+
VALUE key,
|
|
526
|
+
VALUE attribute,
|
|
527
|
+
void *scratch) {
|
|
528
|
+
ddog_CharSlice key_slice = char_slice_from_ruby_string(key);
|
|
529
|
+
int type = NUM2INT(event_hash_value(attribute, event_type_id));
|
|
530
|
+
|
|
531
|
+
switch (type) {
|
|
532
|
+
case SPAN_EVENT_STRING:
|
|
533
|
+
return ddog_tracer_span_event_set_string(
|
|
534
|
+
event, key_slice,
|
|
535
|
+
char_slice_from_ruby_string(event_hash_value(attribute, event_string_value_id)));
|
|
536
|
+
case SPAN_EVENT_BOOL:
|
|
537
|
+
return ddog_tracer_span_event_set_bool(
|
|
538
|
+
event, key_slice,
|
|
539
|
+
event_hash_value(attribute, event_bool_value_id) == Qtrue);
|
|
540
|
+
case SPAN_EVENT_INT:
|
|
541
|
+
return ddog_tracer_span_event_set_int(
|
|
542
|
+
event, key_slice,
|
|
543
|
+
NUM2LL(event_hash_value(attribute, event_int_value_id)));
|
|
544
|
+
case SPAN_EVENT_DOUBLE:
|
|
545
|
+
return ddog_tracer_span_event_set_double(
|
|
546
|
+
event, key_slice,
|
|
547
|
+
NUM2DBL(event_hash_value(attribute, event_double_value_id)));
|
|
548
|
+
case SPAN_EVENT_ARRAY:
|
|
549
|
+
break;
|
|
550
|
+
default:
|
|
551
|
+
return NULL;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
VALUE array_value = event_hash_value(attribute, event_array_value_id);
|
|
555
|
+
VALUE values = event_hash_value(array_value, event_values_id);
|
|
556
|
+
long length = RARRAY_LEN(values);
|
|
557
|
+
int element_type = SPAN_EVENT_STRING;
|
|
558
|
+
if (length > 0) {
|
|
559
|
+
element_type = NUM2INT(event_hash_value(rb_ary_entry(values, 0), event_type_id));
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
switch (element_type) {
|
|
563
|
+
case SPAN_EVENT_STRING: {
|
|
564
|
+
ddog_CharSlice *out = (ddog_CharSlice *)scratch;
|
|
565
|
+
for (long i = 0; i < length; i++) {
|
|
566
|
+
VALUE value = event_hash_value(rb_ary_entry(values, i), event_string_value_id);
|
|
567
|
+
out[i] = char_slice_from_ruby_string(value);
|
|
568
|
+
}
|
|
569
|
+
return ddog_tracer_span_event_set_string_array(
|
|
570
|
+
event, key_slice,
|
|
571
|
+
(struct ddog_Slice_CharSlice){.ptr = out, .len = (uintptr_t)length});
|
|
572
|
+
}
|
|
573
|
+
case SPAN_EVENT_BOOL: {
|
|
574
|
+
bool *out = (bool *)scratch;
|
|
575
|
+
for (long i = 0; i < length; i++) {
|
|
576
|
+
out[i] = event_hash_value(rb_ary_entry(values, i), event_bool_value_id) == Qtrue;
|
|
577
|
+
}
|
|
578
|
+
return ddog_tracer_span_event_set_bool_array(
|
|
579
|
+
event, key_slice,
|
|
580
|
+
(struct ddog_Slice_Bool){.ptr = out, .len = (uintptr_t)length});
|
|
581
|
+
}
|
|
582
|
+
case SPAN_EVENT_INT: {
|
|
583
|
+
int64_t *out = (int64_t *)scratch;
|
|
584
|
+
for (long i = 0; i < length; i++) {
|
|
585
|
+
out[i] = NUM2LL(event_hash_value(rb_ary_entry(values, i), event_int_value_id));
|
|
586
|
+
}
|
|
587
|
+
return ddog_tracer_span_event_set_int_array(
|
|
588
|
+
event, key_slice,
|
|
589
|
+
(struct ddog_Slice_I64){.ptr = out, .len = (uintptr_t)length});
|
|
590
|
+
}
|
|
591
|
+
case SPAN_EVENT_DOUBLE: {
|
|
592
|
+
double *out = (double *)scratch;
|
|
593
|
+
for (long i = 0; i < length; i++) {
|
|
594
|
+
out[i] = NUM2DBL(event_hash_value(rb_ary_entry(values, i), event_double_value_id));
|
|
595
|
+
}
|
|
596
|
+
return ddog_tracer_span_event_set_double_array(
|
|
597
|
+
event, key_slice,
|
|
598
|
+
(struct ddog_Slice_F64){.ptr = out, .len = (uintptr_t)length});
|
|
599
|
+
}
|
|
600
|
+
default:
|
|
601
|
+
return NULL;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/* Build every normalized event before constructing its owning span. The
|
|
606
|
+
* conversion context retains each event until add_event consumes it. */
|
|
607
|
+
static void build_native_events(span_conversion_ctx *ctx) {
|
|
608
|
+
for (long i = 0; i < ctx->event_count; i++) {
|
|
609
|
+
VALUE source = rb_ary_entry(ctx->normalized_events, i);
|
|
610
|
+
ddog_TraceExporterError *err = ddog_tracer_span_event_new(
|
|
611
|
+
&ctx->events[i],
|
|
612
|
+
char_slice_from_ruby_string(rb_ary_entry(source, 0)),
|
|
613
|
+
NUM2ULL(rb_ary_entry(source, 1)));
|
|
614
|
+
check_exporter_error("Failed to create span event", err);
|
|
615
|
+
|
|
616
|
+
VALUE pairs = rb_ary_entry(source, 2);
|
|
617
|
+
for (long j = 0; j < RARRAY_LEN(pairs); j++) {
|
|
618
|
+
VALUE pair = rb_ary_entry(pairs, j);
|
|
619
|
+
err = set_native_event_attribute(
|
|
620
|
+
ctx->events[i], rb_ary_entry(pair, 0), rb_ary_entry(pair, 1), ctx->array_scratch);
|
|
621
|
+
check_exporter_error("Failed to set span event attribute", err);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
typedef struct {
|
|
627
|
+
char *ptr;
|
|
628
|
+
size_t len;
|
|
629
|
+
} owned_link_string;
|
|
630
|
+
|
|
631
|
+
typedef struct {
|
|
632
|
+
owned_link_string key;
|
|
633
|
+
owned_link_string value;
|
|
634
|
+
} owned_link_attribute;
|
|
635
|
+
|
|
636
|
+
typedef struct {
|
|
637
|
+
uint64_t trace_id_low;
|
|
638
|
+
uint64_t trace_id_high;
|
|
639
|
+
uint64_t span_id;
|
|
640
|
+
owned_link_attribute *attributes;
|
|
641
|
+
size_t attribute_count;
|
|
642
|
+
owned_link_string tracestate;
|
|
643
|
+
uint32_t flags;
|
|
644
|
+
} owned_span_link;
|
|
645
|
+
|
|
646
|
+
typedef struct {
|
|
647
|
+
VALUE rb_links;
|
|
648
|
+
owned_span_link *links;
|
|
649
|
+
size_t link_count;
|
|
650
|
+
size_t attribute_count;
|
|
651
|
+
void *ffi_storage;
|
|
652
|
+
ddog_TracerSpanLink *ffi_links;
|
|
653
|
+
} span_links_snapshot;
|
|
654
|
+
|
|
655
|
+
typedef struct {
|
|
656
|
+
owned_link_attribute *attributes;
|
|
657
|
+
size_t offset;
|
|
658
|
+
size_t capacity;
|
|
659
|
+
} snapshot_attribute_ctx;
|
|
660
|
+
|
|
661
|
+
/* SpanLink#to_hash stringifies attribute values and yields tracestate as a
|
|
662
|
+
* String; enforce T_STRING here so a non-String surfaces as a type error at
|
|
663
|
+
* the boundary. */
|
|
664
|
+
static void snapshot_link_string(VALUE string, owned_link_string *snapshot) {
|
|
665
|
+
ENFORCE_TYPE(string, T_STRING);
|
|
666
|
+
snapshot->len = (size_t)RSTRING_LEN(string);
|
|
667
|
+
snapshot->ptr = ruby_xmalloc(snapshot->len == 0 ? 1 : snapshot->len);
|
|
668
|
+
if (snapshot->len > 0) {
|
|
669
|
+
memcpy(snapshot->ptr, RSTRING_PTR(string), snapshot->len);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
static int snapshot_link_attribute_cb(VALUE key, VALUE value, VALUE arg) {
|
|
674
|
+
snapshot_attribute_ctx *ctx = (snapshot_attribute_ctx *)arg;
|
|
675
|
+
if (ctx->offset >= ctx->capacity) {
|
|
676
|
+
raise_error(rb_eRuntimeError, "Span link attributes changed during snapshot");
|
|
677
|
+
}
|
|
678
|
+
owned_link_attribute *attribute = &ctx->attributes[ctx->offset++];
|
|
679
|
+
snapshot_link_string(key, &attribute->key);
|
|
680
|
+
snapshot_link_string(value, &attribute->value);
|
|
681
|
+
return ST_CONTINUE;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
static ddog_CharSlice snapshot_char_slice(const owned_link_string *snapshot) {
|
|
685
|
+
return (ddog_CharSlice){
|
|
686
|
+
.ptr = snapshot->ptr == NULL ? "" : snapshot->ptr,
|
|
687
|
+
.len = snapshot->len,
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
static void free_owned_link_string(owned_link_string *snapshot) {
|
|
692
|
+
if (snapshot->ptr != NULL) {
|
|
693
|
+
ruby_xfree(snapshot->ptr);
|
|
694
|
+
snapshot->ptr = NULL;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
static void free_span_links_snapshot(span_links_snapshot *snapshot) {
|
|
699
|
+
if (snapshot->ffi_storage != NULL) {
|
|
700
|
+
ruby_xfree(snapshot->ffi_storage);
|
|
701
|
+
}
|
|
702
|
+
snapshot->ffi_storage = NULL;
|
|
703
|
+
snapshot->ffi_links = NULL;
|
|
704
|
+
|
|
705
|
+
if (snapshot->links != NULL) {
|
|
706
|
+
for (size_t i = 0; i < snapshot->link_count; i++) {
|
|
707
|
+
owned_span_link *link = &snapshot->links[i];
|
|
708
|
+
if (link->attributes != NULL) {
|
|
709
|
+
for (size_t j = 0; j < link->attribute_count; j++) {
|
|
710
|
+
free_owned_link_string(&link->attributes[j].key);
|
|
711
|
+
free_owned_link_string(&link->attributes[j].value);
|
|
712
|
+
}
|
|
713
|
+
ruby_xfree(link->attributes);
|
|
714
|
+
}
|
|
715
|
+
free_owned_link_string(&link->tracestate);
|
|
716
|
+
}
|
|
717
|
+
ruby_xfree(snapshot->links);
|
|
718
|
+
snapshot->links = NULL;
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
static VALUE prepare_span_links_snapshot(VALUE arg) {
|
|
723
|
+
span_links_snapshot *snapshot = (span_links_snapshot *)arg;
|
|
724
|
+
snapshot->link_count = (size_t)RARRAY_LEN(snapshot->rb_links);
|
|
725
|
+
if (snapshot->link_count > SIZE_MAX / sizeof(owned_span_link)) {
|
|
726
|
+
raise_error(rb_eArgError, "Span link count is too large");
|
|
727
|
+
}
|
|
728
|
+
if (snapshot->link_count > 0) {
|
|
729
|
+
snapshot->links = ruby_xcalloc(snapshot->link_count, sizeof(owned_span_link));
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
for (size_t i = 0; i < snapshot->link_count; i++) {
|
|
733
|
+
VALUE canonical = rb_funcall(rb_ary_entry(snapshot->rb_links, (long)i), id_to_hash, 0);
|
|
734
|
+
ENFORCE_TYPE(canonical, T_HASH);
|
|
735
|
+
|
|
736
|
+
/* Fetch each canonical field exactly once. Hash default procs may be
|
|
737
|
+
* stateful, so every value must be captured before Rust allocation/FFI. */
|
|
738
|
+
owned_span_link *link = &snapshot->links[i];
|
|
739
|
+
VALUE trace_id = rb_hash_aref(canonical, ID2SYM(link_trace_id_id));
|
|
740
|
+
link->trace_id_low = NUM2ULL(trace_id);
|
|
741
|
+
|
|
742
|
+
VALUE trace_id_high = rb_hash_aref(canonical, ID2SYM(link_trace_id_high_id));
|
|
743
|
+
link->trace_id_high = trace_id_high == Qnil ? 0 : NUM2ULL(trace_id_high);
|
|
744
|
+
|
|
745
|
+
VALUE span_id = rb_hash_aref(canonical, ID2SYM(link_span_id_id));
|
|
746
|
+
link->span_id = NUM2ULL(span_id);
|
|
747
|
+
|
|
748
|
+
VALUE attributes = rb_hash_aref(canonical, ID2SYM(link_attributes_id));
|
|
749
|
+
if (attributes != Qnil) {
|
|
750
|
+
ENFORCE_TYPE(attributes, T_HASH);
|
|
751
|
+
link->attribute_count = (size_t)RHASH_SIZE(attributes);
|
|
752
|
+
if (link->attribute_count > SIZE_MAX - snapshot->attribute_count) {
|
|
753
|
+
raise_error(rb_eArgError, "Span link attribute count is too large");
|
|
754
|
+
}
|
|
755
|
+
snapshot->attribute_count += link->attribute_count;
|
|
756
|
+
if (link->attribute_count > 0) {
|
|
757
|
+
link->attributes = ruby_xcalloc(link->attribute_count, sizeof(owned_link_attribute));
|
|
758
|
+
snapshot_attribute_ctx ctx = {
|
|
759
|
+
.attributes = link->attributes,
|
|
760
|
+
.offset = 0,
|
|
761
|
+
.capacity = link->attribute_count,
|
|
762
|
+
};
|
|
763
|
+
rb_hash_foreach(attributes, snapshot_link_attribute_cb, (VALUE)&ctx);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
VALUE tracestate = rb_hash_aref(canonical, ID2SYM(link_tracestate_id));
|
|
768
|
+
if (tracestate != Qnil) {
|
|
769
|
+
snapshot_link_string(tracestate, &link->tracestate);
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
VALUE flags = rb_hash_aref(canonical, ID2SYM(link_flags_id));
|
|
773
|
+
link->flags = NUM2UINT(flags);
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
if (snapshot->attribute_count > SIZE_MAX / sizeof(ddog_TracerSpanLinkAttribute)) {
|
|
777
|
+
raise_error(rb_eArgError, "Span link attribute count is too large");
|
|
778
|
+
}
|
|
779
|
+
size_t attributes_size =
|
|
780
|
+
snapshot->attribute_count * sizeof(ddog_TracerSpanLinkAttribute);
|
|
781
|
+
if (snapshot->link_count > (SIZE_MAX - attributes_size) /
|
|
782
|
+
sizeof(ddog_TracerSpanLink)) {
|
|
783
|
+
raise_error(rb_eArgError, "Span link count is too large");
|
|
784
|
+
}
|
|
785
|
+
size_t links_size = snapshot->link_count * sizeof(ddog_TracerSpanLink);
|
|
786
|
+
size_t storage_size = links_size + attributes_size;
|
|
787
|
+
snapshot->ffi_storage = ruby_xcalloc(1, storage_size == 0 ? 1 : storage_size);
|
|
788
|
+
snapshot->ffi_links = (ddog_TracerSpanLink *)snapshot->ffi_storage;
|
|
789
|
+
ddog_TracerSpanLinkAttribute *ffi_attributes =
|
|
790
|
+
(ddog_TracerSpanLinkAttribute *)((char *)snapshot->ffi_storage + links_size);
|
|
791
|
+
size_t attribute_offset = 0;
|
|
792
|
+
|
|
793
|
+
for (size_t i = 0; i < snapshot->link_count; i++) {
|
|
794
|
+
owned_span_link *link = &snapshot->links[i];
|
|
795
|
+
size_t first_attribute = attribute_offset;
|
|
796
|
+
for (size_t j = 0; j < link->attribute_count; j++) {
|
|
797
|
+
owned_link_attribute *attribute = &link->attributes[j];
|
|
798
|
+
ffi_attributes[attribute_offset++] = (ddog_TracerSpanLinkAttribute){
|
|
799
|
+
.key = snapshot_char_slice(&attribute->key),
|
|
800
|
+
.value = snapshot_char_slice(&attribute->value),
|
|
801
|
+
};
|
|
802
|
+
}
|
|
803
|
+
snapshot->ffi_links[i] = (ddog_TracerSpanLink){
|
|
804
|
+
.trace_id_low = link->trace_id_low,
|
|
805
|
+
.trace_id_high = link->trace_id_high,
|
|
806
|
+
.span_id = link->span_id,
|
|
807
|
+
.attributes = {
|
|
808
|
+
.ptr = ffi_attributes + first_attribute,
|
|
809
|
+
.len = link->attribute_count,
|
|
810
|
+
},
|
|
811
|
+
.tracestate = snapshot_char_slice(&link->tracestate),
|
|
812
|
+
.flags = link->flags,
|
|
813
|
+
};
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
return Qnil;
|
|
817
|
+
}
|
|
818
|
+
|
|
328
819
|
typedef struct {
|
|
329
820
|
ddog_TracerValueToken *tokens;
|
|
330
821
|
size_t len;
|
|
@@ -638,8 +1129,37 @@ static void set_prepared_metastruct(
|
|
|
638
1129
|
* in TypedData or consumed by a trace chunk.
|
|
639
1130
|
* ======================================================================== */
|
|
640
1131
|
|
|
641
|
-
|
|
642
|
-
|
|
1132
|
+
/* rb_ensure body for span conversion. Event resources are owned by the inner
|
|
1133
|
+
* ensure, while the raw span is owned by the caller's outer ensure. */
|
|
1134
|
+
static VALUE build_rust_span(VALUE arg) {
|
|
1135
|
+
span_conversion_ctx *conversion = (span_conversion_ctx *)arg;
|
|
1136
|
+
VALUE span = conversion->span;
|
|
1137
|
+
|
|
1138
|
+
if ((size_t)conversion->event_count > SIZE_MAX / sizeof(ddog_TracerSpanEvent *)) {
|
|
1139
|
+
rb_raise(rb_eNoMemError, "span event conversion allocation is too large");
|
|
1140
|
+
}
|
|
1141
|
+
size_t event_pointers_size = (size_t)conversion->event_count * sizeof(ddog_TracerSpanEvent *);
|
|
1142
|
+
size_t scratch_alignment = offsetof(span_event_array_scratch_alignment, value);
|
|
1143
|
+
if (event_pointers_size > SIZE_MAX - (scratch_alignment - 1)) {
|
|
1144
|
+
rb_raise(rb_eNoMemError, "span event conversion allocation is too large");
|
|
1145
|
+
}
|
|
1146
|
+
size_t scratch_offset = event_pointers_size == 0
|
|
1147
|
+
? 0
|
|
1148
|
+
: ((event_pointers_size + scratch_alignment - 1) / scratch_alignment) * scratch_alignment;
|
|
1149
|
+
if (conversion->max_array_length > (SIZE_MAX - scratch_offset) / sizeof(span_event_array_scratch)) {
|
|
1150
|
+
rb_raise(rb_eNoMemError, "span event conversion allocation is too large");
|
|
1151
|
+
}
|
|
1152
|
+
/* One allocation means no second Ruby allocation can non-locally exit and
|
|
1153
|
+
* strand the first. rb_ensure owns it from the moment this call returns. */
|
|
1154
|
+
size_t allocation_size = scratch_offset + conversion->max_array_length * sizeof(span_event_array_scratch);
|
|
1155
|
+
if (allocation_size > 0) {
|
|
1156
|
+
conversion->allocation = ruby_xcalloc(1, allocation_size);
|
|
1157
|
+
conversion->events = (ddog_TracerSpanEvent **)conversion->allocation;
|
|
1158
|
+
conversion->array_scratch = (char *)conversion->allocation + scratch_offset;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
build_native_events(conversion);
|
|
1162
|
+
/* Read Ruby ivars */
|
|
643
1163
|
VALUE rb_name = rb_ivar_get(span, at_name_id);
|
|
644
1164
|
VALUE rb_service = rb_ivar_get(span, at_service_id);
|
|
645
1165
|
VALUE rb_resource = rb_ivar_get(span, at_resource_id);
|
|
@@ -649,12 +1169,15 @@ static void convert_ruby_span_to_rust(VALUE span, raw_span_owner *owner) {
|
|
|
649
1169
|
VALUE rb_trace_id = rb_ivar_get(span, at_trace_id_id);
|
|
650
1170
|
VALUE rb_status = rb_ivar_get(span, at_status_id);
|
|
651
1171
|
|
|
1172
|
+
/* Validate scalar strings without borrowing their pointers. Link and
|
|
1173
|
+
* meta_struct normalization below can call arbitrary Ruby code that mutates
|
|
1174
|
+
* these strings and invalidates any prior RSTRING_PTR. */
|
|
652
1175
|
ENFORCE_TYPE(rb_name, T_STRING);
|
|
653
1176
|
if (rb_service != Qnil) ENFORCE_TYPE(rb_service, T_STRING);
|
|
654
1177
|
if (rb_resource != Qnil) ENFORCE_TYPE(rb_resource, T_STRING);
|
|
655
1178
|
if (rb_type != Qnil) ENFORCE_TYPE(rb_type, T_STRING);
|
|
656
1179
|
|
|
657
|
-
/*
|
|
1180
|
+
/* Convert scalars that may call Ruby. */
|
|
658
1181
|
uint64_t span_id = NUM2ULL(rb_span_id);
|
|
659
1182
|
uint64_t parent_id = NUM2ULL(rb_parent_id);
|
|
660
1183
|
int32_t error_val = NUM2INT(rb_status);
|
|
@@ -678,18 +1201,43 @@ static void convert_ruby_span_to_rust(VALUE span, raw_span_owner *owner) {
|
|
|
678
1201
|
if (dur != Qnil) duration_ns = (int64_t)(NUM2DBL(dur) * 1e9);
|
|
679
1202
|
}
|
|
680
1203
|
|
|
681
|
-
/* Structured-value
|
|
682
|
-
* borrowing scalar string pointers or allocating the Rust span. */
|
|
1204
|
+
/* Structured-value and link normalization can call Ruby code. Prepare both
|
|
1205
|
+
* before borrowing scalar string pointers or allocating the Rust span. */
|
|
683
1206
|
prepared_metastruct metastruct = {0};
|
|
684
1207
|
prepare_metastruct(span, &metastruct);
|
|
685
1208
|
|
|
686
|
-
/*
|
|
1209
|
+
/* Snapshot canonical links and all string bytes before allocating Rust
|
|
1210
|
+
* state. rb_protect guarantees partial C-owned snapshots are reclaimed if a
|
|
1211
|
+
* default proc, to_hash, validation, or allocation raises. */
|
|
1212
|
+
VALUE rb_links = rb_ivar_get(span, at_links_id);
|
|
1213
|
+
if (RB_UNLIKELY(!RB_TYPE_P(rb_links, T_ARRAY))) {
|
|
1214
|
+
/* Read @links after prepare_metastruct so a meta_struct callback that
|
|
1215
|
+
* reassigns it is honoured. That ordering means this raise happens with
|
|
1216
|
+
* C-owned metastruct storage already allocated, which the caller's
|
|
1217
|
+
* free_raw_span ensure handler does not reclaim. */
|
|
1218
|
+
free_prepared_metastruct(&metastruct);
|
|
1219
|
+
ENFORCE_TYPE(rb_links, T_ARRAY);
|
|
1220
|
+
}
|
|
1221
|
+
span_links_snapshot links_snapshot = {
|
|
1222
|
+
.rb_links = rb_links,
|
|
1223
|
+
};
|
|
1224
|
+
if (RARRAY_LEN(rb_links) > 0) {
|
|
1225
|
+
int snapshot_state = 0;
|
|
1226
|
+
rb_protect(prepare_span_links_snapshot, (VALUE)&links_snapshot, &snapshot_state);
|
|
1227
|
+
if (snapshot_state) {
|
|
1228
|
+
free_span_links_snapshot(&links_snapshot);
|
|
1229
|
+
free_prepared_metastruct(&metastruct);
|
|
1230
|
+
rb_jump_tag(snapshot_state);
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
/* No Ruby callbacks occur between borrowing these pointers and the FFI call. */
|
|
687
1235
|
ddog_CharSlice name_s = char_slice_from_ruby_string(rb_name);
|
|
688
1236
|
ddog_CharSlice service_s = nullable_char_slice(rb_service);
|
|
689
1237
|
ddog_CharSlice resource_s = nullable_char_slice(rb_resource);
|
|
690
1238
|
ddog_CharSlice type_s = nullable_char_slice(rb_type);
|
|
691
1239
|
|
|
692
|
-
/*
|
|
1240
|
+
/* Create Rust span and immediately consume the stable prepared values. */
|
|
693
1241
|
ddog_TracerSpanFields fields = {
|
|
694
1242
|
.service = service_s,
|
|
695
1243
|
.name = name_s,
|
|
@@ -704,13 +1252,35 @@ static void convert_ruby_span_to_rust(VALUE span, raw_span_owner *owner) {
|
|
|
704
1252
|
.error = error_val,
|
|
705
1253
|
};
|
|
706
1254
|
|
|
707
|
-
ddog_TraceExporterError *err = ddog_tracer_span_new(&owner->span, &fields);
|
|
708
|
-
if (err != NULL)
|
|
1255
|
+
ddog_TraceExporterError *err = ddog_tracer_span_new(&conversion->owner->span, &fields);
|
|
1256
|
+
if (err != NULL) {
|
|
1257
|
+
free_span_links_snapshot(&links_snapshot);
|
|
1258
|
+
free_prepared_metastruct(&metastruct);
|
|
1259
|
+
}
|
|
709
1260
|
check_exporter_error("Failed to create TracerSpan", err);
|
|
710
|
-
set_prepared_metastruct(owner->span, &metastruct);
|
|
711
1261
|
|
|
712
|
-
|
|
713
|
-
|
|
1262
|
+
if (links_snapshot.link_count > 0) {
|
|
1263
|
+
ddog_Slice_TracerSpanLink links_slice = {
|
|
1264
|
+
.ptr = links_snapshot.ffi_links,
|
|
1265
|
+
.len = links_snapshot.link_count,
|
|
1266
|
+
};
|
|
1267
|
+
/* libdatadog validates link strings as UTF-8, matching the native meta
|
|
1268
|
+
* setters. A validation error rejects this span conversion and therefore the
|
|
1269
|
+
* complete batch; send_traces reports it as an InternalErrorResponse. */
|
|
1270
|
+
err = ddog_tracer_span_set_links(conversion->owner->span, links_slice);
|
|
1271
|
+
free_span_links_snapshot(&links_snapshot);
|
|
1272
|
+
if (err != NULL) free_prepared_metastruct(&metastruct);
|
|
1273
|
+
check_exporter_error("Failed to set span links", err);
|
|
1274
|
+
}
|
|
1275
|
+
set_prepared_metastruct(conversion->owner->span, &metastruct);
|
|
1276
|
+
|
|
1277
|
+
for (long i = 0; i < conversion->event_count; i++) {
|
|
1278
|
+
err = ddog_tracer_span_add_event(conversion->owner->span, conversion->events[i]);
|
|
1279
|
+
conversion->events[i] = NULL; /* add_event consumes the event on every path. */
|
|
1280
|
+
check_exporter_error("Failed to attach span event", err);
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
hash_iter_ctx ctx = {.span = conversion->owner->span, .error = NULL, .skipped = 0};
|
|
714
1284
|
|
|
715
1285
|
VALUE rb_meta = rb_ivar_get(span, at_meta_id);
|
|
716
1286
|
if (RB_TYPE_P(rb_meta, T_HASH) && RHASH_SIZE(rb_meta) > 0) {
|
|
@@ -735,6 +1305,24 @@ static void convert_ruby_span_to_rust(VALUE span, raw_span_owner *owner) {
|
|
|
735
1305
|
}
|
|
736
1306
|
}
|
|
737
1307
|
|
|
1308
|
+
return Qnil;
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
static void convert_ruby_span_to_rust(VALUE span, VALUE native_events_supported, raw_span_owner *owner) {
|
|
1312
|
+
span_conversion_ctx ctx = {
|
|
1313
|
+
.span = span,
|
|
1314
|
+
.normalized_events = Qnil,
|
|
1315
|
+
.owner = owner,
|
|
1316
|
+
};
|
|
1317
|
+
if (native_events_supported == Qtrue) {
|
|
1318
|
+
ctx.normalized_events = normalize_span_events(span, &ctx.max_array_length);
|
|
1319
|
+
ctx.event_count = RARRAY_LEN(ctx.normalized_events);
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
rb_ensure(
|
|
1323
|
+
build_rust_span, (VALUE)&ctx,
|
|
1324
|
+
free_span_conversion, (VALUE)&ctx);
|
|
1325
|
+
RB_GC_GUARD(ctx.normalized_events);
|
|
738
1326
|
}
|
|
739
1327
|
|
|
740
1328
|
static VALUE free_raw_span(VALUE arg) {
|
|
@@ -757,7 +1345,7 @@ typedef struct {
|
|
|
757
1345
|
|
|
758
1346
|
static VALUE convert_and_wrap_span(VALUE arg) {
|
|
759
1347
|
wrap_span_ctx *ctx = (wrap_span_ctx *)arg;
|
|
760
|
-
convert_ruby_span_to_rust(ctx->span, &ctx->owner);
|
|
1348
|
+
convert_ruby_span_to_rust(ctx->span, Qtrue, &ctx->owner);
|
|
761
1349
|
|
|
762
1350
|
VALUE wrapped = TypedData_Wrap_Struct(
|
|
763
1351
|
tracer_span_class, &tracer_span_typed_data, ctx->owner.span);
|
|
@@ -1021,7 +1609,7 @@ static int check_if_pending_exception(void) {
|
|
|
1021
1609
|
* TraceExporter#_native_send_traces
|
|
1022
1610
|
*
|
|
1023
1611
|
* Ruby signature:
|
|
1024
|
-
* exporter._native_send_traces(traces) -> Array[Response]
|
|
1612
|
+
* exporter._native_send_traces(traces, native_events_supported) -> Array[Response]
|
|
1025
1613
|
*
|
|
1026
1614
|
* +traces+ is an Array of Arrays of Spans:
|
|
1027
1615
|
* [[span, span, ...], [span, ...], ...]
|
|
@@ -1041,6 +1629,7 @@ static int check_if_pending_exception(void) {
|
|
|
1041
1629
|
typedef struct {
|
|
1042
1630
|
const ddog_TraceExporter *exporter;
|
|
1043
1631
|
VALUE traces;
|
|
1632
|
+
VALUE native_events_supported;
|
|
1044
1633
|
long trace_count;
|
|
1045
1634
|
raw_span_owner span_owner;
|
|
1046
1635
|
ddog_TracerTraceChunks *chunks; /* NULL after send consumes it */
|
|
@@ -1068,7 +1657,7 @@ static VALUE build_and_send_traces(VALUE arg) {
|
|
|
1068
1657
|
check_exporter_error("Failed to begin trace chunk", begin_err);
|
|
1069
1658
|
for (long j = 0; j < span_count; j++) {
|
|
1070
1659
|
convert_ruby_span_to_rust(
|
|
1071
|
-
rb_ary_entry(chunk_spans, j), &ctx->span_owner);
|
|
1660
|
+
rb_ary_entry(chunk_spans, j), ctx->native_events_supported, &ctx->span_owner);
|
|
1072
1661
|
|
|
1073
1662
|
ddog_TraceExporterError *push_err =
|
|
1074
1663
|
ddog_tracer_trace_chunks_push_span(ctx->chunks, ctx->span_owner.span);
|
|
@@ -1180,8 +1769,11 @@ static VALUE free_send_resources(VALUE arg) {
|
|
|
1180
1769
|
return Qnil;
|
|
1181
1770
|
}
|
|
1182
1771
|
|
|
1183
|
-
static VALUE _native_send_traces(VALUE self, VALUE traces) {
|
|
1772
|
+
static VALUE _native_send_traces(VALUE self, VALUE traces, VALUE native_events_supported) {
|
|
1184
1773
|
ENFORCE_TYPE(traces, T_ARRAY);
|
|
1774
|
+
if (native_events_supported != Qtrue && native_events_supported != Qfalse) {
|
|
1775
|
+
rb_raise(rb_eTypeError, "native_events_supported must be true or false");
|
|
1776
|
+
}
|
|
1185
1777
|
|
|
1186
1778
|
trace_exporter_t *wrapper;
|
|
1187
1779
|
TypedData_Get_Struct(self, trace_exporter_t, &trace_exporter_typed_data,
|
|
@@ -1210,6 +1802,7 @@ static VALUE _native_send_traces(VALUE self, VALUE traces) {
|
|
|
1210
1802
|
send_traces_ctx ctx = {
|
|
1211
1803
|
.exporter = wrapper->exporter,
|
|
1212
1804
|
.traces = traces,
|
|
1805
|
+
.native_events_supported = native_events_supported,
|
|
1213
1806
|
.trace_count = trace_count,
|
|
1214
1807
|
.span_owner = {.span = NULL},
|
|
1215
1808
|
.chunks = chunks,
|
|
@@ -1252,9 +1845,9 @@ void trace_exporter_init(VALUE tracing_module) {
|
|
|
1252
1845
|
rb_define_singleton_method(trace_exporter_class, "_native_new",
|
|
1253
1846
|
_native_exporter_new, -1);
|
|
1254
1847
|
|
|
1255
|
-
/* Instance: _native_send_traces(traces) -> Array[Response] */
|
|
1848
|
+
/* Instance: _native_send_traces(traces, native_events_supported) -> Array[Response] */
|
|
1256
1849
|
rb_define_method(trace_exporter_class, "_native_send_traces",
|
|
1257
|
-
_native_send_traces,
|
|
1850
|
+
_native_send_traces, 2);
|
|
1258
1851
|
|
|
1259
1852
|
/* Instance: fork safety hooks */
|
|
1260
1853
|
rb_define_method(trace_exporter_class, "_native_before_fork",
|
|
@@ -1292,12 +1885,39 @@ void trace_exporter_init(VALUE tracing_module) {
|
|
|
1292
1885
|
at_status_id = rb_intern("@status");
|
|
1293
1886
|
at_meta_id = rb_intern("@meta");
|
|
1294
1887
|
at_metrics_id = rb_intern("@metrics");
|
|
1888
|
+
at_events_id = rb_intern("@events");
|
|
1889
|
+
at_links_id = rb_intern("@links");
|
|
1295
1890
|
at_metastruct_id = rb_intern("@metastruct");
|
|
1296
1891
|
|
|
1297
1892
|
/* Methods */
|
|
1298
1893
|
id_duration_method = rb_intern("duration");
|
|
1299
|
-
|
|
1300
|
-
|
|
1894
|
+
id_to_a = rb_intern("to_a");
|
|
1895
|
+
id_to_native_format = rb_intern("to_native_format");
|
|
1896
|
+
id_to_hash = rb_intern("to_hash");
|
|
1897
|
+
id_to_h = rb_intern("to_h");
|
|
1898
|
+
id_negative_p = rb_intern("negative?");
|
|
1899
|
+
|
|
1900
|
+
event_name_key = rb_str_new_cstr("name");
|
|
1901
|
+
event_time_key = rb_str_new_cstr("time_unix_nano");
|
|
1902
|
+
event_attributes_key = rb_str_new_cstr("attributes");
|
|
1903
|
+
rb_global_variable(&event_name_key);
|
|
1904
|
+
rb_global_variable(&event_time_key);
|
|
1905
|
+
rb_global_variable(&event_attributes_key);
|
|
1906
|
+
event_type_id = rb_intern("type");
|
|
1907
|
+
event_string_value_id = rb_intern("string_value");
|
|
1908
|
+
event_bool_value_id = rb_intern("bool_value");
|
|
1909
|
+
event_int_value_id = rb_intern("int_value");
|
|
1910
|
+
event_double_value_id = rb_intern("double_value");
|
|
1911
|
+
event_array_value_id = rb_intern("array_value");
|
|
1912
|
+
event_values_id = rb_intern("values");
|
|
1913
|
+
|
|
1914
|
+
/* SpanLink#to_hash fields */
|
|
1915
|
+
link_trace_id_id = rb_intern("trace_id");
|
|
1916
|
+
link_trace_id_high_id = rb_intern("trace_id_high");
|
|
1917
|
+
link_span_id_id = rb_intern("span_id");
|
|
1918
|
+
link_attributes_id = rb_intern("attributes");
|
|
1919
|
+
link_tracestate_id = rb_intern("tracestate");
|
|
1920
|
+
link_flags_id = rb_intern("flags");
|
|
1301
1921
|
|
|
1302
1922
|
/* Response.new */
|
|
1303
1923
|
id_new = rb_intern("new");
|