Skip to content

Commit 7c03677

Browse files
feat(unstable): add metrics to otel (denoland#27143)
Refs: denoland#26852 Initial support for exporting metrics. Co-authored-by: Luca Casonato <[email protected]>
1 parent 6dd2d5e commit 7c03677

File tree

11 files changed

+1174
-35
lines changed

11 files changed

+1174
-35
lines changed

ext/telemetry/lib.rs

Lines changed: 720 additions & 28 deletions
Large diffs are not rendered by default.

ext/telemetry/telemetry.ts

Lines changed: 277 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,23 @@ import {
77
op_otel_instrumentation_scope_enter,
88
op_otel_instrumentation_scope_enter_builtin,
99
op_otel_log,
10+
op_otel_metrics_data_point_attribute,
11+
op_otel_metrics_data_point_attribute2,
12+
op_otel_metrics_data_point_attribute3,
13+
op_otel_metrics_gauge,
14+
op_otel_metrics_histogram,
15+
op_otel_metrics_histogram_data_point,
16+
op_otel_metrics_histogram_data_point_entry1,
17+
op_otel_metrics_histogram_data_point_entry2,
18+
op_otel_metrics_histogram_data_point_entry3,
19+
op_otel_metrics_histogram_data_point_entry_final,
20+
op_otel_metrics_resource_attribute,
21+
op_otel_metrics_resource_attribute2,
22+
op_otel_metrics_resource_attribute3,
23+
op_otel_metrics_scope,
24+
op_otel_metrics_submit,
25+
op_otel_metrics_sum,
26+
op_otel_metrics_sum_or_gauge_data_point,
1027
op_otel_span_attribute,
1128
op_otel_span_attribute2,
1229
op_otel_span_attribute3,
@@ -186,7 +203,7 @@ const instrumentationScopes = new SafeWeakMap<
186203
>();
187204
let activeInstrumentationLibrary: WeakRef<InstrumentationLibrary> | null = null;
188205

189-
function submit(
206+
function submitSpan(
190207
spanId: string | Uint8Array,
191208
traceId: string | Uint8Array,
192209
traceFlags: number,
@@ -411,7 +428,7 @@ export class Span {
411428

412429
endSpan = (span: Span) => {
413430
const endTime = now();
414-
submit(
431+
submitSpan(
415432
span.#spanId,
416433
span.#traceId,
417434
span.#traceFlags,
@@ -571,7 +588,7 @@ class SpanExporter {
571588
for (let i = 0; i < spans.length; i += 1) {
572589
const span = spans[i];
573590
const context = span.spanContext();
574-
submit(
591+
submitSpan(
575592
context.spanId,
576593
context.traceId,
577594
context.traceFlags,
@@ -671,6 +688,262 @@ class ContextManager {
671688
}
672689
}
673690

691+
function attributeValue(value: IAnyValue) {
692+
return value.boolValue ?? value.stringValue ?? value.doubleValue ??
693+
value.intValue;
694+
}
695+
696+
function submitMetrics(resource, scopeMetrics) {
697+
let i = 0;
698+
while (i < resource.attributes.length) {
699+
if (i + 2 < resource.attributes.length) {
700+
op_otel_metrics_resource_attribute3(
701+
resource.attributes.length,
702+
resource.attributes[i].key,
703+
attributeValue(resource.attributes[i].value),
704+
resource.attributes[i + 1].key,
705+
attributeValue(resource.attributes[i + 1].value),
706+
resource.attributes[i + 2].key,
707+
attributeValue(resource.attributes[i + 2].value),
708+
);
709+
i += 3;
710+
} else if (i + 1 < resource.attributes.length) {
711+
op_otel_metrics_resource_attribute2(
712+
resource.attributes.length,
713+
resource.attributes[i].key,
714+
attributeValue(resource.attributes[i].value),
715+
resource.attributes[i + 1].key,
716+
attributeValue(resource.attributes[i + 1].value),
717+
);
718+
i += 2;
719+
} else {
720+
op_otel_metrics_resource_attribute(
721+
resource.attributes.length,
722+
resource.attributes[i].key,
723+
attributeValue(resource.attributes[i].value),
724+
);
725+
i += 1;
726+
}
727+
}
728+
729+
for (let smi = 0; smi < scopeMetrics.length; smi += 1) {
730+
const { scope, metrics } = scopeMetrics[smi];
731+
732+
op_otel_metrics_scope(scope.name, scope.schemaUrl, scope.version);
733+
734+
for (let mi = 0; mi < metrics.length; mi += 1) {
735+
const metric = metrics[mi];
736+
switch (metric.dataPointType) {
737+
case 3:
738+
op_otel_metrics_sum(
739+
metric.descriptor.name,
740+
// deno-lint-ignore prefer-primordials
741+
metric.descriptor.description,
742+
metric.descriptor.unit,
743+
metric.aggregationTemporality,
744+
metric.isMonotonic,
745+
);
746+
for (let di = 0; di < metric.dataPoints.length; di += 1) {
747+
const dataPoint = metric.dataPoints[di];
748+
op_otel_metrics_sum_or_gauge_data_point(
749+
dataPoint.value,
750+
hrToSecs(dataPoint.startTime),
751+
hrToSecs(dataPoint.endTime),
752+
);
753+
const attributes = ObjectEntries(dataPoint.attributes);
754+
let i = 0;
755+
while (i < attributes.length) {
756+
if (i + 2 < attributes.length) {
757+
op_otel_metrics_data_point_attribute3(
758+
attributes.length,
759+
attributes[i][0],
760+
attributes[i][1],
761+
attributes[i + 1][0],
762+
attributes[i + 1][1],
763+
attributes[i + 2][0],
764+
attributes[i + 2][1],
765+
);
766+
i += 3;
767+
} else if (i + 1 < attributes.length) {
768+
op_otel_metrics_data_point_attribute2(
769+
attributes.length,
770+
attributes[i][0],
771+
attributes[i][1],
772+
attributes[i + 1][0],
773+
attributes[i + 1][1],
774+
);
775+
i += 2;
776+
} else {
777+
op_otel_metrics_data_point_attribute(
778+
attributes.length,
779+
attributes[i][0],
780+
attributes[i][1],
781+
);
782+
i += 1;
783+
}
784+
}
785+
}
786+
break;
787+
case 2:
788+
op_otel_metrics_gauge(
789+
metric.descriptor.name,
790+
// deno-lint-ignore prefer-primordials
791+
metric.descriptor.description,
792+
metric.descriptor.unit,
793+
);
794+
for (let di = 0; di < metric.dataPoints.length; di += 1) {
795+
const dataPoint = metric.dataPoints[di];
796+
op_otel_metrics_sum_or_gauge_data_point(
797+
dataPoint.value,
798+
hrToSecs(dataPoint.startTime),
799+
hrToSecs(dataPoint.endTime),
800+
);
801+
const attributes = ObjectEntries(dataPoint.attributes);
802+
let i = 0;
803+
while (i < attributes.length) {
804+
if (i + 2 < attributes.length) {
805+
op_otel_metrics_data_point_attribute3(
806+
attributes.length,
807+
attributes[i][0],
808+
attributes[i][1],
809+
attributes[i + 1][0],
810+
attributes[i + 1][1],
811+
attributes[i + 2][0],
812+
attributes[i + 2][1],
813+
);
814+
i += 3;
815+
} else if (i + 1 < attributes.length) {
816+
op_otel_metrics_data_point_attribute2(
817+
attributes.length,
818+
attributes[i][0],
819+
attributes[i][1],
820+
attributes[i + 1][0],
821+
attributes[i + 1][1],
822+
);
823+
i += 2;
824+
} else {
825+
op_otel_metrics_data_point_attribute(
826+
attributes.length,
827+
attributes[i][0],
828+
attributes[i][1],
829+
);
830+
i += 1;
831+
}
832+
}
833+
}
834+
break;
835+
case 0:
836+
op_otel_metrics_histogram(
837+
metric.descriptor.name,
838+
// deno-lint-ignore prefer-primordials
839+
metric.descriptor.description,
840+
metric.descriptor.unit,
841+
metric.aggregationTemporality,
842+
);
843+
for (let di = 0; di < metric.dataPoints.length; di += 1) {
844+
const dataPoint = metric.dataPoints[di];
845+
const { boundaries, counts } = dataPoint.value.buckets;
846+
op_otel_metrics_histogram_data_point(
847+
dataPoint.value.count,
848+
dataPoint.value.min ?? NaN,
849+
dataPoint.value.max ?? NaN,
850+
dataPoint.value.sum,
851+
hrToSecs(dataPoint.startTime),
852+
hrToSecs(dataPoint.endTime),
853+
boundaries.length,
854+
);
855+
let j = 0;
856+
while (j < boundaries.length) {
857+
if (j + 3 < boundaries.length) {
858+
op_otel_metrics_histogram_data_point_entry3(
859+
counts[j],
860+
boundaries[j],
861+
counts[j + 1],
862+
boundaries[j + 1],
863+
counts[j + 2],
864+
boundaries[j + 2],
865+
);
866+
j += 3;
867+
} else if (j + 2 < boundaries.length) {
868+
op_otel_metrics_histogram_data_point_entry2(
869+
counts[j],
870+
boundaries[j],
871+
counts[j + 1],
872+
boundaries[j + 1],
873+
);
874+
j += 2;
875+
} else {
876+
op_otel_metrics_histogram_data_point_entry1(
877+
counts[j],
878+
boundaries[j],
879+
);
880+
j += 1;
881+
}
882+
}
883+
op_otel_metrics_histogram_data_point_entry_final(counts[j]);
884+
const attributes = ObjectEntries(dataPoint.attributes);
885+
let i = 0;
886+
while (i < attributes.length) {
887+
if (i + 2 < attributes.length) {
888+
op_otel_metrics_data_point_attribute3(
889+
attributes.length,
890+
attributes[i][0],
891+
attributes[i][1],
892+
attributes[i + 1][0],
893+
attributes[i + 1][1],
894+
attributes[i + 2][0],
895+
attributes[i + 2][1],
896+
);
897+
i += 3;
898+
} else if (i + 1 < attributes.length) {
899+
op_otel_metrics_data_point_attribute2(
900+
attributes.length,
901+
attributes[i][0],
902+
attributes[i][1],
903+
attributes[i + 1][0],
904+
attributes[i + 1][1],
905+
);
906+
i += 2;
907+
} else {
908+
op_otel_metrics_data_point_attribute(
909+
attributes.length,
910+
attributes[i][0],
911+
attributes[i][1],
912+
);
913+
i += 1;
914+
}
915+
}
916+
}
917+
break;
918+
default:
919+
continue;
920+
}
921+
}
922+
}
923+
924+
op_otel_metrics_submit();
925+
}
926+
927+
class MetricExporter {
928+
export(metrics, resultCallback: (result: ExportResult) => void) {
929+
try {
930+
submitMetrics(metrics.resource, metrics.scopeMetrics);
931+
resultCallback({ code: 0 });
932+
} catch (error) {
933+
resultCallback({
934+
code: 1,
935+
error: ObjectPrototypeIsPrototypeOf(error, Error)
936+
? error as Error
937+
: new Error(String(error)),
938+
});
939+
}
940+
}
941+
942+
async forceFlush() {}
943+
944+
async shutdown() {}
945+
}
946+
674947
const otelConsoleConfig = {
675948
ignore: 0,
676949
capture: 1,
@@ -708,4 +981,5 @@ export function bootstrap(
708981
export const telemetry = {
709982
SpanExporter,
710983
ContextManager,
984+
MetricExporter,
711985
};

tests/registry/npm/@opentelemetry/sdk-metrics/registry.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

tests/specs/cli/otel_basic/__test__.jsonc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
{
1616
"args": "run -A main.ts uncaught.ts",
1717
"output": "uncaught.out"
18+
},
19+
{
20+
"args": "run -A main.ts metric.ts",
21+
"output": "metric.out"
1822
}
1923
]
2024
}

tests/specs/cli/otel_basic/basic.out

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,5 +188,6 @@
188188
"traceId": "00000000000000000000000000000003",
189189
"spanId": "1000000000000002"
190190
}
191-
]
191+
],
192+
"metrics": []
192193
}

tests/specs/cli/otel_basic/deno_dot_exit.out

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@
1515
"traceId": "",
1616
"spanId": ""
1717
}
18-
]
18+
],
19+
"metrics": []
1920
}

tests/specs/cli/otel_basic/main.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
const data = {
44
spans: [],
55
logs: [],
6+
metrics: [],
67
};
78

89
const server = Deno.serve(
@@ -45,6 +46,11 @@ const server = Deno.serve(
4546
data.spans.push(...sSpans.spans);
4647
});
4748
});
49+
body.resourceMetrics?.forEach((rMetrics) => {
50+
rMetrics.scopeMetrics.forEach((sMetrics) => {
51+
data.metrics.push(...sMetrics.metrics);
52+
});
53+
});
4854
return Response.json({ partialSuccess: {} }, { status: 200 });
4955
},
5056
},

0 commit comments

Comments
 (0)