Skip to content

Commit

Permalink
feat: add addExporter to Meter (#482)
Browse files Browse the repository at this point in the history
* feat: add addExporter to Meter

* chore: remove event emitter from handle

* style: remove nested if
  • Loading branch information
dyladan authored and mayurkale22 committed Nov 6, 2019
1 parent e9f9570 commit 6be9e1e
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 12 deletions.
8 changes: 6 additions & 2 deletions packages/opentelemetry-metrics/src/Handle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ export class CounterHandle extends BaseHandle implements types.CounterHandle {
private readonly _monotonic: boolean,
private readonly _valueType: types.ValueType,
private readonly _labelValues: string[],
private readonly _logger: types.Logger
private readonly _logger: types.Logger,
private readonly _onUpdate: Function
) {
super(_labelValues);
}
Expand All @@ -72,6 +73,7 @@ export class CounterHandle extends BaseHandle implements types.CounterHandle {
value = Math.trunc(value);
}
this._data = this._data + value;
this._onUpdate();
}
}

Expand All @@ -85,7 +87,8 @@ export class GaugeHandle extends BaseHandle implements types.GaugeHandle {
private readonly _monotonic: boolean,
private readonly _valueType: types.ValueType,
private readonly _labelValues: string[],
private readonly _logger: types.Logger
private readonly _logger: types.Logger,
private readonly _onUpdate: Function
) {
super(_labelValues);
}
Expand All @@ -107,6 +110,7 @@ export class GaugeHandle extends BaseHandle implements types.GaugeHandle {
value = Math.trunc(value);
}
this._data = value;
this._onUpdate();
}
}

Expand Down
47 changes: 41 additions & 6 deletions packages/opentelemetry-metrics/src/Meter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,17 @@ import {
DEFAULT_CONFIG,
MeterConfig,
} from './types';
import { ReadableMetric } from './export/types';
import { ReadableMetric, MetricExporter } from './export/types';
import { notNull } from './Utils';
import { ExportResult } from '@opentelemetry/base';

