Skip to content

Commit

Permalink
feat: implement metrics sdk (#415)
Browse files Browse the repository at this point in the history
* feat: add metrics sdk

* refactor: based on comments and add Gauge support

* fix: add JSDoc comment

* fix: pass label values to handle and generate TimeSeries

* fix: add MeterConfig and logger option
  • Loading branch information
mayurkale22 authored Oct 16, 2019
1 parent 502f9e4 commit a025108
Show file tree
Hide file tree
Showing 9 changed files with 658 additions and 20 deletions.
109 changes: 109 additions & 0 deletions packages/opentelemetry-metrics/src/Handle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*!
* 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 types from '@opentelemetry/types';
import { TimeSeries } from './export/types';

/**
* CounterHandle allows the SDK to observe/record a single metric event. The
* value of single handle in the `Counter` associated with specified label
* values.
*/
export class CounterHandle implements types.CounterHandle {
private _data = 0;

constructor(
private readonly _disabled: boolean,
private readonly _monotonic: boolean,
private readonly _labelValues: string[],
private readonly _logger: types.Logger
) {}

add(value: number): void {
if (this._disabled) return;

if (this._monotonic && value < 0) {
this._logger.error('Monotonic counter cannot descend.');
return;
}
this._data = this._data + value;
}

/**
* Returns the TimeSeries with one or more Point.
*
* @param timestamp The time at which the counter is recorded.
* @returns The TimeSeries.
*/
getTimeSeries(timestamp: types.HrTime): TimeSeries {
return {
labelValues: this._labelValues.map(value => ({ value })),
points: [{ value: this._data, timestamp }],
};
}
}

/**
* GaugeHandle allows the SDK to observe/record a single metric event. The
* value of single handle in the `Gauge` associated with specified label values.
*/
export class GaugeHandle implements types.GaugeHandle {
private _data = 0;

constructor(
private readonly _disabled: boolean,
private readonly _monotonic: boolean,
private readonly _labelValues: string[],
private readonly _logger: types.Logger
) {}

set(value: number): void {
if (this._disabled) return;

if (this._monotonic && value < this._data) {
this._logger.error('Monotonic gauge cannot descend.');
return;
}
this._data = value;
}

/**
* Returns the TimeSeries with one or more Point.
*
* @param timestamp The time at which the gauge is recorded.
* @returns The TimeSeries.
*/
getTimeSeries(timestamp: types.HrTime): TimeSeries {
return {
labelValues: this._labelValues.map(value => ({ value })),
points: [{ value: this._data, timestamp }],
};
}
}

/**
* MeasureHandle is an implementation of the {@link MeasureHandle} interface.
*/
export class MeasureHandle implements types.MeasureHandle {
record(
value: number,
distContext?: types.DistributedContext,
spanContext?: types.SpanContext
): void {
// @todo: implement this method.
return;
}
}
96 changes: 96 additions & 0 deletions packages/opentelemetry-metrics/src/Meter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*!
* 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 types from '@opentelemetry/types';
import { ConsoleLogger } from '@opentelemetry/core';
import { CounterHandle, GaugeHandle, MeasureHandle } from './Handle';
import { Metric, CounterMetric, GaugeMetric } from './Metric';
import {
MetricOptions,
DEFAULT_METRIC_OPTIONS,
DEFAULT_CONFIG,
MeterConfig,
} from './types';

/**
* Meter is an implementation of the {@link Meter} interface.
*/
export class Meter implements types.Meter {
private readonly _logger: types.Logger;

/**
* Constructs a new Meter instance.
*/
constructor(config: MeterConfig = DEFAULT_CONFIG) {
this._logger = config.logger || new ConsoleLogger(config.logLevel);
}

/**
* Creates and returns a new {@link Measure}.
* @param name the name of the metric.
* @param [options] the metric options.
*/
createMeasure(
name: string,
options?: types.MetricOptions
): Metric<MeasureHandle> {
// @todo: implement this method
throw new Error('not implemented yet');
}

/**
* Creates a new counter metric. Generally, this kind of metric when the
* value is a quantity, the sum is of primary interest, and the event count
* and value distribution are not of primary interest.
* @param name the name of the metric.
* @param [options] the metric options.
*/
createCounter(
name: string,
options?: types.MetricOptions
): Metric<CounterHandle> {
const opt: MetricOptions = {
// Counters are defined as monotonic by default
monotonic: true,
logger: this._logger,
...DEFAULT_METRIC_OPTIONS,
...options,
};
return new CounterMetric(name, opt);
}

/**
* Creates a new gauge metric. Generally, this kind of metric should be used
* when the metric cannot be expressed as a sum or because the measurement
* interval is arbitrary. Use this kind of metric when the measurement is not
* a quantity, and the sum and event count are not of interest.
* @param name the name of the metric.
* @param [options] the metric options.
*/
createGauge(
name: string,
options?: types.MetricOptions
): Metric<GaugeHandle> {
const opt: MetricOptions = {
// Gauges are defined as non-monotonic by default
monotonic: false,
logger: this._logger,
...DEFAULT_METRIC_OPTIONS,
...options,
};
return new GaugeMetric(name, opt);
}
}
111 changes: 111 additions & 0 deletions packages/opentelemetry-metrics/src/Metric.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*!
* 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 types from '@opentelemetry/types';
import { hashLabelValues } from './Utils';
import { CounterHandle, GaugeHandle } from './Handle';
import { MetricOptions } from './types';

/** This is a SDK implementation of {@link Metric} interface. */
export abstract class Metric<T> implements types.Metric<T> {
protected readonly _monotonic: boolean;
protected readonly _disabled: boolean;
protected readonly _logger: types.Logger;
private readonly _handles: Map<String, T> = new Map();

constructor(name: string, options: MetricOptions) {
this._monotonic = options.monotonic;
this._disabled = options.disabled;
this._logger = options.logger;
}

/**
* 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 each operation.
* @param labelValues the list of label values.
*/
getHandle(labelValues: string[]): T {
const hash = hashLabelValues(labelValues);
if (this._handles.has(hash)) return this._handles.get(hash)!;

const handle = this._makeHandle(labelValues);
this._handles.set(hash, handle);
return handle;
}

/**
* Returns a Handle for a metric with all labels not set.
*/
getDefaultHandle(): T {
// @todo: implement this method
this._logger.error('not implemented yet');
throw new Error('not implemented yet');
}

/**
* Removes the Handle from the metric, if it is present.
* @param labelValues the list of label values.
*/
removeHandle(labelValues: string[]): void {
this._handles.delete(hashLabelValues(labelValues));
}

/**
* Clears all Handles from the Metric.
*/
clear(): void {
this._handles.clear();
}

setCallback(fn: () => void): void {
// @todo: implement this method
this._logger.error('not implemented yet');
return;
}

protected abstract _makeHandle(labelValues: string[]): T;
}

/** This is a SDK implementation of Counter Metric. */
export class CounterMetric extends Metric<CounterHandle> {
constructor(name: string, options: MetricOptions) {
super(name, options);
}
protected _makeHandle(labelValues: string[]): CounterHandle {
return new CounterHandle(
this._disabled,
this._monotonic,
labelValues,
this._logger
);
}
}

/** This is a SDK implementation of Gauge Metric. */
export class GaugeMetric extends Metric<GaugeHandle> {
constructor(name: string, options: MetricOptions) {
super(name, options);
}
protected _makeHandle(labelValues: string[]): GaugeHandle {
return new GaugeHandle(
this._disabled,
this._monotonic,
labelValues,
this._logger
);
}
}
27 changes: 27 additions & 0 deletions packages/opentelemetry-metrics/src/Utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*!
* 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.
*/

const COMMA_SEPARATOR = ',';

/**
* Returns a string(comma separated) from the list of label values.
*
* @param labelValues The list of the label values.
* @returns The hashed label values string.
*/
export function hashLabelValues(labelValues: string[]): string {
return labelValues.sort().join(COMMA_SEPARATOR);
}
4 changes: 4 additions & 0 deletions packages/opentelemetry-metrics/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export * from './Handle';
export * from './Meter';
export * from './Metric';
Loading

0 comments on commit a025108

Please sign in to comment.