Skip to content

Commit

Permalink
feat(unstable): add metrics to otel (#27143)
Browse files Browse the repository at this point in the history
Refs: #26852

Initial support for exporting metrics.

Co-authored-by: Luca Casonato <[email protected]>
  • Loading branch information
devsnek and lucacasonato authored Dec 2, 2024
1 parent 6dd2d5e commit 7c03677
Show file tree
Hide file tree
Showing 11 changed files with 1,174 additions and 35 deletions.
748 changes: 720 additions & 28 deletions ext/telemetry/lib.rs

Large diffs are not rendered by default.

280 changes: 277 additions & 3 deletions ext/telemetry/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,23 @@ import {
op_otel_instrumentation_scope_enter,
op_otel_instrumentation_scope_enter_builtin,
op_otel_log,
op_otel_metrics_data_point_attribute,
op_otel_metrics_data_point_attribute2,
op_otel_metrics_data_point_attribute3,
op_otel_metrics_gauge,
op_otel_metrics_histogram,
op_otel_metrics_histogram_data_point,
op_otel_metrics_histogram_data_point_entry1,
op_otel_metrics_histogram_data_point_entry2,
op_otel_metrics_histogram_data_point_entry3,
op_otel_metrics_histogram_data_point_entry_final,
op_otel_metrics_resource_attribute,
op_otel_metrics_resource_attribute2,
op_otel_metrics_resource_attribute3,
op_otel_metrics_scope,
op_otel_metrics_submit,
op_otel_metrics_sum,
op_otel_metrics_sum_or_gauge_data_point,
op_otel_span_attribute,
op_otel_span_attribute2,
op_otel_span_attribute3,
Expand Down Expand Up @@ -186,7 +203,7 @@ const instrumentationScopes = new SafeWeakMap<
>();
let activeInstrumentationLibrary: WeakRef<InstrumentationLibrary> | null = null;

function submit(
function submitSpan(
spanId: string | Uint8Array,
traceId: string | Uint8Array,
traceFlags: number,
Expand Down Expand Up @@ -411,7 +428,7 @@ export class Span {

endSpan = (span: Span) => {
const endTime = now();
submit(
submitSpan(
span.#spanId,
span.#traceId,
span.#traceFlags,
Expand Down Expand Up @@ -571,7 +588,7 @@ class SpanExporter {
for (let i = 0; i < spans.length; i += 1) {
const span = spans[i];
const context = span.spanContext();
submit(
submitSpan(
context.spanId,
context.traceId,
context.traceFlags,
Expand Down Expand Up @@ -671,6 +688,262 @@ class ContextManager {
}
}

function attributeValue(value: IAnyValue) {
return value.boolValue ?? value.stringValue ?? value.doubleValue ??
value.intValue;
}

function submitMetrics(resource, scopeMetrics) {
let i = 0;
while (i < resource.attributes.length) {
if (i + 2 < resource.attributes.length) {
op_otel_metrics_resource_attribute3(
resource.attributes.length,
resource.attributes[i].key,
attributeValue(resource.attributes[i].value),
resource.attributes[i + 1].key,
attributeValue(resource.attributes[i + 1].value),
resource.attributes[i + 2].key,
attributeValue(resource.attributes[i + 2].value),
);
i += 3;
} else if (i + 1 < resource.attributes.length) {
op_otel_metrics_resource_attribute2(
resource.attributes.length,
resource.attributes[i].key,
attributeValue(resource.attributes[i].value),
resource.attributes[i + 1].key,
attributeValue(resource.attributes[i + 1].value),
);
i += 2;
} else {
op_otel_metrics_resource_attribute(
resource.attributes.length,
resource.attributes[i].key,
attributeValue(resource.attributes[i].value),
);
i += 1;
}
}

for (let smi = 0; smi < scopeMetrics.length; smi += 1) {
const { scope, metrics } = scopeMetrics[smi];

op_otel_metrics_scope(scope.name, scope.schemaUrl, scope.version);

for (let mi = 0; mi < metrics.length; mi += 1) {
const metric = metrics[mi];
switch (metric.dataPointType) {
case 3:
op_otel_metrics_sum(
metric.descriptor.name,
// deno-lint-ignore prefer-primordials
metric.descriptor.description,
metric.descriptor.unit,
metric.aggregationTemporality,
metric.isMonotonic,
);
for (let di = 0; di < metric.dataPoints.length; di += 1) {
const dataPoint = metric.dataPoints[di];
op_otel_metrics_sum_or_gauge_data_point(
dataPoint.value,
hrToSecs(dataPoint.startTime),
hrToSecs(dataPoint.endTime),
);
const attributes = ObjectEntries(dataPoint.attributes);
let i = 0;
while (i < attributes.length) {
if (i + 2 < attributes.length) {
op_otel_metrics_data_point_attribute3(
attributes.length,
attributes[i][0],
attributes[i][1],
attributes[i + 1][0],
attributes[i + 1][1],
attributes[i + 2][0],
attributes[i + 2][1],
);
i += 3;
} else if (i + 1 < attributes.length) {
op_otel_metrics_data_point_attribute2(
attributes.length,
attributes[i][0],
attributes[i][1],
attributes[i + 1][0],
attributes[i + 1][1],
);
i += 2;
} else {
op_otel_metrics_data_point_attribute(
attributes.length,
attributes[i][0],
attributes[i][1],
);
i += 1;
}
}
}
break;
case 2:
op_otel_metrics_gauge(
metric.descriptor.name,
// deno-lint-ignore prefer-primordials
metric.descriptor.description,
metric.descriptor.unit,
);
for (let di = 0; di < metric.dataPoints.length; di += 1) {
const dataPoint = metric.dataPoints[di];
op_otel_metrics_sum_or_gauge_data_point(
dataPoint.value,
hrToSecs(dataPoint.startTime),
hrToSecs(dataPoint.endTime),
);
const attributes = ObjectEntries(dataPoint.attributes);
let i = 0;
while (i < attributes.length) {
if (i + 2 < attributes.length) {
op_otel_metrics_data_point_attribute3(
attributes.length,
attributes[i][0],
attributes[i][1],
attributes[i + 1][0],
attributes[i + 1][1],
attributes[i + 2][0],
attributes[i + 2][1],
);
i += 3;
} else if (i + 1 < attributes.length) {
op_otel_metrics_data_point_attribute2(
attributes.length,
attributes[i][0],
attributes[i][1],
attributes[i + 1][0],
attributes[i + 1][1],
);
i += 2;
} else {
op_otel_metrics_data_point_attribute(
attributes.length,
attributes[i][0],
attributes[i][1],
);
i += 1;
}
}
}
break;
case 0:
op_otel_metrics_histogram(
metric.descriptor.name,
// deno-lint-ignore prefer-primordials
metric.descriptor.description,
metric.descriptor.unit,
metric.aggregationTemporality,
);
for (let di = 0; di < metric.dataPoints.length; di += 1) {
const dataPoint = metric.dataPoints[di];
const { boundaries, counts } = dataPoint.value.buckets;
op_otel_metrics_histogram_data_point(
dataPoint.value.count,
dataPoint.value.min ?? NaN,
dataPoint.value.max ?? NaN,
dataPoint.value.sum,
hrToSecs(dataPoint.startTime),
hrToSecs(dataPoint.endTime),
boundaries.length,
);
let j = 0;
while (j < boundaries.length) {
if (j + 3 < boundaries.length) {
op_otel_metrics_histogram_data_point_entry3(
counts[j],
boundaries[j],
counts[j + 1],
boundaries[j + 1],
counts[j + 2],
boundaries[j + 2],
);
j += 3;
} else if (j + 2 < boundaries.length) {
op_otel_metrics_histogram_data_point_entry2(
counts[j],
boundaries[j],
counts[j + 1],
boundaries[j + 1],
);
j += 2;
} else {
op_otel_metrics_histogram_data_point_entry1(
counts[j],
boundaries[j],
);
j += 1;
}
}
op_otel_metrics_histogram_data_point_entry_final(counts[j]);
const attributes = ObjectEntries(dataPoint.attributes);
let i = 0;
while (i < attributes.length) {
if (i + 2 < attributes.length) {
op_otel_metrics_data_point_attribute3(
attributes.length,
attributes[i][0],
attributes[i][1],
attributes[i + 1][0],
attributes[i + 1][1],
attributes[i + 2][0],
attributes[i + 2][1],
);
i += 3;
} else if (i + 1 < attributes.length) {
op_otel_metrics_data_point_attribute2(
attributes.length,
attributes[i][0],
attributes[i][1],
attributes[i + 1][0],
attributes[i + 1][1],
);
i += 2;
} else {
op_otel_metrics_data_point_attribute(
attributes.length,
attributes[i][0],
attributes[i][1],
);
i += 1;
}
}
}
break;
default:
continue;
}
}
}

op_otel_metrics_submit();
}

class MetricExporter {
export(metrics, resultCallback: (result: ExportResult) => void) {
try {
submitMetrics(metrics.resource, metrics.scopeMetrics);
resultCallback({ code: 0 });
} catch (error) {
resultCallback({
code: 1,
error: ObjectPrototypeIsPrototypeOf(error, Error)
? error as Error
: new Error(String(error)),
});
}
}

async forceFlush() {}

async shutdown() {}
}

const otelConsoleConfig = {
ignore: 0,
capture: 1,
Expand Down Expand Up @@ -708,4 +981,5 @@ export function bootstrap(
export const telemetry = {
SpanExporter,
ContextManager,
MetricExporter,
};

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions tests/specs/cli/otel_basic/__test__.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
{
"args": "run -A main.ts uncaught.ts",
"output": "uncaught.out"
},
{
"args": "run -A main.ts metric.ts",
"output": "metric.out"
}
]
}
3 changes: 2 additions & 1 deletion tests/specs/cli/otel_basic/basic.out
Original file line number Diff line number Diff line change
Expand Up @@ -188,5 +188,6 @@
"traceId": "00000000000000000000000000000003",
"spanId": "1000000000000002"
}
]
],
"metrics": []
}
3 changes: 2 additions & 1 deletion tests/specs/cli/otel_basic/deno_dot_exit.out
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@
"traceId": "",
"spanId": ""
}
]
],
"metrics": []
}
6 changes: 6 additions & 0 deletions tests/specs/cli/otel_basic/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
const data = {
spans: [],
logs: [],
metrics: [],
};

const server = Deno.serve(
Expand Down Expand Up @@ -45,6 +46,11 @@ const server = Deno.serve(
data.spans.push(...sSpans.spans);
});
});
body.resourceMetrics?.forEach((rMetrics) => {
rMetrics.scopeMetrics.forEach((sMetrics) => {
data.metrics.push(...sMetrics.metrics);
});
});
return Response.json({ partialSuccess: {} }, { status: 200 });
},
},
Expand Down
Loading

0 comments on commit 7c03677

Please sign in to comment.