mirror of
https://github.com/denoland/deno.git
synced 2025-01-03 04:48:52 -05:00
feat(unstable): add OTEL MeterProvider (#27240)
This commit replaces `Deno.telemetry.MetricsExporter` with `Deno.telemetry.MeterProvider`. Signed-off-by: Luca Casonato <hello@lcas.dev> Co-authored-by: snek <snek@deno.com>
This commit is contained in:
parent
91150706d8
commit
f4e321342f
6 changed files with 1764 additions and 933 deletions
|
@ -1006,6 +1006,8 @@ impl Flags {
|
|||
OtelConfig {
|
||||
tracing_enabled: !disabled
|
||||
&& otel_var("OTEL_DENO_TRACING").unwrap_or(default),
|
||||
metrics_enabled: !disabled
|
||||
&& otel_var("OTEL_DENO_METRICS").unwrap_or(default),
|
||||
console: match std::env::var("OTEL_DENO_CONSOLE").as_deref() {
|
||||
Ok(_) if disabled => OtelConsoleConfig::Ignore,
|
||||
Ok("ignore") => OtelConsoleConfig::Ignore,
|
||||
|
|
1451
ext/telemetry/lib.rs
1451
ext/telemetry/lib.rs
File diff suppressed because it is too large
Load diff
|
@ -7,23 +7,24 @@ 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_metric_attribute3,
|
||||
op_otel_metric_create_counter,
|
||||
op_otel_metric_create_gauge,
|
||||
op_otel_metric_create_histogram,
|
||||
op_otel_metric_create_observable_counter,
|
||||
op_otel_metric_create_observable_gauge,
|
||||
op_otel_metric_create_observable_up_down_counter,
|
||||
op_otel_metric_create_up_down_counter,
|
||||
op_otel_metric_observable_record0,
|
||||
op_otel_metric_observable_record1,
|
||||
op_otel_metric_observable_record2,
|
||||
op_otel_metric_observable_record3,
|
||||
op_otel_metric_observation_done,
|
||||
op_otel_metric_record0,
|
||||
op_otel_metric_record1,
|
||||
op_otel_metric_record2,
|
||||
op_otel_metric_record3,
|
||||
op_otel_metric_wait_to_observe,
|
||||
op_otel_span_attribute,
|
||||
op_otel_span_attribute2,
|
||||
op_otel_span_attribute3,
|
||||
|
@ -36,25 +37,32 @@ import { Console } from "ext:deno_console/01_console.js";
|
|||
import { performance } from "ext:deno_web/15_performance.js";
|
||||
|
||||
const {
|
||||
SafeWeakMap,
|
||||
Array,
|
||||
ObjectEntries,
|
||||
ReflectApply,
|
||||
SymbolFor,
|
||||
ArrayPrototypePush,
|
||||
Error,
|
||||
Uint8Array,
|
||||
TypedArrayPrototypeSubarray,
|
||||
ObjectAssign,
|
||||
ObjectDefineProperty,
|
||||
WeakRefPrototypeDeref,
|
||||
ObjectEntries,
|
||||
ObjectPrototypeIsPrototypeOf,
|
||||
ReflectApply,
|
||||
SafeIterator,
|
||||
SafeMap,
|
||||
SafePromiseAll,
|
||||
SafeSet,
|
||||
SafeWeakMap,
|
||||
SafeWeakRef,
|
||||
SafeWeakSet,
|
||||
String,
|
||||
StringPrototypePadStart,
|
||||
ObjectPrototypeIsPrototypeOf,
|
||||
SafeWeakRef,
|
||||
SymbolFor,
|
||||
TypedArrayPrototypeSubarray,
|
||||
Uint8Array,
|
||||
WeakRefPrototypeDeref,
|
||||
} = primordials;
|
||||
const { AsyncVariable, setAsyncContext } = core;
|
||||
|
||||
export let TRACING_ENABLED = false;
|
||||
export let METRICS_ENABLED = false;
|
||||
let DETERMINISTIC = false;
|
||||
|
||||
// Note: These start at 0 in the JS library,
|
||||
|
@ -202,30 +210,9 @@ const instrumentationScopes = new SafeWeakMap<
|
|||
>();
|
||||
let activeInstrumentationLibrary: WeakRef<InstrumentationLibrary> | null = null;
|
||||
|
||||
function submitSpan(
|
||||
spanId: string | Uint8Array,
|
||||
traceId: string | Uint8Array,
|
||||
traceFlags: number,
|
||||
parentSpanId: string | Uint8Array | null,
|
||||
span: Omit<
|
||||
ReadableSpan,
|
||||
| "spanContext"
|
||||
| "startTime"
|
||||
| "endTime"
|
||||
| "parentSpanId"
|
||||
| "duration"
|
||||
| "ended"
|
||||
| "resource"
|
||||
>,
|
||||
startTime: number,
|
||||
endTime: number,
|
||||
function activateInstrumentationLibrary(
|
||||
instrumentationLibrary: InstrumentationLibrary,
|
||||
) {
|
||||
if (!TRACING_ENABLED) return;
|
||||
if (!(traceFlags & TRACE_FLAG_SAMPLED)) return;
|
||||
|
||||
// TODO(@lucacasonato): `resource` is ignored for now, should we implement it?
|
||||
|
||||
const instrumentationLibrary = span.instrumentationLibrary;
|
||||
if (
|
||||
!activeInstrumentationLibrary ||
|
||||
WeakRefPrototypeDeref(activeInstrumentationLibrary) !==
|
||||
|
@ -255,6 +242,32 @@ function submitSpan(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function submitSpan(
|
||||
spanId: string | Uint8Array,
|
||||
traceId: string | Uint8Array,
|
||||
traceFlags: number,
|
||||
parentSpanId: string | Uint8Array | null,
|
||||
span: Omit<
|
||||
ReadableSpan,
|
||||
| "spanContext"
|
||||
| "startTime"
|
||||
| "endTime"
|
||||
| "parentSpanId"
|
||||
| "duration"
|
||||
| "ended"
|
||||
| "resource"
|
||||
>,
|
||||
startTime: number,
|
||||
endTime: number,
|
||||
) {
|
||||
if (!TRACING_ENABLED) return;
|
||||
if (!(traceFlags & TRACE_FLAG_SAMPLED)) return;
|
||||
|
||||
// TODO(@lucacasonato): `resource` is ignored for now, should we implement it?
|
||||
|
||||
activateInstrumentationLibrary(span.instrumentationLibrary);
|
||||
|
||||
op_otel_span_start(
|
||||
traceId,
|
||||
|
@ -368,7 +381,7 @@ export let endSpan: (span: Span) => void;
|
|||
|
||||
export class Span {
|
||||
#traceId: string | Uint8Array;
|
||||
#spanId: Uint8Array;
|
||||
#spanId: string | Uint8Array;
|
||||
#traceFlags = TRACE_FLAG_SAMPLED;
|
||||
|
||||
#spanContext: SpanContext | null = null;
|
||||
|
@ -687,260 +700,510 @@ class ContextManager {
|
|||
}
|
||||
}
|
||||
|
||||
function attributeValue(value: IAnyValue) {
|
||||
return value.boolValue ?? value.stringValue ?? value.doubleValue ??
|
||||
value.intValue;
|
||||
// metrics
|
||||
|
||||
interface MeterOptions {
|
||||
schemaUrl?: string;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
interface MetricOptions {
|
||||
description?: string;
|
||||
|
||||
unit?: string;
|
||||
|
||||
valueType?: ValueType;
|
||||
|
||||
advice?: MetricAdvice;
|
||||
}
|
||||
|
||||
enum ValueType {
|
||||
INT = 0,
|
||||
DOUBLE = 1,
|
||||
}
|
||||
|
||||
interface MetricAdvice {
|
||||
/**
|
||||
* Hint the explicit bucket boundaries for SDK if the metric is been
|
||||
* aggregated with a HistogramAggregator.
|
||||
*/
|
||||
explicitBucketBoundaries?: number[];
|
||||
}
|
||||
|
||||
export class MeterProvider {
|
||||
getMeter(name: string, version?: string, options?: MeterOptions): Meter {
|
||||
return new Meter({ name, version, schemaUrl: options?.schemaUrl });
|
||||
}
|
||||
}
|
||||
|
||||
type MetricAttributes = Attributes;
|
||||
|
||||
type Instrument = { __key: "instrument" };
|
||||
|
||||
let batchResultHasObservables: (
|
||||
res: BatchObservableResult,
|
||||
observables: Observable[],
|
||||
) => boolean;
|
||||
|
||||
class BatchObservableResult {
|
||||
#observables: WeakSet<Observable>;
|
||||
|
||||
constructor(observables: WeakSet<Observable>) {
|
||||
this.#observables = observables;
|
||||
}
|
||||
|
||||
for (let smi = 0; smi < scopeMetrics.length; smi += 1) {
|
||||
const { scope, metrics } = scopeMetrics[smi];
|
||||
static {
|
||||
batchResultHasObservables = (cb, observables) => {
|
||||
for (const observable of new SafeIterator(observables)) {
|
||||
if (!cb.#observables.has(observable)) return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
op_otel_metrics_scope(scope.name, scope.schemaUrl, scope.version);
|
||||
observe(
|
||||
metric: Observable,
|
||||
value: number,
|
||||
attributes?: MetricAttributes,
|
||||
): void {
|
||||
if (!this.#observables.has(metric)) return;
|
||||
getObservableResult(metric).observe(value, attributes);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
const BATCH_CALLBACKS = new SafeMap<
|
||||
BatchObservableCallback,
|
||||
BatchObservableResult
|
||||
>();
|
||||
const INDIVIDUAL_CALLBACKS = new SafeMap<Observable, Set<ObservableCallback>>();
|
||||
|
||||
class Meter {
|
||||
#instrumentationLibrary: InstrumentationLibrary;
|
||||
|
||||
constructor(instrumentationLibrary: InstrumentationLibrary) {
|
||||
this.#instrumentationLibrary = instrumentationLibrary;
|
||||
}
|
||||
|
||||
createCounter(
|
||||
name: string,
|
||||
options?: MetricOptions,
|
||||
): Counter {
|
||||
if (options?.valueType !== undefined && options?.valueType !== 1) {
|
||||
throw new Error("Only valueType: DOUBLE is supported");
|
||||
}
|
||||
if (!METRICS_ENABLED) return new Counter(null, false);
|
||||
activateInstrumentationLibrary(this.#instrumentationLibrary);
|
||||
const instrument = op_otel_metric_create_counter(
|
||||
name,
|
||||
// deno-lint-ignore prefer-primordials
|
||||
options?.description,
|
||||
options?.unit,
|
||||
) as Instrument;
|
||||
return new Counter(instrument, false);
|
||||
}
|
||||
|
||||
createUpDownCounter(
|
||||
name: string,
|
||||
options?: MetricOptions,
|
||||
): Counter {
|
||||
if (options?.valueType !== undefined && options?.valueType !== 1) {
|
||||
throw new Error("Only valueType: DOUBLE is supported");
|
||||
}
|
||||
if (!METRICS_ENABLED) return new Counter(null, true);
|
||||
activateInstrumentationLibrary(this.#instrumentationLibrary);
|
||||
const instrument = op_otel_metric_create_up_down_counter(
|
||||
name,
|
||||
// deno-lint-ignore prefer-primordials
|
||||
options?.description,
|
||||
options?.unit,
|
||||
) as Instrument;
|
||||
return new Counter(instrument, true);
|
||||
}
|
||||
|
||||
createGauge(
|
||||
name: string,
|
||||
options?: MetricOptions,
|
||||
): Gauge {
|
||||
if (options?.valueType !== undefined && options?.valueType !== 1) {
|
||||
throw new Error("Only valueType: DOUBLE is supported");
|
||||
}
|
||||
if (!METRICS_ENABLED) return new Gauge(null);
|
||||
activateInstrumentationLibrary(this.#instrumentationLibrary);
|
||||
const instrument = op_otel_metric_create_gauge(
|
||||
name,
|
||||
// deno-lint-ignore prefer-primordials
|
||||
options?.description,
|
||||
options?.unit,
|
||||
) as Instrument;
|
||||
return new Gauge(instrument);
|
||||
}
|
||||
|
||||
createHistogram(
|
||||
name: string,
|
||||
options?: MetricOptions,
|
||||
): Histogram {
|
||||
if (options?.valueType !== undefined && options?.valueType !== 1) {
|
||||
throw new Error("Only valueType: DOUBLE is supported");
|
||||
}
|
||||
if (!METRICS_ENABLED) return new Histogram(null);
|
||||
activateInstrumentationLibrary(this.#instrumentationLibrary);
|
||||
const instrument = op_otel_metric_create_histogram(
|
||||
name,
|
||||
// deno-lint-ignore prefer-primordials
|
||||
options?.description,
|
||||
options?.unit,
|
||||
options?.advice?.explicitBucketBoundaries,
|
||||
) as Instrument;
|
||||
return new Histogram(instrument);
|
||||
}
|
||||
|
||||
createObservableCounter(
|
||||
name: string,
|
||||
options?: MetricOptions,
|
||||
): Observable {
|
||||
if (options?.valueType !== undefined && options?.valueType !== 1) {
|
||||
throw new Error("Only valueType: DOUBLE is supported");
|
||||
}
|
||||
if (!METRICS_ENABLED) new Observable(new ObservableResult(null, true));
|
||||
activateInstrumentationLibrary(this.#instrumentationLibrary);
|
||||
const instrument = op_otel_metric_create_observable_counter(
|
||||
name,
|
||||
// deno-lint-ignore prefer-primordials
|
||||
options?.description,
|
||||
options?.unit,
|
||||
) as Instrument;
|
||||
return new Observable(new ObservableResult(instrument, true));
|
||||
}
|
||||
|
||||
createObservableGauge(
|
||||
name: string,
|
||||
options?: MetricOptions,
|
||||
): Observable {
|
||||
if (options?.valueType !== undefined && options?.valueType !== 1) {
|
||||
throw new Error("Only valueType: DOUBLE is supported");
|
||||
}
|
||||
if (!METRICS_ENABLED) new Observable(new ObservableResult(null, false));
|
||||
activateInstrumentationLibrary(this.#instrumentationLibrary);
|
||||
const instrument = op_otel_metric_create_observable_gauge(
|
||||
name,
|
||||
// deno-lint-ignore prefer-primordials
|
||||
options?.description,
|
||||
options?.unit,
|
||||
) as Instrument;
|
||||
return new Observable(new ObservableResult(instrument, false));
|
||||
}
|
||||
|
||||
createObservableUpDownCounter(
|
||||
name: string,
|
||||
options?: MetricOptions,
|
||||
): Observable {
|
||||
if (options?.valueType !== undefined && options?.valueType !== 1) {
|
||||
throw new Error("Only valueType: DOUBLE is supported");
|
||||
}
|
||||
if (!METRICS_ENABLED) new Observable(new ObservableResult(null, false));
|
||||
activateInstrumentationLibrary(this.#instrumentationLibrary);
|
||||
const instrument = op_otel_metric_create_observable_up_down_counter(
|
||||
name,
|
||||
// deno-lint-ignore prefer-primordials
|
||||
options?.description,
|
||||
options?.unit,
|
||||
) as Instrument;
|
||||
return new Observable(new ObservableResult(instrument, false));
|
||||
}
|
||||
|
||||
addBatchObservableCallback(
|
||||
callback: BatchObservableCallback,
|
||||
observables: Observable[],
|
||||
): void {
|
||||
if (!METRICS_ENABLED) return;
|
||||
const result = new BatchObservableResult(new SafeWeakSet(observables));
|
||||
startObserving();
|
||||
BATCH_CALLBACKS.set(callback, result);
|
||||
}
|
||||
|
||||
removeBatchObservableCallback(
|
||||
callback: BatchObservableCallback,
|
||||
observables: Observable[],
|
||||
): void {
|
||||
if (!METRICS_ENABLED) return;
|
||||
const result = BATCH_CALLBACKS.get(callback);
|
||||
if (result && batchResultHasObservables(result, observables)) {
|
||||
BATCH_CALLBACKS.delete(callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type BatchObservableCallback = (
|
||||
observableResult: BatchObservableResult,
|
||||
) => void | Promise<void>;
|
||||
|
||||
function record(
|
||||
instrument: Instrument | null,
|
||||
value: number,
|
||||
attributes?: MetricAttributes,
|
||||
) {
|
||||
if (instrument === null) return;
|
||||
if (attributes === undefined) {
|
||||
op_otel_metric_record0(instrument, value);
|
||||
} else {
|
||||
const attrs = ObjectEntries(attributes);
|
||||
if (attrs.length === 0) {
|
||||
op_otel_metric_record0(instrument, value);
|
||||
}
|
||||
let i = 0;
|
||||
while (i < attrs.length) {
|
||||
const remaining = attrs.length - i;
|
||||
if (remaining > 3) {
|
||||
op_otel_metric_attribute3(
|
||||
instrument,
|
||||
value,
|
||||
attrs[i][0],
|
||||
attrs[i][1],
|
||||
attrs[i + 1][0],
|
||||
attrs[i + 1][1],
|
||||
attrs[i + 2][0],
|
||||
attrs[i + 2][1],
|
||||
);
|
||||
i += 3;
|
||||
} else if (remaining === 3) {
|
||||
op_otel_metric_record3(
|
||||
instrument,
|
||||
value,
|
||||
attrs[i][0],
|
||||
attrs[i][1],
|
||||
attrs[i + 1][0],
|
||||
attrs[i + 1][1],
|
||||
attrs[i + 2][0],
|
||||
attrs[i + 2][1],
|
||||
);
|
||||
i += 3;
|
||||
} else if (remaining === 2) {
|
||||
op_otel_metric_record2(
|
||||
instrument,
|
||||
value,
|
||||
attrs[i][0],
|
||||
attrs[i][1],
|
||||
attrs[i + 1][0],
|
||||
attrs[i + 1][1],
|
||||
);
|
||||
i += 2;
|
||||
} else if (remaining === 1) {
|
||||
op_otel_metric_record1(
|
||||
instrument,
|
||||
value,
|
||||
attrs[i][0],
|
||||
attrs[i][1],
|
||||
);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)),
|
||||
});
|
||||
function recordObservable(
|
||||
instrument: Instrument | null,
|
||||
value: number,
|
||||
attributes?: MetricAttributes,
|
||||
) {
|
||||
if (instrument === null) return;
|
||||
if (attributes === undefined) {
|
||||
op_otel_metric_observable_record0(instrument, value);
|
||||
} else {
|
||||
const attrs = ObjectEntries(attributes);
|
||||
if (attrs.length === 0) {
|
||||
op_otel_metric_observable_record0(instrument, value);
|
||||
}
|
||||
let i = 0;
|
||||
while (i < attrs.length) {
|
||||
const remaining = attrs.length - i;
|
||||
if (remaining > 3) {
|
||||
op_otel_metric_attribute3(
|
||||
instrument,
|
||||
value,
|
||||
attrs[i][0],
|
||||
attrs[i][1],
|
||||
attrs[i + 1][0],
|
||||
attrs[i + 1][1],
|
||||
attrs[i + 2][0],
|
||||
attrs[i + 2][1],
|
||||
);
|
||||
i += 3;
|
||||
} else if (remaining === 3) {
|
||||
op_otel_metric_observable_record3(
|
||||
instrument,
|
||||
value,
|
||||
attrs[i][0],
|
||||
attrs[i][1],
|
||||
attrs[i + 1][0],
|
||||
attrs[i + 1][1],
|
||||
attrs[i + 2][0],
|
||||
attrs[i + 2][1],
|
||||
);
|
||||
i += 3;
|
||||
} else if (remaining === 2) {
|
||||
op_otel_metric_observable_record2(
|
||||
instrument,
|
||||
value,
|
||||
attrs[i][0],
|
||||
attrs[i][1],
|
||||
attrs[i + 1][0],
|
||||
attrs[i + 1][1],
|
||||
);
|
||||
i += 2;
|
||||
} else if (remaining === 1) {
|
||||
op_otel_metric_observable_record1(
|
||||
instrument,
|
||||
value,
|
||||
attrs[i][0],
|
||||
attrs[i][1],
|
||||
);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async forceFlush() {}
|
||||
class Counter {
|
||||
#instrument: Instrument | null;
|
||||
#upDown: boolean;
|
||||
|
||||
async shutdown() {}
|
||||
constructor(instrument: Instrument | null, upDown: boolean) {
|
||||
this.#instrument = instrument;
|
||||
this.#upDown = upDown;
|
||||
}
|
||||
|
||||
add(value: number, attributes?: MetricAttributes, _context?: Context): void {
|
||||
if (value < 0 && !this.#upDown) {
|
||||
throw new Error("Counter can only be incremented");
|
||||
}
|
||||
record(this.#instrument, value, attributes);
|
||||
}
|
||||
}
|
||||
|
||||
class Gauge {
|
||||
#instrument: Instrument | null;
|
||||
|
||||
constructor(instrument: Instrument | null) {
|
||||
this.#instrument = instrument;
|
||||
}
|
||||
|
||||
record(
|
||||
value: number,
|
||||
attributes?: MetricAttributes,
|
||||
_context?: Context,
|
||||
): void {
|
||||
record(this.#instrument, value, attributes);
|
||||
}
|
||||
}
|
||||
|
||||
class Histogram {
|
||||
#instrument: Instrument | null;
|
||||
|
||||
constructor(instrument: Instrument | null) {
|
||||
this.#instrument = instrument;
|
||||
}
|
||||
|
||||
record(
|
||||
value: number,
|
||||
attributes?: MetricAttributes,
|
||||
_context?: Context,
|
||||
): void {
|
||||
record(this.#instrument, value, attributes);
|
||||
}
|
||||
}
|
||||
|
||||
type ObservableCallback = (
|
||||
observableResult: ObservableResult,
|
||||
) => void | Promise<void>;
|
||||
|
||||
let getObservableResult: (observable: Observable) => ObservableResult;
|
||||
|
||||
class Observable {
|
||||
#result: ObservableResult;
|
||||
|
||||
constructor(result: ObservableResult) {
|
||||
this.#result = result;
|
||||
}
|
||||
|
||||
static {
|
||||
getObservableResult = (observable) => observable.#result;
|
||||
}
|
||||
|
||||
addCallback(callback: ObservableCallback): void {
|
||||
const res = INDIVIDUAL_CALLBACKS.get(this);
|
||||
if (res) res.add(callback);
|
||||
else INDIVIDUAL_CALLBACKS.set(this, new SafeSet([callback]));
|
||||
startObserving();
|
||||
}
|
||||
|
||||
removeCallback(callback: ObservableCallback): void {
|
||||
const res = INDIVIDUAL_CALLBACKS.get(this);
|
||||
if (res) res.delete(callback);
|
||||
if (res?.size === 0) INDIVIDUAL_CALLBACKS.delete(this);
|
||||
}
|
||||
}
|
||||
|
||||
class ObservableResult {
|
||||
#instrument: Instrument | null;
|
||||
#isRegularCounter: boolean;
|
||||
|
||||
constructor(instrument: Instrument | null, isRegularCounter: boolean) {
|
||||
this.#instrument = instrument;
|
||||
this.#isRegularCounter = isRegularCounter;
|
||||
}
|
||||
|
||||
observe(
|
||||
this: ObservableResult,
|
||||
value: number,
|
||||
attributes?: MetricAttributes,
|
||||
): void {
|
||||
if (this.#isRegularCounter) {
|
||||
if (value < 0) {
|
||||
throw new Error("Observable counters can only be incremented");
|
||||
}
|
||||
}
|
||||
recordObservable(this.#instrument, value, attributes);
|
||||
}
|
||||
}
|
||||
|
||||
async function observe(): Promise<void> {
|
||||
const promises: Promise<void>[] = [];
|
||||
// Primordials are not needed, because this is a SafeMap.
|
||||
// deno-lint-ignore prefer-primordials
|
||||
for (const { 0: observable, 1: callbacks } of INDIVIDUAL_CALLBACKS) {
|
||||
const result = getObservableResult(observable);
|
||||
// Primordials are not needed, because this is a SafeSet.
|
||||
// deno-lint-ignore prefer-primordials
|
||||
for (const callback of callbacks) {
|
||||
// PromiseTry is not in primordials?
|
||||
// deno-lint-ignore prefer-primordials
|
||||
ArrayPrototypePush(promises, Promise.try(callback, result));
|
||||
}
|
||||
}
|
||||
// Primordials are not needed, because this is a SafeMap.
|
||||
// deno-lint-ignore prefer-primordials
|
||||
for (const { 0: callback, 1: result } of BATCH_CALLBACKS) {
|
||||
// PromiseTry is not in primordials?
|
||||
// deno-lint-ignore prefer-primordials
|
||||
ArrayPrototypePush(promises, Promise.try(callback, result));
|
||||
}
|
||||
await SafePromiseAll(promises);
|
||||
}
|
||||
|
||||
let isObserving = false;
|
||||
function startObserving() {
|
||||
if (!isObserving) {
|
||||
isObserving = true;
|
||||
(async () => {
|
||||
while (true) {
|
||||
const promise = op_otel_metric_wait_to_observe();
|
||||
core.unrefOpPromise(promise);
|
||||
const ok = await promise;
|
||||
if (!ok) break;
|
||||
await observe();
|
||||
op_otel_metric_observation_done();
|
||||
}
|
||||
})();
|
||||
}
|
||||
}
|
||||
|
||||
const otelConsoleConfig = {
|
||||
|
@ -951,14 +1214,21 @@ const otelConsoleConfig = {
|
|||
|
||||
export function bootstrap(
|
||||
config: [
|
||||
0 | 1,
|
||||
0 | 1,
|
||||
typeof otelConsoleConfig[keyof typeof otelConsoleConfig],
|
||||
0 | 1,
|
||||
],
|
||||
): void {
|
||||
const { 0: tracingEnabled, 1: consoleConfig, 2: deterministic } = config;
|
||||
const {
|
||||
0: tracingEnabled,
|
||||
1: metricsEnabled,
|
||||
2: consoleConfig,
|
||||
3: deterministic,
|
||||
} = config;
|
||||
|
||||
TRACING_ENABLED = tracingEnabled === 1;
|
||||
METRICS_ENABLED = metricsEnabled === 1;
|
||||
DETERMINISTIC = deterministic === 1;
|
||||
|
||||
switch (consoleConfig) {
|
||||
|
@ -980,5 +1250,5 @@ export function bootstrap(
|
|||
export const telemetry = {
|
||||
SpanExporter,
|
||||
ContextManager,
|
||||
MetricExporter,
|
||||
MeterProvider,
|
||||
};
|
||||
|
|
|
@ -1,28 +1,27 @@
|
|||
{
|
||||
"steps": [
|
||||
{
|
||||
"tests": {
|
||||
"basic": {
|
||||
"args": "run -A main.ts basic.ts",
|
||||
"output": "basic.out"
|
||||
},
|
||||
{
|
||||
"natural_exit": {
|
||||
"args": "run -A main.ts natural_exit.ts",
|
||||
"output": "natural_exit.out"
|
||||
},
|
||||
{
|
||||
"deno_dot_exit": {
|
||||
"args": "run -A main.ts deno_dot_exit.ts",
|
||||
"output": "deno_dot_exit.out"
|
||||
},
|
||||
{
|
||||
"uncaught": {
|
||||
"args": "run -A main.ts uncaught.ts",
|
||||
"output": "uncaught.out"
|
||||
},
|
||||
{
|
||||
"metric": {
|
||||
"envs": {
|
||||
"OTEL_METRIC_EXPORT_INTERVAL": "1000"
|
||||
},
|
||||
"args": "run -A main.ts metric.ts",
|
||||
"output": "metric.out"
|
||||
},
|
||||
{
|
||||
"args": "run -A --unstable-otel context.ts",
|
||||
"output": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,6 +56,31 @@
|
|||
"isMonotonic": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "gauge",
|
||||
"description": "Example of a Gauge",
|
||||
"unit": "",
|
||||
"metadata": [],
|
||||
"gauge": {
|
||||
"dataPoints": [
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"key": "attribute",
|
||||
"value": {
|
||||
"doubleValue": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"startTimeUnixNano": "[WILDCARD]",
|
||||
"timeUnixNano": "[WILDCARD]",
|
||||
"exemplars": [],
|
||||
"flags": 0,
|
||||
"asDouble": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "histogram",
|
||||
"description": "Example of a Histogram",
|
||||
|
@ -119,6 +144,265 @@
|
|||
],
|
||||
"aggregationTemporality": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "observable_counter",
|
||||
"description": "Example of a ObservableCounter",
|
||||
"unit": "",
|
||||
"metadata": [],
|
||||
"sum": {
|
||||
"dataPoints": [
|
||||
{
|
||||
"attributes": [],
|
||||
"startTimeUnixNano": "[WILDCARD]",
|
||||
"timeUnixNano": "[WILDCARD]",
|
||||
"exemplars": [],
|
||||
"flags": 0,
|
||||
"asDouble": 1
|
||||
}
|
||||
],
|
||||
"aggregationTemporality": 2,
|
||||
"isMonotonic": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "observable_up_down_counter",
|
||||
"description": "Example of a ObservableUpDownCounter",
|
||||
"unit": "",
|
||||
"metadata": [],
|
||||
"sum": {
|
||||
"dataPoints": [
|
||||
{
|
||||
"attributes": [],
|
||||
"startTimeUnixNano": "[WILDCARD]",
|
||||
"timeUnixNano": "[WILDCARD]",
|
||||
"exemplars": [],
|
||||
"flags": 0,
|
||||
"asDouble": 1
|
||||
}
|
||||
],
|
||||
"aggregationTemporality": 2,
|
||||
"isMonotonic": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "observable_gauge",
|
||||
"description": "Example of a ObservableGauge",
|
||||
"unit": "",
|
||||
"metadata": [],
|
||||
"gauge": {
|
||||
"dataPoints": [
|
||||
{
|
||||
"attributes": [],
|
||||
"startTimeUnixNano": "[WILDCARD]",
|
||||
"timeUnixNano": "[WILDCARD]",
|
||||
"exemplars": [],
|
||||
"flags": 0,
|
||||
"asDouble": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "counter",
|
||||
"description": "Example of a Counter",
|
||||
"unit": "",
|
||||
"metadata": [],
|
||||
"sum": {
|
||||
"dataPoints": [
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"key": "attribute",
|
||||
"value": {
|
||||
"doubleValue": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"startTimeUnixNano": "[WILDCARD]",
|
||||
"timeUnixNano": "[WILDCARD]",
|
||||
"exemplars": [],
|
||||
"flags": 0,
|
||||
"asDouble": 1
|
||||
}
|
||||
],
|
||||
"aggregationTemporality": 2,
|
||||
"isMonotonic": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "up_down_counter",
|
||||
"description": "Example of a UpDownCounter",
|
||||
"unit": "",
|
||||
"metadata": [],
|
||||
"sum": {
|
||||
"dataPoints": [
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"key": "attribute",
|
||||
"value": {
|
||||
"doubleValue": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"startTimeUnixNano": "[WILDCARD]",
|
||||
"timeUnixNano": "[WILDCARD]",
|
||||
"exemplars": [],
|
||||
"flags": 0,
|
||||
"asDouble": -1
|
||||
}
|
||||
],
|
||||
"aggregationTemporality": 2,
|
||||
"isMonotonic": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "gauge",
|
||||
"description": "Example of a Gauge",
|
||||
"unit": "",
|
||||
"metadata": [],
|
||||
"gauge": {
|
||||
"dataPoints": [
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"key": "attribute",
|
||||
"value": {
|
||||
"doubleValue": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"startTimeUnixNano": "[WILDCARD]",
|
||||
"timeUnixNano": "[WILDCARD]",
|
||||
"exemplars": [],
|
||||
"flags": 0,
|
||||
"asDouble": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "histogram",
|
||||
"description": "Example of a Histogram",
|
||||
"unit": "",
|
||||
"metadata": [],
|
||||
"histogram": {
|
||||
"dataPoints": [
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"key": "attribute",
|
||||
"value": {
|
||||
"doubleValue": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"startTimeUnixNano": "[WILDCARD]",
|
||||
"timeUnixNano": "[WILDCARD]",
|
||||
"count": 1,
|
||||
"sum": 1,
|
||||
"bucketCounts": [
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"explicitBounds": [
|
||||
0,
|
||||
5,
|
||||
10,
|
||||
25,
|
||||
50,
|
||||
75,
|
||||
100,
|
||||
250,
|
||||
500,
|
||||
750,
|
||||
1000,
|
||||
2500,
|
||||
5000,
|
||||
7500,
|
||||
10000
|
||||
],
|
||||
"exemplars": [],
|
||||
"flags": 0,
|
||||
"min": 1,
|
||||
"max": 1
|
||||
}
|
||||
],
|
||||
"aggregationTemporality": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "observable_counter",
|
||||
"description": "Example of a ObservableCounter",
|
||||
"unit": "",
|
||||
"metadata": [],
|
||||
"sum": {
|
||||
"dataPoints": [
|
||||
{
|
||||
"attributes": [],
|
||||
"startTimeUnixNano": "[WILDCARD]",
|
||||
"timeUnixNano": "[WILDCARD]",
|
||||
"exemplars": [],
|
||||
"flags": 0,
|
||||
"asDouble": 1
|
||||
}
|
||||
],
|
||||
"aggregationTemporality": 2,
|
||||
"isMonotonic": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "observable_up_down_counter",
|
||||
"description": "Example of a ObservableUpDownCounter",
|
||||
"unit": "",
|
||||
"metadata": [],
|
||||
"sum": {
|
||||
"dataPoints": [
|
||||
{
|
||||
"attributes": [],
|
||||
"startTimeUnixNano": "[WILDCARD]",
|
||||
"timeUnixNano": "[WILDCARD]",
|
||||
"exemplars": [],
|
||||
"flags": 0,
|
||||
"asDouble": 1
|
||||
}
|
||||
],
|
||||
"aggregationTemporality": 2,
|
||||
"isMonotonic": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "observable_gauge",
|
||||
"description": "Example of a ObservableGauge",
|
||||
"unit": "",
|
||||
"metadata": [],
|
||||
"gauge": {
|
||||
"dataPoints": [
|
||||
{
|
||||
"attributes": [],
|
||||
"startTimeUnixNano": "[WILDCARD]",
|
||||
"timeUnixNano": "[WILDCARD]",
|
||||
"exemplars": [],
|
||||
"flags": 0,
|
||||
"asDouble": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,18 +1,8 @@
|
|||
import {
|
||||
MeterProvider,
|
||||
PeriodicExportingMetricReader,
|
||||
} from "npm:@opentelemetry/sdk-metrics@1.28.0";
|
||||
import { metrics } from "npm:@opentelemetry/api@1";
|
||||
|
||||
const meterProvider = new MeterProvider();
|
||||
metrics.setGlobalMeterProvider(new Deno.telemetry.MeterProvider());
|
||||
|
||||
meterProvider.addMetricReader(
|
||||
new PeriodicExportingMetricReader({
|
||||
exporter: new Deno.telemetry.MetricExporter(),
|
||||
exportIntervalMillis: 100,
|
||||
}),
|
||||
);
|
||||
|
||||
const meter = meterProvider.getMeter("m");
|
||||
const meter = metrics.getMeter("m");
|
||||
|
||||
const counter = meter.createCounter("counter", {
|
||||
description: "Example of a Counter",
|
||||
|
@ -22,13 +12,82 @@ const upDownCounter = meter.createUpDownCounter("up_down_counter", {
|
|||
description: "Example of a UpDownCounter",
|
||||
});
|
||||
|
||||
const gauge = meter.createGauge("gauge", {
|
||||
description: "Example of a Gauge",
|
||||
});
|
||||
|
||||
const histogram = meter.createHistogram("histogram", {
|
||||
description: "Example of a Histogram",
|
||||
});
|
||||
|
||||
const observableCounterPromise = Promise.withResolvers<void>();
|
||||
const observableCounter = meter.createObservableCounter("observable_counter", {
|
||||
description: "Example of a ObservableCounter",
|
||||
});
|
||||
observableCounter.addCallback((res) => {
|
||||
res.observe(1);
|
||||
observableCounterPromise.resolve();
|
||||
});
|
||||
|
||||
const observableUpDownCounterPromise = Promise.withResolvers<void>();
|
||||
const observableUpDownCounter = meter
|
||||
.createObservableUpDownCounter("observable_up_down_counter", {
|
||||
description: "Example of a ObservableUpDownCounter",
|
||||
});
|
||||
observableUpDownCounter.addCallback((res) => {
|
||||
res.observe(1);
|
||||
observableUpDownCounterPromise.resolve();
|
||||
});
|
||||
|
||||
const observableGaugePromise = Promise.withResolvers<void>();
|
||||
const observableGauge = meter.createObservableGauge("observable_gauge", {
|
||||
description: "Example of a ObservableGauge",
|
||||
});
|
||||
observableGauge.addCallback((res) => {
|
||||
res.observe(1);
|
||||
observableGaugePromise.resolve();
|
||||
});
|
||||
|
||||
const observableCounterBatch = meter.createObservableCounter(
|
||||
"observable_counter_batch",
|
||||
{ description: "Example of a ObservableCounter, written in batch" },
|
||||
);
|
||||
const observableUpDownCounterBatch = meter.createObservableUpDownCounter(
|
||||
"observable_up_down_counter_batch",
|
||||
{ description: "Example of a ObservableUpDownCounter, written in batch" },
|
||||
);
|
||||
const observableGaugeBatch = meter.createObservableGauge(
|
||||
"observable_gauge_batch",
|
||||
{
|
||||
description: "Example of a ObservableGauge, written in batch",
|
||||
},
|
||||
);
|
||||
|
||||
const observableBatchPromise = Promise.withResolvers<void>();
|
||||
meter.addBatchObservableCallback((observer) => {
|
||||
observer.observe(observableCounter, 2);
|
||||
observer.observe(observableUpDownCounter, 2);
|
||||
observer.observe(observableGauge, 2);
|
||||
observableBatchPromise.resolve();
|
||||
}, [
|
||||
observableCounterBatch,
|
||||
observableUpDownCounterBatch,
|
||||
observableGaugeBatch,
|
||||
]);
|
||||
|
||||
const attributes = { attribute: 1 };
|
||||
counter.add(1, attributes);
|
||||
upDownCounter.add(-1, attributes);
|
||||
gauge.record(1, attributes);
|
||||
histogram.record(1, attributes);
|
||||
|
||||
await meterProvider.forceFlush();
|
||||
const timer = setTimeout(() => {}, 100000);
|
||||
|
||||
await Promise.all([
|
||||
observableCounterPromise.promise,
|
||||
observableUpDownCounterPromise.promise,
|
||||
observableGaugePromise.promise,
|
||||
observableBatchPromise.promise,
|
||||
]);
|
||||
|
||||
clearTimeout(timer);
|
||||
|
|
Loading…
Reference in a new issue