@opentelemetry/sdk-metrics
Version:
143 lines • 6.22 kB
JavaScript
"use strict";
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.MetricReader = void 0;
const api = require("@opentelemetry/api");
const utils_1 = require("../utils");
const AggregationSelector_1 = require("./AggregationSelector");
const MetricReaderMetrics_1 = require("./MetricReaderMetrics");
const version_1 = require("../version");
const core_1 = require("@opentelemetry/core");
/**
* A registered reader of metrics that, when linked to a {@link MetricProducer}, offers global
* control over metrics.
*/
class MetricReader {
// Tracks the shutdown state.
// TODO: use BindOncePromise here once a new version of @opentelemetry/core is available.
_shutdown = false;
// Additional MetricProducers which will be combined with the SDK's output
_metricProducers;
// MetricProducer used by this instance which produces metrics from the SDK
_sdkMetricProducer;
// Metrics about the MetricReader itself
_selfObsMetrics;
_aggregationTemporalitySelector;
_aggregationSelector;
_cardinalitySelector;
_otelComponentType;
constructor(options) {
this._aggregationSelector =
options?.aggregationSelector ?? AggregationSelector_1.DEFAULT_AGGREGATION_SELECTOR;
this._aggregationTemporalitySelector =
options?.aggregationTemporalitySelector ??
AggregationSelector_1.DEFAULT_AGGREGATION_TEMPORALITY_SELECTOR;
this._metricProducers = options?.metricProducers ?? [];
this._cardinalitySelector = options?.cardinalitySelector;
this._otelComponentType =
options?.otelComponentType ?? this.constructor.name;
this._selfObsMetrics = new MetricReaderMetrics_1.MetricReaderMetrics(this._otelComponentType, api.createNoopMeter());
}
setMetricProducer(metricProducer) {
if (this._sdkMetricProducer) {
// This check ensures the following requirement from the spec
// (https://opentelemetry.io/docs/specs/otel/metrics/sdk/#metricreader):
// > The SDK MUST NOT allow a `MetricReader` instance to be registered
// > on more than one `MeterProvider` instance.
//
// So while the argument is a `MetricProducer`, the relevant user-level
// error message is about the **MeterProvider**.
throw new Error('MetricReader can not be bound to a MeterProvider again.');
}
this._sdkMetricProducer = metricProducer;
this.onInitialized();
}
_setSelfObsMeterProvider(meterProvider) {
const meter = meterProvider.getMeter('@opentelemetry/sdk-metrics', version_1.VERSION);
this._selfObsMetrics = new MetricReaderMetrics_1.MetricReaderMetrics(this._otelComponentType, meter);
}
selectAggregation(instrumentType) {
return this._aggregationSelector(instrumentType);
}
selectAggregationTemporality(instrumentType) {
return this._aggregationTemporalitySelector(instrumentType);
}
selectCardinalityLimit(instrumentType) {
return this._cardinalitySelector
? this._cardinalitySelector(instrumentType)
: 2000; // default value if no selector is provided
}
/**
* Handle once the SDK has initialized this {@link MetricReader}
* Overriding this method is optional.
*/
onInitialized() {
// Default implementation is empty.
}
async collect(options) {
if (this._sdkMetricProducer === undefined) {
throw new Error('MetricReader is not bound to a MetricProducer');
}
// Subsequent invocations to collect are not allowed. SDKs SHOULD return some failure for these calls.
if (this._shutdown) {
throw new Error('MetricReader is shutdown');
}
const startTime = (0, core_1.hrTime)();
const [sdkCollectionResults, ...additionalCollectionResults] = await Promise.all([
this._sdkMetricProducer.collect({
timeoutMillis: options?.timeoutMillis,
}),
...this._metricProducers.map(producer => producer.collect({
timeoutMillis: options?.timeoutMillis,
})),
]);
const endTime = (0, core_1.hrTime)();
// Merge the results, keeping the SDK's Resource
const errors = sdkCollectionResults.errors.concat(additionalCollectionResults.flatMap(result => result.errors));
const collectDuration = (0, core_1.hrTimeToSeconds)((0, core_1.hrTimeDuration)(startTime, endTime));
this._selfObsMetrics.recordCollection(collectDuration, errors.length > 0
? (errors[0].name ?? 'collect_error')
: undefined);
const resource = sdkCollectionResults.resourceMetrics.resource;
const scopeMetrics = sdkCollectionResults.resourceMetrics.scopeMetrics.concat(additionalCollectionResults.flatMap(result => result.resourceMetrics.scopeMetrics));
return {
resourceMetrics: {
resource,
scopeMetrics,
},
errors,
};
}
async shutdown(options) {
// Do not call shutdown again if it has already been called.
if (this._shutdown) {
api.diag.error('Cannot call shutdown twice.');
return;
}
// No timeout if timeoutMillis is undefined or null.
if (options?.timeoutMillis == null) {
await this.onShutdown();
}
else {
await (0, utils_1.callWithTimeout)(this.onShutdown(), options.timeoutMillis);
}
this._shutdown = true;
}
async forceFlush(options) {
if (this._shutdown) {
api.diag.warn('Cannot forceFlush on already shutdown MetricReader.');
return;
}
// No timeout if timeoutMillis is undefined or null.
if (options?.timeoutMillis == null) {
await this.onForceFlush();
return;
}
await (0, utils_1.callWithTimeout)(this.onForceFlush(), options.timeoutMillis);
}
}
exports.MetricReader = MetricReader;
//# sourceMappingURL=MetricReader.js.map