/**
* Meter is an implementation of the {@link Meter} interface.
*/
export class Meter implements types.Meter {
private readonly _logger: types.Logger;
private readonly _metrics = new Map();
private readonly _metrics = new Map<string, Metric<BaseHandle>>();
private readonly _exporters: MetricExporter[] = [];

/**
* Constructs a new Meter instance.
Expand Down Expand Up @@ -88,7 +91,9 @@ export class Meter implements types.Meter {
...DEFAULT_METRIC_OPTIONS,
...options,
};
const counter = new CounterMetric(name, opt);
const counter = new CounterMetric(name, opt, () => {
this._exportOneMetric(name);
});
this._registerMetric(name, counter);
return counter;
}
Expand Down Expand Up @@ -118,19 +123,49 @@ export class Meter implements types.Meter {
...DEFAULT_METRIC_OPTIONS,
...options,
};
const gauge = new GaugeMetric(name, opt);
const gauge = new GaugeMetric(name, opt, () => {
this._exportOneMetric(name);
});
this._registerMetric(name, gauge);
return gauge;
}

/**
* Gets a collection of Metric`s to be exported.
* Gets a collection of Metrics to be exported.
* @returns The list of metrics.
*/
getMetrics(): ReadableMetric[] {
return Array.from(this._metrics.values())
.map(metric => metric.get())
.filter(metric => !!metric);
.filter(notNull);
}

/**
* Add an exporter to the list of registered exporters
*
* @param exporter {@Link MetricExporter} to add to the list of registered exporters
*/
addExporter(exporter: MetricExporter) {
this._exporters.push(exporter);
}

/**
* Send a single metric by name to all registered exporters
*/
private _exportOneMetric(name: string) {
const metric = this._metrics.get(name);
if (!metric) return;

const readableMetric = metric.get();
if (!readableMetric) return;

for (const exporter of this._exporters) {
exporter.export([readableMetric], result => {
if (result !== ExportResult.SUCCESS) {
this._logger.error(`Failed to export ${name}`);
}
});
}
}

/**
Expand Down
19 changes: 15 additions & 4 deletions packages/opentelemetry-metrics/src/Metric.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export abstract class Metric<T extends BaseHandle> implements types.Metric<T> {
if (this._handles.has(hash)) return this._handles.get(hash)!;

const handle = this._makeHandle(labelValues);

this._handles.set(hash, handle);
return handle;
}
Expand Down Expand Up @@ -123,7 +124,11 @@ export abstract class Metric<T extends BaseHandle> implements types.Metric<T> {

/** This is a SDK implementation of Counter Metric. */
export class CounterMetric extends Metric<CounterHandle> {
constructor(name: string, options: MetricOptions) {
constructor(
name: string,
options: MetricOptions,
private readonly _onUpdate: Function
) {
super(
name,
options,
Expand All @@ -138,14 +143,19 @@ export class CounterMetric extends Metric<CounterHandle> {
this._monotonic,
this._valueType,
labelValues,
this._logger
this._logger,
this._onUpdate
);
}
}

/** This is a SDK implementation of Gauge Metric. */
export class GaugeMetric extends Metric<GaugeHandle> {
constructor(name: string, options: MetricOptions) {
constructor(
name: string,
options: MetricOptions,
private readonly _onUpdate: Function
) {
super(
name,
options,
Expand All @@ -160,7 +170,8 @@ export class GaugeMetric extends Metric<GaugeHandle> {
this._monotonic,
this._valueType,
labelValues,
this._logger
this._logger,
this._onUpdate
);
}
}
9 changes: 9 additions & 0 deletions packages/opentelemetry-metrics/src/Utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,12 @@ const COMMA_SEPARATOR = ',';
export function hashLabelValues(labelValues: string[]): string {
return labelValues.sort().join(COMMA_SEPARATOR);
}

/**
* Type guard to remove nulls from arrays
*
* @param value value to be checked for null equality
*/
export function notNull<T>(value: T | null): value is T {
return value !== null;
}
53 changes: 53 additions & 0 deletions packages/opentelemetry-metrics/test/Meter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
hrTime,
hrTimeToMilliseconds,
} from '@opentelemetry/core';
import { NoopExporter } from './mocks/Exporter';

const performanceTimeOrigin = hrTime();

Expand Down Expand Up @@ -481,4 +482,56 @@ describe('Meter', () => {
);
});
});

describe('Exporters', () => {
it('should register an exporter', () => {
const exporter = new NoopExporter();
meter.addExporter(exporter);
assert.equal(meter['_exporters'].length, 1);
});

it('should export a gauge when it is updated', done => {
const exporter = new NoopExporter();
exporter.on('export', metrics => {
assert.equal(metrics[0].descriptor.name, 'name');
assert.equal(metrics[0].timeseries[0].points[0].value, 20);
assert.deepEqual(metrics[0].timeseries[0].labelValues, [
{
value: 'value1',
},
{
value: 'value2',
},
]);
done();
});

meter.addExporter(exporter);
const gauge = meter.createGauge('name') as GaugeMetric;
const handle = gauge.getHandle(['value1', 'value2']);
handle.set(20);
});

it('should export a counter when it is updated', done => {
const counter = meter.createCounter('name') as CounterMetric;
const exporter = new NoopExporter();
exporter.on('export', metrics => {
assert.equal(metrics[0].descriptor.name, 'name');
assert.equal(metrics[0].timeseries[0].points[0].value, 20);
assert.deepEqual(metrics[0].timeseries[0].labelValues, [
{
value: 'value1',
},
{
value: 'value2',
},
]);
done();
});

meter.addExporter(exporter);
const handle = counter.getHandle(['value1', 'value2']);
handle.add(20);
});
});
});
32 changes: 32 additions & 0 deletions packages/opentelemetry-metrics/test/mocks/Exporter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*!
* 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 { MetricExporter, ReadableMetric } from '../../src/export/types';
import { ExportResult } from '@opentelemetry/base';
import { EventEmitter } from 'events';

export class NoopExporter extends EventEmitter implements MetricExporter {
export(
metrics: ReadableMetric[],
resultCallback: (result: ExportResult) => void
): void {
this.emit('export', metrics, resultCallback);
}

shutdown(): void {
this.emit('shutdown');
}
}

0 comments on commit 6be9e1e

Please sign in to comment.