Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add the UpDownCounter instrument #1120

Merged
merged 6 commits into from
Jun 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion packages/opentelemetry-api/src/metrics/Meter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@
* limitations under the License.
*/

import { MetricOptions, Counter, ValueRecorder, Observer } from './Metric';
import {
MetricOptions,
Counter,
ValueRecorder,
Observer,
UpDownCounter,
} from './Metric';

/**
* An interface to allow the recording metrics.
Expand All @@ -40,6 +46,25 @@ export interface Meter {
*/
createCounter(name: string, options?: MetricOptions): Counter;

/**
* Creates a new `UpDownCounter` metric. UpDownCounter is a synchronous
* instrument and very similar to Counter except that Add(increment)
* supports negative increments. It is generally useful for capturing changes
* in an amount of resources used, or any quantity that rises and falls
* during a request.
mayurkale22 marked this conversation as resolved.
Show resolved Hide resolved
* Example uses for UpDownCounter:
* <ol>
* <li> count the number of active requests. </li>
* <li> count memory in use by instrumenting new and delete. </li>
* <li> count queue size by instrumenting enqueue and dequeue. </li>
* <li> count semaphore up and down operations. </li>
* </ol>
*
* @param name the name of the metric.
* @param [options] the metric options.
*/
createUpDownCounter(name: string, options?: MetricOptions): UpDownCounter;

/**
* Creates a new `Observer` metric.
* @param name the name of the metric.
Expand Down
14 changes: 8 additions & 6 deletions packages/opentelemetry-api/src/metrics/Metric.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,7 @@ export interface MetricOptions {
disabled?: boolean;

/**
* Asserts that this metric may only increase (e.g. time spent).
*/
monotonic?: boolean;

/**
* (ValueRecorder only, default true) Asserts that this metric will only accept
* (Measure only, default true) Asserts that this metric will only accept
* non-negative values (e.g. disk usage).
*/
absolute?: boolean;
Expand Down Expand Up @@ -125,6 +120,13 @@ export interface Counter extends UnboundMetric<BoundCounter> {
add(value: number, labels?: Labels): void;
}

export interface UpDownCounter extends UnboundMetric<BoundCounter> {
/**
* Adds the given value to the current value. Values can be negative.
*/
add(value: number, labels?: Labels): void;
}

