Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
feat: support all metric types from otel spec
  • Loading branch information
pragmaticivan committed Dec 11, 2021
1 parent 646e2ea commit 36a7c5f
Show file tree
Hide file tree
Showing 8 changed files with 133 additions and 50 deletions.
5 changes: 4 additions & 1 deletion README.md
Expand Up @@ -244,6 +244,9 @@ You have the following decorators:
* `@OtelCounter()`
* `@OtelUpDownCounter()`
* `@OtelHistogram()`
* `@OtelObservableGauge()`
* `@OtelObservableCounter()`
* `@OtelObservableUpDownCounter()`

Example of usage:

Expand All @@ -258,7 +261,7 @@ export class AppController {
home(
@OtelCounter('app_counter_1_inc', { description: 'counter 1 description' }) counter1: Counter,
) {
counter1.inc(1);
counter1.add(1);
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/metrics/decorators/common.ts
@@ -1,5 +1,5 @@
import { Counter, MetricOptions } from '@opentelemetry/api-metrics';
import { getOrCreateCounter, MetricType } from '../metric-data';
import { getOrCreateCounter } from '../metric-data';

/**
* Create and increment a counter when a new instance is created
Expand All @@ -16,7 +16,7 @@ export const OtelInstanceCounter = (
return class extends ctor {
constructor(...args) {
if (!counterMetric) {
counterMetric = getOrCreateCounter(name, MetricType.Counter, { description, ...options });
counterMetric = getOrCreateCounter(name, { description, ...options });
}

counterMetric.add(1);
Expand Down Expand Up @@ -44,7 +44,7 @@ export const OtelMethodCounter = (
// eslint-disable-next-line no-param-reassign, func-names
descriptor.value = function (...args: any[]) {
if (!counterMetric) {
counterMetric = getOrCreateCounter(name, MetricType.Counter, { description, ...options });
counterMetric = getOrCreateCounter(name, { description, ...options });
}
counterMetric.add(1);
return methodFunc.apply(this, args);
Expand Down
4 changes: 2 additions & 2 deletions src/metrics/decorators/counter.ts
Expand Up @@ -6,12 +6,12 @@ export const OtelCounter = createParamDecorator((name: string, options?: MetricO
if (!name || name.length === 0) {
throw new Error('OtelCounter need a name argument');
}
return getOrCreateCounter(name, MetricType.Counter, options);
return getOrCreateCounter(name, options);
});

export const OtelUpDownCounter = createParamDecorator((name: string, options?: MetricOptions) => {
if (!name || name.length === 0) {
throw new Error('OtelUpDownCounter need a name argument');
}
return getOrCreateCounter(name, MetricType.UpDownCounter, options);
return getOrCreateCounter(name, options);
});
4 changes: 2 additions & 2 deletions src/metrics/decorators/histogram.ts
@@ -1,10 +1,10 @@
import { createParamDecorator } from '@nestjs/common';
import { MetricOptions } from '@opentelemetry/api-metrics';
import { getOrCreateHistogram, MetricType } from '../metric-data';
import { getOrCreateHistogram } from '../metric-data';

export const OtelHistogram = createParamDecorator((name: string, options?: MetricOptions) => {
if (!name || name.length === 0) {
throw new Error('OtelHistogram need a name argument');
}
return getOrCreateHistogram(name, MetricType.Histogram, options);
return getOrCreateHistogram(name, options);
});
1 change: 1 addition & 0 deletions src/metrics/decorators/index.ts
@@ -1,3 +1,4 @@
export * from './common';
export * from './counter';
export * from './histogram';
export * from './observable';
28 changes: 28 additions & 0 deletions src/metrics/decorators/observable.ts
@@ -0,0 +1,28 @@
import { createParamDecorator } from '@nestjs/common';
import { MetricOptions } from '@opentelemetry/api-metrics';
import { getOrCreateObservableCounter, getOrCreateObservableGauge, getOrCreateObservableUpDownCounter } from '../metric-data';

export const OtelObservableGauge = createParamDecorator((name: string, options?: MetricOptions) => {
if (!name || name.length === 0) {
throw new Error('OtelObservableGauge need a name argument');
}
return getOrCreateObservableGauge(name, options);
});

export const OtelObservableCounter = createParamDecorator(
(name: string, options?: MetricOptions) => {
if (!name || name.length === 0) {
throw new Error('OtelObservableCounter need a name argument');
}
return getOrCreateObservableCounter(name, options);
},
);

export const OtelObservableUpDownCounter = createParamDecorator(
(name: string, options?: MetricOptions) => {
if (!name || name.length === 0) {
throw new Error('OtelObservableUpDownCounter need a name argument');
}
return getOrCreateObservableUpDownCounter(name, options);
},
);
103 changes: 78 additions & 25 deletions src/metrics/metric-data.ts
@@ -1,60 +1,113 @@
import {
Counter, MetricOptions, metrics, UpDownCounter, Histogram,
Counter, MetricOptions, metrics, UpDownCounter,
Histogram, ObservableGauge, ObservableCounter, ObservableUpDownCounter,
} from '@opentelemetry/api-metrics';
import { OTEL_METER_NAME } from '../opentelemetry.constants';

export type GenericMetric = Counter | UpDownCounter | Histogram;
export type GenericMetric =
Counter |
UpDownCounter |
Histogram |
ObservableGauge |
ObservableCounter |
ObservableUpDownCounter;

export enum MetricType {
'Counter' = 'Counter',
'UpDownCounter' = 'UpDownCounter',
'Histogram' = 'Histogram',
'ObservableGauge' = 'ObservableGauge',
'ObservableCounter' = 'ObservableCounter',
'ObservableUpDownCounter' = 'ObservableUpDownCounter',
}

export const meterData: Map<string, GenericMetric> = new Map();

export function getOrCreateHistogram(
name: string,
type: MetricType,
options: MetricOptions,
): Histogram {
if (meterData.has(name)) {
return meterData.get(name) as Histogram;
}

const meter = metrics.getMeterProvider().getMeter(OTEL_METER_NAME);
const histogram = meter.createHistogram(name, options);
meterData.set(name, histogram);
return histogram;
}

switch (type) {
case MetricType.Histogram:
const histogram = meter.createHistogram(name, options);
meterData.set(name, histogram);
return histogram;
default:
throw new Error(`Unknown type: ${type}`);
export function getOrCreateCounter(
name: string,
options: MetricOptions,
): Counter {
if (meterData.has(name)) {
return meterData.get(name) as Counter;
}

const meter = metrics.getMeterProvider().getMeter(OTEL_METER_NAME);

const counter = meter.createCounter(name, options);
meterData.set(name, counter);
return counter;
}

export function getOrCreateCounter(
export function getOrCreateUpDownCounter(
name: string,
type: MetricType,
options: MetricOptions,
): Counter | UpDownCounter {
): UpDownCounter {
if (meterData.has(name)) {
return meterData.get(name) as Counter | UpDownCounter;
return meterData.get(name) as UpDownCounter;
}

const meter = metrics.getMeterProvider().getMeter(OTEL_METER_NAME);

switch (type) {
case MetricType.Counter:
const counter = meter.createCounter(name, options);
meterData.set(name, counter);
return counter;
case MetricType.UpDownCounter:
const upDownCounter = meter.createUpDownCounter(name, options);
meterData.set(name, upDownCounter);
return upDownCounter;
default:
throw new Error(`Unknown type: ${type}`);
const upDownCounter = meter.createUpDownCounter(name, options);
meterData.set(name, upDownCounter);
return upDownCounter;
}

export function getOrCreateObservableGauge(
name: string,
options: MetricOptions,
): ObservableGauge {
if (meterData.has(name)) {
return meterData.get(name) as ObservableGauge;
}

const meter = metrics.getMeterProvider().getMeter(OTEL_METER_NAME);

const observableGauge = meter.createObservableGauge(name, options);
meterData.set(name, observableGauge);
return observableGauge;
}

export function getOrCreateObservableCounter(
name: string,
options: MetricOptions,
): ObservableCounter {
if (meterData.has(name)) {
return meterData.get(name) as ObservableCounter;
}

const meter = metrics.getMeterProvider().getMeter(OTEL_METER_NAME);

const observableCounter = meter.createObservableCounter(name, options);
meterData.set(name, observableCounter);
return observableCounter;
}

export function getOrCreateObservableUpDownCounter(
name: string,
options: MetricOptions,
): ObservableUpDownCounter {
if (meterData.has(name)) {
return meterData.get(name) as ObservableUpDownCounter;
}

const meter = metrics.getMeterProvider().getMeter(OTEL_METER_NAME);

const observableCounter = meter.createObservableCounter(name, options);
meterData.set(name, observableCounter);
return observableCounter;
}
32 changes: 15 additions & 17 deletions src/metrics/metric.service.ts
@@ -1,38 +1,36 @@
import { Injectable } from '@nestjs/common';
import {
Counter, UpDownCounter, Histogram, MetricOptions,
MetricOptions,
} from '@opentelemetry/api-metrics';
import {
getOrCreateCounter, getOrCreateHistogram, MetricType,
getOrCreateCounter, getOrCreateHistogram,
getOrCreateObservableCounter, getOrCreateObservableGauge,
getOrCreateObservableUpDownCounter, getOrCreateUpDownCounter, MetricType,
} from './metric-data';

@Injectable()
export class MetricService {
getCounter(name: string, options?: MetricOptions) {
return this.getOrCreateCounter(name, MetricType.Counter, options);
return getOrCreateCounter(name, options);
}

getUpDownCounter(name: string, options?: MetricOptions) {
return this.getOrCreateCounter(name, MetricType.UpDownCounter, options);
return getOrCreateUpDownCounter(name, options);
}

getHistogram(name: string, options?: MetricOptions) {
return this.getOrCreateHistogram(name, MetricType.Histogram, options);
return getOrCreateHistogram(name, options);
}

private getOrCreateHistogram(
name: string,
type: MetricType,
options: MetricOptions,
): Histogram {
return getOrCreateHistogram(name, type, options);
getObservableCounter(name: string, options?: MetricOptions) {
return getOrCreateObservableCounter(name, options);
}

private getOrCreateCounter(
name: string,
type: MetricType,
options: MetricOptions,
): Counter | UpDownCounter {
return getOrCreateCounter(name, type, options);
getObservableGauge(name: string, options?: MetricOptions) {
return getOrCreateObservableGauge(name, options);
}

getObservableUpDownCounter(name: string, options?: MetricOptions) {
return getOrCreateObservableUpDownCounter(name, options);
}
}

0 comments on commit 36a7c5f

Please sign in to comment.