From a9df5672cf205b9b276e17a241530a516e3531af Mon Sep 17 00:00:00 2001 From: Brandon Gonzalez Date: Thu, 19 Sep 2019 14:46:07 -0400 Subject: [PATCH] feat(metrics): adds NoopMeter implementation (#278) * feat(metrics): adds NoopMeter implementation Resolves https://github.com/open-telemetry/opentelemetry-js/issues/264 * yarn fix * rename test file * fix the build attempt 1 * respond to comments * fix the build part 2 * yarn fix --- packages/opentelemetry-core/src/index.ts | 1 + .../src/metrics/NoopMeter.ts | 139 ++++++++++++++++++ .../test/metrics/NoopMeter.test.ts | 90 ++++++++++++ 3 files changed, 230 insertions(+) create mode 100644 packages/opentelemetry-core/src/metrics/NoopMeter.ts create mode 100644 packages/opentelemetry-core/test/metrics/NoopMeter.test.ts diff --git a/packages/opentelemetry-core/src/index.ts b/packages/opentelemetry-core/src/index.ts index a515efed42a..56c477f91c3 100644 --- a/packages/opentelemetry-core/src/index.ts +++ b/packages/opentelemetry-core/src/index.ts @@ -32,3 +32,4 @@ export * from './trace/sampler/ProbabilitySampler'; export * from './trace/spancontext-utils'; export * from './trace/TracerDelegate'; export * from './trace/TraceState'; +export * from './metrics/NoopMeter'; diff --git a/packages/opentelemetry-core/src/metrics/NoopMeter.ts b/packages/opentelemetry-core/src/metrics/NoopMeter.ts new file mode 100644 index 00000000000..ccff9f13586 --- /dev/null +++ b/packages/opentelemetry-core/src/metrics/NoopMeter.ts @@ -0,0 +1,139 @@ +/*! + * Copyright 2019, 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 { + CounterHandle, + DistributedContext, + GaugeHandle, + Meter, + Metric, + MetricOptions, + MeasureHandle, + SpanContext, +} from '@opentelemetry/types'; + +/** + * NoopMeter is a noop implementation of the {@link Meter} interface. It reuses constant + * NoopMetrics for all of its methods. + */ +export class NoopMeter implements Meter { + constructor() {} + + /** + * Returns constant noop measure. + * @param name the name of the metric. + * @param [options] the metric options. + */ + createMeasure(name: string, options?: MetricOptions): Metric { + return NOOP_MEASURE_METRIC; + } + + /** + * Returns a constant noop counter. + * @param name the name of the metric. + * @param [options] the metric options. + */ + createCounter(name: string, options?: MetricOptions): Metric { + return NOOP_COUNTER_METRIC; + } + + /** + * Returns a constant gauge metric. + * @param name the name of the metric. + * @param [options] the metric options. + */ + createGauge(name: string, options?: MetricOptions): Metric { + return NOOP_GAUGE_METRIC; + } +} + +export class NoopMetric implements Metric { + private readonly _handle: T; + + constructor(handle: T) { + this._handle = handle; + } + /** + * Returns a Handle associated with specified label values. + * It is recommended to keep a reference to the Handle instead of always + * calling this method for every operations. + * @param labelValues the list of label values. + */ + getHandle(labelValues: string[]): T { + return this._handle; + } + + /** + * Returns a Handle for a metric with all labels not set. + */ + getDefaultHandle(): T { + return this._handle; + } + + /** + * Removes the Handle from the metric, if it is present. + * @param labelValues the list of label values. + */ + removeHandle(labelValues: string[]): void { + return; + } + + /** + * Clears all timeseries from the Metric. + */ + clear(): void { + return; + } + + setCallback(fn: () => void): void { + return; + } +} + +export class NoopCounterHandle implements CounterHandle { + add(value: number): void { + return; + } +} + +export class NoopGaugeHandle implements GaugeHandle { + set(value: number): void { + return; + } +} + +export class NoopMeasureHandle implements MeasureHandle { + record( + value: number, + distContext?: DistributedContext, + spanContext?: SpanContext + ): void { + return; + } +} + +export const NOOP_GAUGE_HANDLE = new NoopGaugeHandle(); +export const NOOP_GAUGE_METRIC = new NoopMetric(NOOP_GAUGE_HANDLE); + +export const NOOP_COUNTER_HANDLE = new NoopCounterHandle(); +export const NOOP_COUNTER_METRIC = new NoopMetric( + NOOP_COUNTER_HANDLE +); + +export const NOOP_MEASURE_HANDLE = new NoopMeasureHandle(); +export const NOOP_MEASURE_METRIC = new NoopMetric( + NOOP_MEASURE_HANDLE +); diff --git a/packages/opentelemetry-core/test/metrics/NoopMeter.test.ts b/packages/opentelemetry-core/test/metrics/NoopMeter.test.ts new file mode 100644 index 00000000000..b37762bc7c5 --- /dev/null +++ b/packages/opentelemetry-core/test/metrics/NoopMeter.test.ts @@ -0,0 +1,90 @@ +/*! + * Copyright 2019, 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 assert from 'assert'; +import { + NoopMeter, + NOOP_GAUGE_HANDLE, + NOOP_GAUGE_METRIC, + NOOP_COUNTER_HANDLE, + NOOP_COUNTER_METRIC, + NOOP_MEASURE_HANDLE, + NOOP_MEASURE_METRIC, +} from '../../src/metrics/NoopMeter'; + +describe('NoopMeter', () => { + it('should not crash', () => { + const meter = new NoopMeter(); + const counter = meter.createCounter('some-name'); + // ensure NoopMetric does not crash. + counter.setCallback(() => { + assert.fail('callback occurred'); + }); + counter.getHandle(['val1', 'val2']).add(1); + counter.getDefaultHandle().add(1); + counter.removeHandle(['val1', 'val2']); + + // ensure the correct noop const is returned + assert.strictEqual(counter, NOOP_COUNTER_METRIC); + assert.strictEqual( + counter.getHandle(['val1', 'val2']), + NOOP_COUNTER_HANDLE + ); + assert.strictEqual(counter.getDefaultHandle(), NOOP_COUNTER_HANDLE); + counter.clear(); + + const measure = meter.createMeasure('some-name'); + measure.getDefaultHandle().record(1); + measure.getDefaultHandle().record(1, { key: { value: 'value' } }); + measure.getDefaultHandle().record( + 1, + { key: { value: 'value' } }, + { + traceId: 'a3cda95b652f4a1592b449d5929fda1b', + spanId: '5e0c63257de34c92', + } + ); + + // ensure the correct noop const is returned + assert.strictEqual(measure, NOOP_MEASURE_METRIC); + assert.strictEqual(measure.getDefaultHandle(), NOOP_MEASURE_HANDLE); + assert.strictEqual( + measure.getHandle(['val1', 'val2']), + NOOP_MEASURE_HANDLE + ); + + const gauge = meter.createGauge('some-name'); + gauge.getDefaultHandle().set(1); + + // ensure the correct noop const is returned + assert.strictEqual(gauge, NOOP_GAUGE_METRIC); + assert.strictEqual(gauge.getDefaultHandle(), NOOP_GAUGE_HANDLE); + assert.strictEqual(gauge.getHandle(['val1', 'val2']), NOOP_GAUGE_HANDLE); + + const options = { + component: 'tests', + description: 'the testing package', + labelKeys: ['key1', 'key2'], + }; + + const measureWithOptions = meter.createMeasure('some-name', options); + assert.strictEqual(measureWithOptions, NOOP_MEASURE_METRIC); + const counterWithOptions = meter.createCounter('some-name', options); + assert.strictEqual(counterWithOptions, NOOP_COUNTER_METRIC); + const gaugeWithOptions = meter.createGauge('some-name', options); + assert.strictEqual(gaugeWithOptions, NOOP_GAUGE_METRIC); + }); +});