opentelemetry-js/packages/sdk-metrics/test/export/ConsoleMetricExporter.test.ts

179 lines
6.2 KiB
TypeScript

/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as metrics from '@opentelemetry/api';
import { ExportResult } from '@opentelemetry/core';
import { ConsoleMetricExporter } from '../../src/export/ConsoleMetricExporter';
import { PeriodicExportingMetricReader } from '../../src/export/PeriodicExportingMetricReader';
import { ResourceMetrics } from '../../src/export/MetricData';
import { MeterProvider } from '../../src/MeterProvider';
import { testResource } from '../util';
import * as assert from 'assert';
import * as sinon from 'sinon';
import { assertAggregationTemporalitySelector } from './utils';
import { DEFAULT_AGGREGATION_TEMPORALITY_SELECTOR } from '../../src/export/AggregationSelector';
import { AggregationTemporality, InstrumentType } from '../../src';
async function waitForNumberOfExports(
exporter: sinon.SinonSpy<
[metrics: ResourceMetrics, resultCallback: (result: ExportResult) => void],
void
>,
numberOfExports: number
): Promise<void> {
if (numberOfExports <= 0) {
throw new Error('numberOfExports must be greater than or equal to 0');
}
let totalExports = 0;
while (totalExports < numberOfExports) {
await new Promise(resolve => setTimeout(resolve, 20));
totalExports = exporter.callCount;
}
}
/* eslint-disable no-console */
describe('ConsoleMetricExporter', () => {
describe('export', () => {
let previousConsoleDir: any;
let exporter: ConsoleMetricExporter;
let meterProvider: MeterProvider;
let metricReader: PeriodicExportingMetricReader;
let meter: metrics.Meter;
beforeEach(() => {
previousConsoleDir = console.dir;
console.dir = () => {};
exporter = new ConsoleMetricExporter();
metricReader = new PeriodicExportingMetricReader({
exporter: exporter,
exportIntervalMillis: 100,
exportTimeoutMillis: 100,
});
meterProvider = new MeterProvider({
resource: testResource,
readers: [metricReader],
});
meter = meterProvider.getMeter('ConsoleMetricExporter', '1.0.0');
});
afterEach(async () => {
console.dir = previousConsoleDir;
await metricReader.shutdown();
});
it('should export information about metric', async () => {
const counter = meter.createCounter('counter_total', {
description: 'a test description',
});
const counterAttribute = { key1: 'attributeValue1' };
counter.add(10, counterAttribute);
counter.add(10, counterAttribute);
const histogram = meter.createHistogram('histogram', {
description: 'a histogram',
});
histogram.record(10);
histogram.record(100);
histogram.record(1000);
const spyConsole = sinon.spy(console, 'dir');
const spyExport = sinon.spy(exporter, 'export');
await waitForNumberOfExports(spyExport, 1);
const resourceMetrics = spyExport.args[0];
const firstResourceMetric = resourceMetrics[0];
const consoleArgs = spyConsole.args[0];
const consoleMetric = consoleArgs[0];
const keys = Object.keys(consoleMetric).sort().join(',');
const expectedKeys = ['dataPointType', 'dataPoints', 'descriptor'].join(
','
);
assert.ok(
firstResourceMetric.resource.attributes.resourceKey === 'my-resource',
'resourceKey'
);
assert.ok(keys === expectedKeys, 'expectedKeys');
assert.ok(consoleMetric.descriptor.name === 'counter_total', 'name');
assert.ok(
consoleMetric.descriptor.description === 'a test description',
'description'
);
assert.ok(consoleMetric.descriptor.type === 'COUNTER', 'type');
assert.ok(consoleMetric.descriptor.unit === '', 'unit');
assert.ok(consoleMetric.descriptor.valueType === 1, 'valueType');
assert.ok(
consoleMetric.dataPoints[0].attributes.key1 === 'attributeValue1',
'ensure metric attributes exists'
);
assert.ok(spyExport.calledOnce);
});
});
describe('constructor', () => {
it('with no arguments should select cumulative temporality', () => {
const exporter = new ConsoleMetricExporter();
assertAggregationTemporalitySelector(
exporter,
DEFAULT_AGGREGATION_TEMPORALITY_SELECTOR
);
});
it('with empty options should select cumulative temporality', () => {
const exporter = new ConsoleMetricExporter({});
assertAggregationTemporalitySelector(
exporter,
DEFAULT_AGGREGATION_TEMPORALITY_SELECTOR
);
});
it('with cumulative preference should select cumulative temporality', () => {
const exporter = new ConsoleMetricExporter({
temporalitySelector: _ => AggregationTemporality.CUMULATIVE,
});
assertAggregationTemporalitySelector(
exporter,
_ => AggregationTemporality.CUMULATIVE
);
});
it('with mixed preference should select matching temporality', () => {
// use delta-ish example as a representation of a commonly used "mixed" preference.
const selector = (instrumentType: InstrumentType) => {
switch (instrumentType) {
case InstrumentType.COUNTER:
case InstrumentType.OBSERVABLE_COUNTER:
case InstrumentType.GAUGE:
case InstrumentType.HISTOGRAM:
case InstrumentType.OBSERVABLE_GAUGE:
return AggregationTemporality.DELTA;
case InstrumentType.UP_DOWN_COUNTER:
case InstrumentType.OBSERVABLE_UP_DOWN_COUNTER:
return AggregationTemporality.CUMULATIVE;
}
};
const exporter = new ConsoleMetricExporter({
temporalitySelector: selector,
});
assertAggregationTemporalitySelector(exporter, selector);
});
});
});