@opentelemetry/sdk-metrics
Version:
136 lines • 5.95 kB
JavaScript
;
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.PeriodicExportingMetricReader = void 0;
const api = require("@opentelemetry/api");
const core_1 = require("@opentelemetry/core");
const MetricReader_1 = require("./MetricReader");
const utils_1 = require("../utils");
const MetricData_1 = require("./MetricData");
/**
* {@link MetricReader} which collects metrics based on a user-configurable time interval, and passes the metrics to
* the configured {@link PushMetricExporter}
*/
class PeriodicExportingMetricReader extends MetricReader_1.MetricReader {
_interval;
_exporter;
_exportInterval;
_exportTimeout;
constructor(options) {
const { exporter, exportIntervalMillis = 60000, metricProducers, cardinalityLimits, } = options;
let { exportTimeoutMillis = 30000 } = options;
super({
aggregationSelector: exporter.selectAggregation?.bind(exporter),
aggregationTemporalitySelector: exporter.selectAggregationTemporality?.bind(exporter),
metricProducers,
cardinalitySelector: (instrumentType) => {
const limits = {
default: 2000,
...cardinalityLimits,
};
switch (instrumentType) {
case MetricData_1.InstrumentType.COUNTER:
return limits.counter ?? limits.default;
case MetricData_1.InstrumentType.GAUGE:
return limits.gauge ?? limits.default;
case MetricData_1.InstrumentType.HISTOGRAM:
return limits.histogram ?? limits.default;
case MetricData_1.InstrumentType.OBSERVABLE_COUNTER:
return limits.observableCounter ?? limits.default;
case MetricData_1.InstrumentType.OBSERVABLE_UP_DOWN_COUNTER:
return limits.observableUpDownCounter ?? limits.default;
case MetricData_1.InstrumentType.OBSERVABLE_GAUGE:
return limits.observableGauge ?? limits.default;
case MetricData_1.InstrumentType.UP_DOWN_COUNTER:
return limits.upDownCounter ?? limits.default;
default:
return limits.default;
}
},
});
if (exportIntervalMillis <= 0) {
throw Error('exportIntervalMillis must be greater than 0');
}
if (exportTimeoutMillis <= 0) {
throw Error('exportTimeoutMillis must be greater than 0');
}
if (exportIntervalMillis < exportTimeoutMillis) {
if ('exportIntervalMillis' in options &&
'exportTimeoutMillis' in options) {
// An invalid combination of values was explicitly provided.
throw Error('exportIntervalMillis must be greater than or equal to exportTimeoutMillis');
}
else {
// An invalid combination of value was implicitly provided.
api.diag.info(`Timeout of ${exportTimeoutMillis} exceeds the interval of ${exportIntervalMillis}. Clamping timeout to interval duration.`);
exportTimeoutMillis = exportIntervalMillis;
}
}
this._exportInterval = exportIntervalMillis;
this._exportTimeout = exportTimeoutMillis;
this._exporter = exporter;
}
async _runOnce() {
try {
await (0, utils_1.callWithTimeout)(this._doRun(), this._exportTimeout);
}
catch (err) {
if (err instanceof utils_1.TimeoutError) {
api.diag.error('Export took longer than %s milliseconds and timed out.', this._exportTimeout);
return;
}
(0, core_1.globalErrorHandler)(err);
}
}
async _doRun() {
const { resourceMetrics, errors } = await this.collect({
timeoutMillis: this._exportTimeout,
});
if (errors.length > 0) {
api.diag.error('PeriodicExportingMetricReader: metrics collection errors', ...errors);
}
if (resourceMetrics.resource.asyncAttributesPending) {
try {
await resourceMetrics.resource.waitForAsyncAttributes?.();
}
catch (e) {
api.diag.debug('Error while resolving async portion of resource: ', e);
(0, core_1.globalErrorHandler)(e);
}
}
if (resourceMetrics.scopeMetrics.length === 0) {
return;
}
const result = await core_1.internal._export(this._exporter, resourceMetrics);
if (result.code !== core_1.ExportResultCode.SUCCESS) {
throw new Error(`PeriodicExportingMetricReader: metrics export failed (error ${result.error})`);
}
}
onInitialized() {
// start running the interval as soon as this reader is initialized and keep handle for shutdown.
this._interval = setInterval(() => {
// this._runOnce never rejects. Using void operator to suppress @typescript-eslint/no-floating-promises.
void this._runOnce();
}, this._exportInterval);
// depending on runtime, this may be a 'number' or NodeJS.Timeout
if (typeof this._interval !== 'number') {
this._interval.unref();
}
}
async onForceFlush() {
await this._runOnce();
await this._exporter.forceFlush();
}
async onShutdown() {
if (this._interval) {
clearInterval(this._interval);
}
await this.onForceFlush();
await this._exporter.shutdown();
}
}
exports.PeriodicExportingMetricReader = PeriodicExportingMetricReader;
//# sourceMappingURL=PeriodicExportingMetricReader.js.map