Skip to content

Commit

Permalink
docs(batcher): document how to configure custom aggregators #989
Browse files Browse the repository at this point in the history
  • Loading branch information
vmarchaud committed May 23, 2020
1 parent 7233a03 commit 75f5fbb
Showing 1 changed file with 143 additions and 0 deletions.
143 changes: 143 additions & 0 deletions doc/batcher-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# Batcher API Guide

The batcher has two responsabilities: choosing which aggregator to choose for a metric instrument and store the last record for each metric ready to be exported.

## Selecting a specific aggregator for metrics

Sometimes you may want to use a specific aggregator for one of your metric, export a average of the last X values instead of just the last one.

Here is what a aggregator that does that would looks like:
```ts
import { Aggregator } from '@opentelemetry/metrics';
import { hrTime } from '@opentelemetry/core';

export class AverageAggregator implements Aggregator {

private _values: number[] = [];
private _limit: number;

constructor (limit?: number) {
this._limit = limit ?? 10;
}

update (value: number) {
this._values.push(value);
if (this._values.length >= this._limit) {
this._values = this._values.slice(0, 10);
}
}

toPoint() {
const sum =this._values.reduce((agg, value) => {
agg += value;
return agg;
}, 0);
return {
value: this._values.length > 0 ? sum / this._values.length : 0,
timestamp: hrTime(),
}
}
}
```

Now we will need to implement our own batcher to configure the sdk to use our new aggregator. To simplify even more, we will just extend the `UngroupedBatcher` (which is the default) to avoid re-implementing the whole `Aggregator` interface.

Here the result:
```ts
import {
UngroupedBatcher,
MetricDescriptor,
CounterSumAggregator,
ObserverAggregator,
MeasureExactAggregator,
} from '@opentelemetry/metrics';

export class CustomBatcher extends UngroupedBatcher {
aggregatorFor (metricDescriptor: MetricDescriptor) {
if (metricDescriptor.labels === 'metricToBeAveraged') {
return new AverageAggregator(10);
}
// this is exactly what the "UngroupedBatcher" does, we will re-use it
// to fallback on the default behavior
switch (metricDescriptor.metricKind) {
case MetricKind.COUNTER:
return new CounterSumAggregator();
case MetricKind.OBSERVER:
return new ObserverAggregator();
default:
return new MeasureExactAggregator();
}
}
}
```

Finally, we need to specify to the `MeterProvider` to use our `CustomBatcher` when creating new meter:

```ts
import {
UngroupedBatcher,
MetricDescriptor,
CounterSumAggregator,
ObserverAggregator,
MeasureExactAggregator,
MeterProvider,
Aggregator,
} from '@opentelemetry/metrics';
import { hrTime } from '@opentelemetry/core';

export class AverageAggregator implements Aggregator {

private _values: number[] = [];
private _limit: number;

constructor (limit?: number) {
this._limit = limit ?? 10;
}

update (value: number) {
this._values.push(value);
if (this._values.length >= this._limit) {
this._values = this._values.slice(0, 10);
}
}

toPoint() {
const sum =this._values.reduce((agg, value) => {
agg += value;
return agg;
}, 0);
return {
value: this._values.length > 0 ? sum / this._values.length : 0,
timestamp: hrTime(),
}
}
}

export class CustomBatcher extends UngroupedBatcher {
aggregatorFor (metricDescriptor: MetricDescriptor) {
if (metricDescriptor.name === 'requests') {
return new AverageAggregator(10);
}
// this is exactly what the "UngroupedBatcher" does, we will re-use it
// to fallback on the default behavior
switch (metricDescriptor.metricKind) {
case MetricKind.COUNTER:
return new CounterSumAggregator();
case MetricKind.OBSERVER:
return new ObserverAggregator();
default:
return new MeasureExactAggregator();
}
}
}

const meter = new MeterProvider({
batcher: new CustomBatcher(),
interval: 1000,
}).getMeter('example-custom-batcher');

const requestsLatency = meter.createMeasure('requests', {
monotonic: true,
description: 'Average latency'
});
```

0 comments on commit 75f5fbb

Please sign in to comment.