export interface ValueRecorder extends UnboundMetric<BoundValueRecorder> {
/**
* Records the given value to this value recorder.
Expand Down
10 changes: 10 additions & 0 deletions packages/opentelemetry-api/src/metrics/NoopMeter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
Counter,
ValueRecorder,
Observer,
UpDownCounter,
} from './Metric';
import { BoundValueRecorder, BoundCounter } from './BoundInstrument';
import { CorrelationContext } from '../correlation_context/CorrelationContext';
Expand Down Expand Up @@ -53,6 +54,15 @@ export class NoopMeter implements Meter {
return NOOP_COUNTER_METRIC;
}

/**
* Returns a constant noop UpDownCounter.
* @param name the name of the metric.
* @param [options] the metric options.
*/
createUpDownCounter(name: string, options?: MetricOptions): UpDownCounter {
return NOOP_COUNTER_METRIC;
}

/**
* Returns constant noop observer.
* @param name the name of the metric.
Expand Down
7 changes: 3 additions & 4 deletions packages/opentelemetry-exporter-prometheus/src/prometheus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,10 +191,9 @@ export class PrometheusExporter implements MetricExporter {

switch (record.descriptor.metricKind) {
case MetricKind.COUNTER:
// there is no such thing as a non-monotonic counter in prometheus
return record.descriptor.monotonic
? new Counter(metricObject)
: new Gauge(metricObject);
return new Counter(metricObject);
case MetricKind.UP_DOWN_COUNTER:
return new Gauge(metricObject);
case MetricKind.OBSERVER:
return new Gauge(metricObject);
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -384,10 +384,9 @@ describe('PrometheusExporter', () => {
});
});

it('should export a non-monotonic counter as a gauge', done => {
const counter = meter.createCounter('counter', {
it('should export a UpDownCounter as a gauge', done => {
const counter = meter.createUpDownCounter('counter', {
description: 'a test description',
monotonic: false,
});

counter.bind({ key1: 'labelValue1' }).add(20);
Expand Down
40 changes: 28 additions & 12 deletions packages/opentelemetry-metrics/src/BoundInstrument.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,16 @@ import { Aggregator } from './export/types';
export class BaseBoundInstrument {
protected _labels: api.Labels;
protected _logger: api.Logger;
protected _monotonic: boolean;

constructor(
labels: api.Labels,
logger: api.Logger,
monotonic: boolean,
private readonly _disabled: boolean,
private readonly _valueType: api.ValueType,
private readonly _aggregator: Aggregator
) {
this._labels = labels;
this._logger = logger;
this._monotonic = monotonic;
}

update(value: number): void {
Expand Down Expand Up @@ -72,18 +69,17 @@ export class BoundCounter extends BaseBoundInstrument
constructor(
labels: api.Labels,
disabled: boolean,
monotonic: boolean,
valueType: api.ValueType,
logger: api.Logger,
aggregator: Aggregator
) {
super(labels, logger, monotonic, disabled, valueType, aggregator);
super(labels, logger, disabled, valueType, aggregator);
}

add(value: number): void {
if (this._monotonic && value < 0) {
if (value < 0) {
this._logger.error(
`Monotonic counter cannot descend for ${Object.values(this._labels)}`
`Counter cannot descend for ${Object.values(this._labels)}`
);
return;
}
Expand All @@ -93,7 +89,29 @@ export class BoundCounter extends BaseBoundInstrument
}

/**
* BoundValueRecorder is an implementation of the {@link BoundValueRecorder} interface.
* BoundUpDownCounter allows the SDK to observe/record a single metric event.
* The value of single instrument in the `UpDownCounter` associated with
* specified Labels.
*/
export class BoundUpDownCounter extends BaseBoundInstrument
mayurkale22 marked this conversation as resolved.
Show resolved Hide resolved
implements api.BoundCounter {
constructor(
labels: api.Labels,
disabled: boolean,
valueType: api.ValueType,
logger: api.Logger,
aggregator: Aggregator
) {
super(labels, logger, disabled, valueType, aggregator);
}

add(value: number): void {
this.update(value);
}
}

/**
* BoundMeasure is an implementation of the {@link BoundMeasure} interface.
*/
export class BoundValueRecorder extends BaseBoundInstrument
implements api.BoundValueRecorder {
Expand All @@ -102,13 +120,12 @@ export class BoundValueRecorder extends BaseBoundInstrument
constructor(
labels: api.Labels,
disabled: boolean,
monotonic: boolean,
absolute: boolean,
valueType: api.ValueType,
logger: api.Logger,
aggregator: Aggregator
) {
super(labels, logger, monotonic, disabled, valueType, aggregator);
super(labels, logger, disabled, valueType, aggregator);
this._absolute = absolute;
}

Expand Down Expand Up @@ -137,11 +154,10 @@ export class BoundObserver extends BaseBoundInstrument {
constructor(
labels: api.Labels,
disabled: boolean,
monotonic: boolean,
valueType: api.ValueType,
logger: api.Logger,
aggregator: Aggregator
) {
super(labels, logger, monotonic, disabled, valueType, aggregator);
super(labels, logger, disabled, valueType, aggregator);
}
}
46 changes: 39 additions & 7 deletions packages/opentelemetry-metrics/src/Meter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import * as api from '@opentelemetry/api';
import { ConsoleLogger } from '@opentelemetry/core';
import { Resource } from '@opentelemetry/resources';
import { BaseBoundInstrument } from './BoundInstrument';
import { UpDownCounterMetric } from './UpDownCounterMetric';
import {
Metric,
CounterMetric,
Expand Down Expand Up @@ -72,10 +73,9 @@ export class Meter implements api.Meter {
return api.NOOP_VALUE_RECORDER_METRIC;
}
const opt: MetricOptions = {
absolute: true, // value recorders are defined as absolute by default
monotonic: false, // not applicable to value recorder, set to false
logger: this._logger,
...DEFAULT_METRIC_OPTIONS,
absolute: true, // value recorders are defined as absolute by default
...options,
};

Expand Down Expand Up @@ -104,8 +104,6 @@ export class Meter implements api.Meter {
return api.NOOP_COUNTER_METRIC;
}
const opt: MetricOptions = {
monotonic: true, // Counters are defined as monotonic by default
absolute: false, // not applicable to counter, set to false
logger: this._logger,
...DEFAULT_METRIC_OPTIONS,
...options,
Expand All @@ -115,6 +113,41 @@ export class Meter implements api.Meter {
return counter;
}

/**
* Creates a new `UpDownCounter` metric. UpDownCounter is a synchronous
* instrument and very similar to Counter except that Add(increment)
* supports negative increments. It is generally useful for capturing changes
* in an amount of resources used, or any quantity that rises and falls
* during a request.
*
* @param name the name of the metric.
* @param [options] the metric options.
*/
createUpDownCounter(
name: string,
options?: api.MetricOptions
): api.UpDownCounter {
if (!this._isValidName(name)) {
this._logger.warn(
`Invalid metric name ${name}. Defaulting to noop metric implementation.`
);
return api.NOOP_COUNTER_METRIC;
}
const opt: MetricOptions = {
logger: this._logger,
...DEFAULT_METRIC_OPTIONS,
...options,
};
const upDownCounter = new UpDownCounterMetric(
name,
opt,
this._batcher,
this._resource
);
this._registerMetric(name, upDownCounter);
return upDownCounter;
}

/**
* Creates a new observer metric.
* @param name the name of the metric.
Expand All @@ -128,8 +161,6 @@ export class Meter implements api.Meter {
return api.NOOP_OBSERVER_METRIC;
}
const opt: MetricOptions = {
monotonic: false, // Observers are defined as non-monotonic by default
absolute: false, // not applicable to observer, set to false
logger: this._logger,
...DEFAULT_METRIC_OPTIONS,
...options,
Expand Down Expand Up @@ -188,7 +219,8 @@ export class Meter implements api.Meter {
*
* 2. The first character must be non-numeric, non-space, non-punctuation
*
* 3. Subsequent characters must be belong to the alphanumeric characters, '_', '.', and '-'.
* 3. Subsequent characters must be belong to the alphanumeric characters,
* '_', '.', and '-'.
*
* Names are case insensitive
*
Expand Down
10 changes: 2 additions & 8 deletions packages/opentelemetry-metrics/src/Metric.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import { hashLabels } from './Utils';
/** This is a SDK implementation of {@link Metric} interface. */
export abstract class Metric<T extends BaseBoundInstrument>
implements api.UnboundMetric<T> {
protected readonly _monotonic: boolean;
protected readonly _disabled: boolean;
protected readonly _valueType: api.ValueType;
protected readonly _logger: api.Logger;
Expand All @@ -44,7 +43,6 @@ export abstract class Metric<T extends BaseBoundInstrument>
private readonly _kind: MetricKind,
readonly resource: Resource
) {
this._monotonic = _options.monotonic;
this._disabled = _options.disabled;
this._valueType = _options.valueType;
this._logger = _options.logger;
Expand Down Expand Up @@ -98,7 +96,6 @@ export abstract class Metric<T extends BaseBoundInstrument>
unit: this._options.unit,
metricKind: this._kind,
valueType: this._valueType,
monotonic: this._monotonic,
};
}

Expand All @@ -119,7 +116,6 @@ export class CounterMetric extends Metric<BoundCounter> implements api.Counter {
return new BoundCounter(
labels,
this._disabled,
this._monotonic,
this._valueType,
this._logger,
// @todo: consider to set to CounterSumAggregator always.
Expand All @@ -130,8 +126,8 @@ export class CounterMetric extends Metric<BoundCounter> implements api.Counter {
/**
* Adds the given value to the current value. Values cannot be negative.
* @param value the value to add.
* @param [labels = {}] key-values pairs that are associated with a specific metric
* that you want to record.
* @param [labels = {}] key-values pairs that are associated with a specific
* metric that you want to record.
*/
add(value: number, labels: api.Labels = {}) {
this.bind(labels).add(value);
Expand All @@ -156,7 +152,6 @@ export class ValueRecorderMetric extends Metric<BoundValueRecorder>
return new BoundValueRecorder(
labels,
this._disabled,
this._monotonic,
this._absolute,
this._valueType,
this._logger,
Expand Down Expand Up @@ -187,7 +182,6 @@ export class ObserverMetric extends Metric<BoundObserver>
return new BoundObserver(
labels,
this._disabled,
this._monotonic,
this._valueType,
this._logger,
this._batcher.aggregatorFor(this._descriptor)
Expand Down
Loading