diff --git a/ext/os/lib.rs b/ext/os/lib.rs index f084601678..a2d5a7193b 100644 --- a/ext/os/lib.rs +++ b/ext/os/lib.rs @@ -116,6 +116,12 @@ deno_core::extension!( "op_exit" | "op_set_exit_code" | "op_get_exit_code" => op.with_implementation_from(&deno_core::op_void_sync()), _ => op, + }, + state = |state| { + #[cfg(unix)] + { + state.put(ops::signal::SignalState::default()); + } } ); diff --git a/ext/telemetry/lib.rs b/ext/telemetry/lib.rs index ce3f34a0af..d2f6d1f5ac 100644 --- a/ext/telemetry/lib.rs +++ b/ext/telemetry/lib.rs @@ -42,6 +42,7 @@ use opentelemetry::metrics::InstrumentBuilder; use opentelemetry::metrics::MeterProvider as _; use opentelemetry::otel_debug; use opentelemetry::otel_error; +use opentelemetry::trace::Link; use opentelemetry::trace::SpanContext; use opentelemetry::trace::SpanId; use opentelemetry::trace::SpanKind; @@ -94,6 +95,7 @@ deno_core::extension!( op_otel_span_attribute1, op_otel_span_attribute2, op_otel_span_attribute3, + op_otel_span_add_link, op_otel_span_update_name, op_otel_metric_attribute3, op_otel_metric_record0, @@ -1324,17 +1326,6 @@ impl OtelSpan { } } - #[fast] - fn drop_link(&self) { - let mut state = self.0.borrow_mut(); - match &mut **state { - OtelSpanState::Recording(span) => { - span.links.dropped_count += 1; - } - OtelSpanState::Done(_) => {} - } - } - #[fast] fn end(&self, end_time: f64) { let end_time = if end_time.is_nan() { @@ -1448,6 +1439,48 @@ fn op_otel_span_update_name<'s>( } } +#[op2(fast)] +fn op_otel_span_add_link<'s>( + scope: &mut v8::HandleScope<'s>, + span: v8::Local<'s, v8::Value>, + trace_id: v8::Local<'s, v8::Value>, + span_id: v8::Local<'s, v8::Value>, + #[smi] trace_flags: u8, + is_remote: bool, + #[smi] dropped_attributes_count: u32, +) -> bool { + let trace_id = parse_trace_id(scope, trace_id); + if trace_id == TraceId::INVALID { + return false; + }; + let span_id = parse_span_id(scope, span_id); + if span_id == SpanId::INVALID { + return false; + }; + let span_context = SpanContext::new( + trace_id, + span_id, + TraceFlags::new(trace_flags), + is_remote, + TraceState::NONE, + ); + + let Some(span) = + deno_core::_ops::try_unwrap_cppgc_object::(scope, span) + else { + return true; + }; + let mut state = span.0.borrow_mut(); + if let OtelSpanState::Recording(span) = &mut **state { + span.links.links.push(Link::new( + span_context, + vec![], + dropped_attributes_count, + )); + } + true +} + struct OtelMeter(opentelemetry::metrics::Meter); impl deno_core::GarbageCollected for OtelMeter {} diff --git a/ext/telemetry/telemetry.ts b/ext/telemetry/telemetry.ts index bea16f49d4..7ce44f74ed 100644 --- a/ext/telemetry/telemetry.ts +++ b/ext/telemetry/telemetry.ts @@ -15,6 +15,7 @@ import { op_otel_metric_record2, op_otel_metric_record3, op_otel_metric_wait_to_observe, + op_otel_span_add_link, op_otel_span_attribute1, op_otel_span_attribute2, op_otel_span_attribute3, @@ -186,7 +187,6 @@ interface OtelSpan { spanContext(): SpanContext; setStatus(status: SpanStatusCode, errorDescription: string): void; dropEvent(): void; - dropLink(): void; end(endTime: number): void; } @@ -359,14 +359,24 @@ class Span { return this; } - addLink(_link: Link): Span { - this.#otelSpan?.dropLink(); + addLink(link: Link): Span { + const droppedAttributeCount = (link.droppedAttributesCount ?? 0) + + (link.attributes ? ObjectKeys(link.attributes).length : 0); + const valid = op_otel_span_add_link( + this.#otelSpan, + link.context.traceId, + link.context.spanId, + link.context.traceFlags, + link.context.isRemote ?? false, + droppedAttributeCount, + ); + if (!valid) return this; return this; } addLinks(links: Link[]): Span { for (let i = 0; i < links.length; i++) { - this.#otelSpan?.dropLink(); + this.addLink(links[i]); } return this; } diff --git a/tests/specs/cli/otel_basic/__test__.jsonc b/tests/specs/cli/otel_basic/__test__.jsonc index f9826671e8..4f7992cc11 100644 --- a/tests/specs/cli/otel_basic/__test__.jsonc +++ b/tests/specs/cli/otel_basic/__test__.jsonc @@ -22,6 +22,10 @@ }, "args": "run -A main.ts metric.ts", "output": "metric.out" + }, + "links": { + "args": "run -A main.ts links.ts", + "output": "links.out" } } } diff --git a/tests/specs/cli/otel_basic/links.out b/tests/specs/cli/otel_basic/links.out new file mode 100644 index 0000000000..c3839ea83c --- /dev/null +++ b/tests/specs/cli/otel_basic/links.out @@ -0,0 +1,96 @@ +{ + "spans": [ + { + "traceId": "00000000000000000000000000000001", + "spanId": "0000000000000001", + "traceState": "", + "parentSpanId": "", + "flags": 1, + "name": "example span", + "kind": 1, + "startTimeUnixNano": "[WILDCARD]", + "endTimeUnixNano": "[WILDCARD]", + "attributes": [], + "droppedAttributesCount": 0, + "events": [], + "droppedEventsCount": 0, + "links": [ + { + "traceId": "1234567890abcdef1234567890abcdef", + "spanId": "1234567890abcdef", + "traceState": "", + "attributes": [], + "droppedAttributesCount": 0, + "flags": 1 + } + ], + "droppedLinksCount": 0, + "status": { + "message": "", + "code": 0 + } + }, + { + "traceId": "00000000000000000000000000000002", + "spanId": "0000000000000002", + "traceState": "", + "parentSpanId": "", + "flags": 1, + "name": "example span", + "kind": 1, + "startTimeUnixNano": "[WILDCARD]", + "endTimeUnixNano": "[WILDCARD]", + "attributes": [], + "droppedAttributesCount": 0, + "events": [], + "droppedEventsCount": 0, + "links": [ + { + "traceId": "1234567890abcdef1234567890abcdef", + "spanId": "1234567890abcdef", + "traceState": "", + "attributes": [], + "droppedAttributesCount": 0, + "flags": 1 + } + ], + "droppedLinksCount": 0, + "status": { + "message": "", + "code": 0 + } + }, + { + "traceId": "00000000000000000000000000000003", + "spanId": "0000000000000003", + "traceState": "", + "parentSpanId": "", + "flags": 1, + "name": "example span", + "kind": 1, + "startTimeUnixNano": "[WILDCARD]", + "endTimeUnixNano": "[WILDCARD]", + "attributes": [], + "droppedAttributesCount": 0, + "events": [], + "droppedEventsCount": 0, + "links": [ + { + "traceId": "1234567890abcdef1234567890abcdef", + "spanId": "1234567890abcdef", + "traceState": "", + "attributes": [], + "droppedAttributesCount": 2, + "flags": 1 + } + ], + "droppedLinksCount": 0, + "status": { + "message": "", + "code": 0 + } + } + ], + "logs": [], + "metrics": [] +} diff --git a/tests/specs/cli/otel_basic/links.ts b/tests/specs/cli/otel_basic/links.ts new file mode 100644 index 0000000000..478eb3df97 --- /dev/null +++ b/tests/specs/cli/otel_basic/links.ts @@ -0,0 +1,40 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +import { trace } from "npm:@opentelemetry/api@1.9.0"; + +const tracer = trace.getTracer("example-tracer"); + +const span1 = tracer.startSpan("example span", { + links: [{ + context: { + traceId: "1234567890abcdef1234567890abcdef", + spanId: "1234567890abcdef", + traceFlags: 1, + }, + }], +}); +span1.end(); + +const span2 = tracer.startSpan("example span"); +span2.addLink({ + context: { + traceId: "1234567890abcdef1234567890abcdef", + spanId: "1234567890abcdef", + traceFlags: 1, + }, +}); +span2.end(); + +const span3 = tracer.startSpan("example span"); +span3.addLink({ + context: { + traceId: "1234567890abcdef1234567890abcdef", + spanId: "1234567890abcdef", + traceFlags: 1, + }, + attributes: { + key: "value", + }, + droppedAttributesCount: 1, +}); +span3.end();