mirror of
https://github.com/denoland/deno.git
synced 2025-09-26 12:19:12 +00:00
feat(otel): basic event recording (#28552)
This commit is contained in:
parent
60b502db80
commit
7ac8130854
5 changed files with 185 additions and 21 deletions
|
@ -45,6 +45,7 @@ pub use opentelemetry::metrics::MeterProvider;
|
|||
pub use opentelemetry::metrics::UpDownCounter;
|
||||
use opentelemetry::otel_debug;
|
||||
use opentelemetry::otel_error;
|
||||
use opentelemetry::trace::Event;
|
||||
use opentelemetry::trace::Link;
|
||||
use opentelemetry::trace::SpanContext;
|
||||
use opentelemetry::trace::SpanId;
|
||||
|
@ -1381,6 +1382,32 @@ impl OtelSpan {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[fast]
|
||||
fn add_event(
|
||||
&self,
|
||||
#[string] name: String,
|
||||
start_time: f64,
|
||||
#[smi] dropped_attributes_count: u32,
|
||||
) {
|
||||
let start_time = if start_time.is_nan() {
|
||||
SystemTime::now()
|
||||
} else {
|
||||
SystemTime::UNIX_EPOCH
|
||||
.checked_add(Duration::from_secs_f64(start_time / 1000.0))
|
||||
.unwrap()
|
||||
};
|
||||
let mut state = self.0.borrow_mut();
|
||||
let OtelSpanState::Recording(span) = &mut **state else {
|
||||
return;
|
||||
};
|
||||
span.events.events.push(Event::new(
|
||||
name,
|
||||
start_time,
|
||||
vec![],
|
||||
dropped_attributes_count,
|
||||
));
|
||||
}
|
||||
|
||||
#[fast]
|
||||
fn drop_event(&self) {
|
||||
let mut state = self.0.borrow_mut();
|
||||
|
|
|
@ -169,6 +169,25 @@ function hrToMs(hr: [number, number]): number {
|
|||
return (hr[0] * 1e3 + hr[1] / 1e6);
|
||||
}
|
||||
|
||||
function isTimeInput(input: unknown): input is TimeInput {
|
||||
return typeof input === "number" ||
|
||||
(input && (ArrayIsArray(input) || isDate(input)));
|
||||
}
|
||||
|
||||
function timeInputToMs(input?: TimeInput): number | undefined {
|
||||
if (input === undefined) return;
|
||||
if (ArrayIsArray(input)) {
|
||||
return hrToMs(input);
|
||||
} else if (isDate(input)) {
|
||||
return DatePrototypeGetTime(input);
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
function countAttributes(attributes?: Attributes): number {
|
||||
return attributes ? ObjectKeys(attributes).length : 0;
|
||||
}
|
||||
|
||||
interface AsyncContextSnapshot {
|
||||
__brand: "AsyncContextSnapshot";
|
||||
}
|
||||
|
@ -183,7 +202,7 @@ export const currentSnapshot = getAsyncContext;
|
|||
export const restoreSnapshot = setAsyncContext;
|
||||
|
||||
function isDate(value: unknown): value is Date {
|
||||
return ObjectPrototypeIsPrototypeOf(value, DatePrototype);
|
||||
return ObjectPrototypeIsPrototypeOf(DatePrototype, value);
|
||||
}
|
||||
|
||||
interface OtelTracer {
|
||||
|
@ -215,6 +234,11 @@ interface OtelSpan {
|
|||
|
||||
spanContext(): SpanContext;
|
||||
setStatus(status: SpanStatusCode, errorDescription: string): void;
|
||||
addEvent(
|
||||
name: string,
|
||||
startTime: number,
|
||||
droppedAttributeCount: number,
|
||||
): void;
|
||||
dropEvent(): void;
|
||||
end(endTime: number): void;
|
||||
}
|
||||
|
@ -303,20 +327,13 @@ class Tracer {
|
|||
context = context ?? CURRENT.get();
|
||||
}
|
||||
|
||||
let startTime = options?.startTime;
|
||||
if (startTime && ArrayIsArray(startTime)) {
|
||||
startTime = hrToMs(startTime);
|
||||
} else if (startTime && isDate(startTime)) {
|
||||
startTime = DatePrototypeGetTime(startTime);
|
||||
}
|
||||
const startTime = timeInputToMs(options?.startTime);
|
||||
|
||||
const parentSpan = context?.getValue(SPAN_KEY) as
|
||||
| Span
|
||||
| { spanContext(): SpanContext }
|
||||
| undefined;
|
||||
const attributesCount = options?.attributes
|
||||
? ObjectKeys(options.attributes).length
|
||||
: 0;
|
||||
const attributesCount = countAttributes(options?.attributes);
|
||||
const parentOtelSpan: OtelSpan | null | undefined = parentSpan !== undefined
|
||||
? getOtelSpan(parentSpan) ?? undefined
|
||||
: undefined;
|
||||
|
@ -380,17 +397,27 @@ class Span {
|
|||
}
|
||||
|
||||
addEvent(
|
||||
_name: string,
|
||||
_attributesOrStartTime?: Attributes | TimeInput,
|
||||
_startTime?: TimeInput,
|
||||
name: string,
|
||||
attributesOrStartTime?: Attributes | TimeInput,
|
||||
startTime?: TimeInput,
|
||||
): this {
|
||||
this.#otelSpan?.dropEvent();
|
||||
if (isTimeInput(attributesOrStartTime)) {
|
||||
startTime = attributesOrStartTime;
|
||||
attributesOrStartTime = undefined;
|
||||
}
|
||||
const startTimeMs = timeInputToMs(startTime);
|
||||
|
||||
this.#otelSpan?.addEvent(
|
||||
name,
|
||||
startTimeMs ?? NaN,
|
||||
countAttributes(attributesOrStartTime),
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
addLink(link: Link): this {
|
||||
const droppedAttributeCount = (link.droppedAttributesCount ?? 0) +
|
||||
(link.attributes ? ObjectKeys(link.attributes).length : 0);
|
||||
countAttributes(link.attributes);
|
||||
const valid = op_otel_span_add_link(
|
||||
this.#otelSpan,
|
||||
link.context.traceId,
|
||||
|
@ -411,12 +438,7 @@ class Span {
|
|||
}
|
||||
|
||||
end(endTime?: TimeInput): void {
|
||||
if (endTime && ArrayIsArray(endTime)) {
|
||||
endTime = hrToMs(endTime);
|
||||
} else if (endTime && isDate(endTime)) {
|
||||
endTime = DatePrototypeGetTime(endTime);
|
||||
}
|
||||
this.#otelSpan?.end(endTime || NaN);
|
||||
this.#otelSpan?.end(timeInputToMs(endTime) || NaN);
|
||||
}
|
||||
|
||||
isRecording(): boolean {
|
||||
|
|
|
@ -56,6 +56,10 @@
|
|||
"node_http_request": {
|
||||
"args": "run -A main.ts node_http_request.ts",
|
||||
"output": "node_http_request.out"
|
||||
},
|
||||
"events": {
|
||||
"args": "run -A main.ts events.ts",
|
||||
"output": "events.out"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
90
tests/specs/cli/otel_basic/events.out
Normal file
90
tests/specs/cli/otel_basic/events.out
Normal file
|
@ -0,0 +1,90 @@
|
|||
{
|
||||
"spans": [
|
||||
{
|
||||
"traceId": "00000000000000000000000000000001",
|
||||
"spanId": "0000000000000001",
|
||||
"traceState": "",
|
||||
"parentSpanId": "",
|
||||
"flags": 1,
|
||||
"name": "example span",
|
||||
"kind": 1,
|
||||
"startTimeUnixNano": "[WILDCARD]",
|
||||
"endTimeUnixNano": "[WILDCARD]",
|
||||
"attributes": [],
|
||||
"droppedAttributesCount": 0,
|
||||
"events": [
|
||||
{
|
||||
"timeUnixNano": "[WILDCARD]",
|
||||
"name": "example event",
|
||||
"attributes": [],
|
||||
"droppedAttributesCount": 0
|
||||
}
|
||||
],
|
||||
"droppedEventsCount": 0,
|
||||
"links": [],
|
||||
"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": [
|
||||
{
|
||||
"timeUnixNano": "[WILDCARD]",
|
||||
"name": "example event",
|
||||
"attributes": [],
|
||||
"droppedAttributesCount": 1
|
||||
}
|
||||
],
|
||||
"droppedEventsCount": 0,
|
||||
"links": [],
|
||||
"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": [
|
||||
{
|
||||
"timeUnixNano": "[WILDCARD]",
|
||||
"name": "example event",
|
||||
"attributes": [],
|
||||
"droppedAttributesCount": 1
|
||||
}
|
||||
],
|
||||
"droppedEventsCount": 0,
|
||||
"links": [],
|
||||
"droppedLinksCount": 0,
|
||||
"status": {
|
||||
"message": "",
|
||||
"code": 0
|
||||
}
|
||||
}
|
||||
],
|
||||
"logs": [],
|
||||
"metrics": []
|
||||
}
|
21
tests/specs/cli/otel_basic/events.ts
Normal file
21
tests/specs/cli/otel_basic/events.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
// 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");
|
||||
span1.addEvent("example event");
|
||||
span1.end();
|
||||
|
||||
const span2 = tracer.startSpan("example span");
|
||||
span2.addEvent("example event", {
|
||||
key: "value",
|
||||
});
|
||||
span2.end();
|
||||
|
||||
const span3 = tracer.startSpan("example span");
|
||||
span3.addEvent("example event", {
|
||||
key: "value",
|
||||
}, new Date());
|
||||
span3.end();
|
Loading…
Add table
Add a link
Reference in a new issue