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;
|
pub use opentelemetry::metrics::UpDownCounter;
|
||||||
use opentelemetry::otel_debug;
|
use opentelemetry::otel_debug;
|
||||||
use opentelemetry::otel_error;
|
use opentelemetry::otel_error;
|
||||||
|
use opentelemetry::trace::Event;
|
||||||
use opentelemetry::trace::Link;
|
use opentelemetry::trace::Link;
|
||||||
use opentelemetry::trace::SpanContext;
|
use opentelemetry::trace::SpanContext;
|
||||||
use opentelemetry::trace::SpanId;
|
use opentelemetry::trace::SpanId;
|
||||||
|
@ -1381,6 +1382,32 @@ impl OtelSpan {
|
||||||
Ok(())
|
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]
|
#[fast]
|
||||||
fn drop_event(&self) {
|
fn drop_event(&self) {
|
||||||
let mut state = self.0.borrow_mut();
|
let mut state = self.0.borrow_mut();
|
||||||
|
|
|
@ -169,6 +169,25 @@ function hrToMs(hr: [number, number]): number {
|
||||||
return (hr[0] * 1e3 + hr[1] / 1e6);
|
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 {
|
interface AsyncContextSnapshot {
|
||||||
__brand: "AsyncContextSnapshot";
|
__brand: "AsyncContextSnapshot";
|
||||||
}
|
}
|
||||||
|
@ -183,7 +202,7 @@ export const currentSnapshot = getAsyncContext;
|
||||||
export const restoreSnapshot = setAsyncContext;
|
export const restoreSnapshot = setAsyncContext;
|
||||||
|
|
||||||
function isDate(value: unknown): value is Date {
|
function isDate(value: unknown): value is Date {
|
||||||
return ObjectPrototypeIsPrototypeOf(value, DatePrototype);
|
return ObjectPrototypeIsPrototypeOf(DatePrototype, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OtelTracer {
|
interface OtelTracer {
|
||||||
|
@ -215,6 +234,11 @@ interface OtelSpan {
|
||||||
|
|
||||||
spanContext(): SpanContext;
|
spanContext(): SpanContext;
|
||||||
setStatus(status: SpanStatusCode, errorDescription: string): void;
|
setStatus(status: SpanStatusCode, errorDescription: string): void;
|
||||||
|
addEvent(
|
||||||
|
name: string,
|
||||||
|
startTime: number,
|
||||||
|
droppedAttributeCount: number,
|
||||||
|
): void;
|
||||||
dropEvent(): void;
|
dropEvent(): void;
|
||||||
end(endTime: number): void;
|
end(endTime: number): void;
|
||||||
}
|
}
|
||||||
|
@ -303,20 +327,13 @@ class Tracer {
|
||||||
context = context ?? CURRENT.get();
|
context = context ?? CURRENT.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
let startTime = options?.startTime;
|
const startTime = timeInputToMs(options?.startTime);
|
||||||
if (startTime && ArrayIsArray(startTime)) {
|
|
||||||
startTime = hrToMs(startTime);
|
|
||||||
} else if (startTime && isDate(startTime)) {
|
|
||||||
startTime = DatePrototypeGetTime(startTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
const parentSpan = context?.getValue(SPAN_KEY) as
|
const parentSpan = context?.getValue(SPAN_KEY) as
|
||||||
| Span
|
| Span
|
||||||
| { spanContext(): SpanContext }
|
| { spanContext(): SpanContext }
|
||||||
| undefined;
|
| undefined;
|
||||||
const attributesCount = options?.attributes
|
const attributesCount = countAttributes(options?.attributes);
|
||||||
? ObjectKeys(options.attributes).length
|
|
||||||
: 0;
|
|
||||||
const parentOtelSpan: OtelSpan | null | undefined = parentSpan !== undefined
|
const parentOtelSpan: OtelSpan | null | undefined = parentSpan !== undefined
|
||||||
? getOtelSpan(parentSpan) ?? undefined
|
? getOtelSpan(parentSpan) ?? undefined
|
||||||
: undefined;
|
: undefined;
|
||||||
|
@ -380,17 +397,27 @@ class Span {
|
||||||
}
|
}
|
||||||
|
|
||||||
addEvent(
|
addEvent(
|
||||||
_name: string,
|
name: string,
|
||||||
_attributesOrStartTime?: Attributes | TimeInput,
|
attributesOrStartTime?: Attributes | TimeInput,
|
||||||
_startTime?: TimeInput,
|
startTime?: TimeInput,
|
||||||
): this {
|
): 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;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
addLink(link: Link): this {
|
addLink(link: Link): this {
|
||||||
const droppedAttributeCount = (link.droppedAttributesCount ?? 0) +
|
const droppedAttributeCount = (link.droppedAttributesCount ?? 0) +
|
||||||
(link.attributes ? ObjectKeys(link.attributes).length : 0);
|
countAttributes(link.attributes);
|
||||||
const valid = op_otel_span_add_link(
|
const valid = op_otel_span_add_link(
|
||||||
this.#otelSpan,
|
this.#otelSpan,
|
||||||
link.context.traceId,
|
link.context.traceId,
|
||||||
|
@ -411,12 +438,7 @@ class Span {
|
||||||
}
|
}
|
||||||
|
|
||||||
end(endTime?: TimeInput): void {
|
end(endTime?: TimeInput): void {
|
||||||
if (endTime && ArrayIsArray(endTime)) {
|
this.#otelSpan?.end(timeInputToMs(endTime) || NaN);
|
||||||
endTime = hrToMs(endTime);
|
|
||||||
} else if (endTime && isDate(endTime)) {
|
|
||||||
endTime = DatePrototypeGetTime(endTime);
|
|
||||||
}
|
|
||||||
this.#otelSpan?.end(endTime || NaN);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isRecording(): boolean {
|
isRecording(): boolean {
|
||||||
|
|
|
@ -56,6 +56,10 @@
|
||||||
"node_http_request": {
|
"node_http_request": {
|
||||||
"args": "run -A main.ts node_http_request.ts",
|
"args": "run -A main.ts node_http_request.ts",
|
||||||
"output": "node_http_request.out"
|
"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