nestjs-otel 2.5.2 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +28 -9
  3. package/examples/nestjs-prom-grafana-tempo/package.json +40 -37
  4. package/examples/nestjs-prom-grafana-tempo/src/tracing.ts +5 -5
  5. package/lib/interfaces/opentelemetry-options.interface.d.ts +5 -2
  6. package/lib/metrics/decorators/common.js +2 -2
  7. package/lib/metrics/decorators/common.js.map +1 -1
  8. package/lib/metrics/decorators/counter.js +2 -2
  9. package/lib/metrics/decorators/counter.js.map +1 -1
  10. package/lib/metrics/decorators/histogram.d.ts +1 -0
  11. package/lib/metrics/decorators/histogram.js +12 -0
  12. package/lib/metrics/decorators/histogram.js.map +1 -0
  13. package/lib/metrics/decorators/index.d.ts +2 -1
  14. package/lib/metrics/decorators/index.js +2 -1
  15. package/lib/metrics/decorators/index.js.map +1 -1
  16. package/lib/metrics/decorators/observable.d.ts +3 -0
  17. package/lib/metrics/decorators/observable.js +24 -0
  18. package/lib/metrics/decorators/observable.js.map +1 -0
  19. package/lib/metrics/metric-data.d.ts +12 -5
  20. package/lib/metrics/metric-data.js +53 -24
  21. package/lib/metrics/metric-data.js.map +1 -1
  22. package/lib/metrics/metric.service.d.ts +7 -6
  23. package/lib/metrics/metric.service.js +11 -8
  24. package/lib/metrics/metric.service.js.map +1 -1
  25. package/lib/middleware/api-metrics.middleware.d.ts +6 -5
  26. package/lib/middleware/api-metrics.middleware.js +21 -24
  27. package/lib/middleware/api-metrics.middleware.js.map +1 -1
  28. package/lib/opentelemetry-core.module.js +6 -1
  29. package/lib/opentelemetry-core.module.js.map +1 -1
  30. package/lib/tracing/trace.service.d.ts +1 -0
  31. package/lib/tracing/trace.service.js +4 -2
  32. package/lib/tracing/trace.service.js.map +1 -1
  33. package/lib/tsconfig.tsbuildinfo +1 -1
  34. package/package.json +32 -32
  35. package/src/interfaces/opentelemetry-options.interface.ts +6 -3
  36. package/src/metrics/decorators/common.ts +7 -5
  37. package/src/metrics/decorators/counter.ts +5 -9
  38. package/src/metrics/decorators/histogram.ts +10 -0
  39. package/src/metrics/decorators/observable.ts +28 -0
  40. package/src/metrics/metric-data.ts +89 -29
  41. package/src/metrics/metric.service.ts +28 -14
  42. package/src/middleware/api-metrics.middleware.ts +25 -29
  43. package/src/opentelemetry-core.module.ts +9 -4
  44. package/src/tracing/trace.service.ts +5 -2
  45. package/lib/metrics/decorators/value-recorder.d.ts +0 -1
  46. package/lib/metrics/decorators/value-recorder.js +0 -12
  47. package/lib/metrics/decorators/value-recorder.js.map +0 -1
  48. package/src/metrics/decorators/value-recorder.ts +0 -12
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nestjs-otel",
3
- "version": "2.5.2",
3
+ "version": "3.0.1",
4
4
  "description": "NestJS OpenTelemetry Library",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index",
@@ -14,7 +14,7 @@
14
14
  "test:coverage": "jest --coverage",
15
15
  "test:unit": "jest --passWithNoTests",
16
16
  "test:watch": "jest --watch",
17
- "test:e2e": "jest --config ./tests/jest-e2e.json --runInBand --detectOpenHandles",
17
+ "test:e2e": "jest --config ./tests/jest-e2e.json --runInBand --forceExit",
18
18
  "test:e2e:watch": "jest --config ./tests/jest-e2e.json --runInBand --watch"
19
19
  },
20
20
  "repository": {
@@ -30,47 +30,47 @@
30
30
  "prometheus"
31
31
  ],
32
32
  "author": "pragmaticivan@gmail.com",
33
- "license": "Apache 2",
34
- "test": "jest --ci --runInBand",
33
+ "license": "Apache-2.0",
35
34
  "bugs": {
36
35
  "url": "https://github.com/pragmaticivan/nestjs-otel/issues"
37
36
  },
38
37
  "homepage": "https://github.com/pragmaticivan/nestjs-otel#readme",
39
38
  "dependencies": {
40
- "@opentelemetry/host-metrics": "^0.25.0",
41
- "@opentelemetry/api": "^1.0.3",
42
- "@opentelemetry/api-metrics": "^0.25.0",
43
- "@opentelemetry/metrics": "^0.24.0",
44
- "opentelemetry-node-metrics": "^1.0.3",
39
+ "@opentelemetry/host-metrics": "^0.27.0",
40
+ "@opentelemetry/api": "^1.0.4",
41
+ "@opentelemetry/api-metrics": "^0.27.0",
42
+ "opentelemetry-node-metrics": "^1.1.0",
45
43
  "response-time": "^2.3.2"
46
44
  },
47
45
  "devDependencies": {
48
- "@commitlint/cli": "^13.1.0",
49
- "@commitlint/config-conventional": "^13.1.0",
50
- "@nestjs/common": "^8.0.10",
51
- "@nestjs/core": "^8.0.10",
52
- "@nestjs/platform-express": "^8.0.10",
53
- "@nestjs/platform-fastify": "^8.0.10",
54
- "@nestjs/testing": "^8.0.10",
55
- "@opentelemetry/exporter-prometheus": "^0.25.0",
56
- "@opentelemetry/sdk-node": "^0.25.0",
57
- "@types/jest": "^27.0.2",
58
- "@types/node": "^16.9.6",
46
+ "@commitlint/cli": "^16.0.1",
47
+ "@commitlint/config-conventional": "^16.0.0",
48
+ "@nestjs/common": "^8.2.3",
49
+ "@nestjs/core": "^8.2.3",
50
+ "@nestjs/platform-express": "^8.2.4",
51
+ "@nestjs/platform-fastify": "^8.2.4",
52
+ "@nestjs/testing": "^8.2.4",
53
+ "@opentelemetry/exporter-prometheus": "^0.27.0",
54
+ "@opentelemetry/sdk-node": "^0.27.0",
55
+ "@types/jest": "^27.0.3",
56
+ "@types/node": "^17.0.5",
59
57
  "@types/supertest": "^2.0.11",
60
- "@typescript-eslint/eslint-plugin": "4.31.2",
61
- "@typescript-eslint/parser": "4.31.2",
62
- "eslint": "7.32.0",
63
- "eslint-config-airbnb-typescript": "14.0.0",
64
- "eslint-plugin-import": "2.24.2",
65
- "husky": "^7.0.2",
66
- "jest": "^27.2.1",
67
- "lint-staged": "^11.1.2",
58
+ "@typescript-eslint/eslint-plugin": "5.8.1",
59
+ "@typescript-eslint/parser": "5.8.1",
60
+ "eslint": "^8.5.0",
61
+ "eslint-config-airbnb-base": "^15.0.0",
62
+ "eslint-config-airbnb-typescript": "^16.1.0",
63
+ "eslint-plugin-import": "^2.25.3",
64
+ "husky": "^7.0.4",
65
+ "jest": "^27.4.5",
66
+ "lint-staged": "^12.1.4",
68
67
  "reflect-metadata": "^0.1.13",
69
68
  "rimraf": "^3.0.2",
70
- "rxjs": "^7.3.0",
69
+ "rxjs": "^7.5.1",
71
70
  "supertest": "^6.1.6",
72
- "ts-jest": "^27.0.5",
73
- "typescript": "4.4.3"
71
+ "ts-jest": "^27.1.2",
72
+ "typescript": "4.5.4",
73
+ "ansi-regex": ""
74
74
  },
75
75
  "peerDependencies": {
76
76
  "@nestjs/common": "^7.0.0 || ^8.0.0",
@@ -95,7 +95,7 @@
95
95
  "testEnvironment": "node"
96
96
  },
97
97
  "lint-staged": {
98
- "*.{js,ts}": "eslint --cache --fix"
98
+ "!(examples/**/*)*.{js,ts}": "eslint --cache --fix"
99
99
  },
100
100
  "commitlint": {
101
101
  "extends": [
@@ -1,11 +1,12 @@
1
1
  import { ModuleMetadata, Type, Abstract } from '@nestjs/common';
2
- import { Labels } from '@opentelemetry/api-metrics';
2
+ import { Attributes } from '@opentelemetry/api-metrics';
3
+ import { RouteInfo } from '@nestjs/common/interfaces';
3
4
 
4
5
  export type OpenTelemetryModuleOptions = {
5
6
  /**
6
7
  * OpenTelemetry Metrics Setup
7
8
  */
8
- metrics?: OpenTelemetryMetrics
9
+ metrics?: OpenTelemetryMetrics;
9
10
  };
10
11
 
11
12
  export interface OpenTelemetryOptionsFactory {
@@ -51,6 +52,8 @@ export type OpenTelemetryMetrics = {
51
52
  timeBuckets?: number[],
52
53
  requestSizeBuckets?: number[],
53
54
  responseSizeBuckets?: number[],
54
- defaultLabels?: Labels,
55
+ defaultAttributes?: Attributes,
56
+ ignoreRoutes?: (string | RouteInfo)[],
57
+ ignoreUndefinedRoutes?: boolean,
55
58
  },
56
59
  };
@@ -1,5 +1,5 @@
1
1
  import { Counter, MetricOptions } from '@opentelemetry/api-metrics';
2
- import { getOrCreateCounter, getOrCreateValueRecorder, MetricType } from '../metric-data';
2
+ import { getOrCreateCounter } from '../metric-data';
3
3
 
4
4
  /**
5
5
  * Create and increment a counter when a new instance is created
@@ -16,7 +16,7 @@ export const OtelInstanceCounter = (
16
16
  return class extends ctor {
17
17
  constructor(...args) {
18
18
  if (!counterMetric) {
19
- counterMetric = getOrCreateCounter(name, MetricType.Counter, { description, ...options });
19
+ counterMetric = getOrCreateCounter(name, { description, ...options });
20
20
  }
21
21
 
22
22
  counterMetric.add(1);
@@ -31,7 +31,9 @@ export const OtelInstanceCounter = (
31
31
  export const OtelMethodCounter = (
32
32
  options?: MetricOptions,
33
33
  ) => (
34
- target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<(...args: any[]
34
+ target: Object,
35
+ propertyKey: string | symbol,
36
+ descriptor: TypedPropertyDescriptor<(...args: any[]
35
37
  ) => any>,
36
38
  ) => {
37
39
  const className = target.constructor.name;
@@ -39,10 +41,10 @@ export const OtelMethodCounter = (
39
41
  const description = `app_${className}#${propertyKey.toString()} called total`;
40
42
  let counterMetric: Counter;
41
43
  const methodFunc = descriptor.value;
42
- // eslint-disable-next-line no-param-reassign
44
+ // eslint-disable-next-line no-param-reassign, func-names
43
45
  descriptor.value = function (...args: any[]) {
44
46
  if (!counterMetric) {
45
- counterMetric = getOrCreateCounter(name, MetricType.Counter, { description, ...options });
47
+ counterMetric = getOrCreateCounter(name, { description, ...options });
46
48
  }
47
49
  counterMetric.add(1);
48
50
  return methodFunc.apply(this, args);
@@ -1,21 +1,17 @@
1
1
  import { createParamDecorator } from '@nestjs/common';
2
2
  import { MetricOptions } from '@opentelemetry/api-metrics';
3
- import { getOrCreateCounter, getOrCreateValueRecorder, MetricType } from '../metric-data';
3
+ import { getOrCreateCounter, MetricType } from '../metric-data';
4
4
 
5
- export const OtelCounter = createParamDecorator((
6
- name: string, options?: MetricOptions,
7
- ) => {
5
+ export const OtelCounter = createParamDecorator((name: string, options?: MetricOptions) => {
8
6
  if (!name || name.length === 0) {
9
7
  throw new Error('OtelCounter need a name argument');
10
8
  }
11
- return getOrCreateCounter(name, MetricType.Counter, options);
9
+ return getOrCreateCounter(name, options);
12
10
  });
13
11
 
14
- export const OtelUpDownCounter = createParamDecorator((
15
- name: string, options?: MetricOptions,
16
- ) => {
12
+ export const OtelUpDownCounter = createParamDecorator((name: string, options?: MetricOptions) => {
17
13
  if (!name || name.length === 0) {
18
14
  throw new Error('OtelUpDownCounter need a name argument');
19
15
  }
20
- return getOrCreateCounter(name, MetricType.UpDownCounter, options);
16
+ return getOrCreateCounter(name, options);
21
17
  });
@@ -0,0 +1,10 @@
1
+ import { createParamDecorator } from '@nestjs/common';
2
+ import { MetricOptions } from '@opentelemetry/api-metrics';
3
+ import { getOrCreateHistogram } from '../metric-data';
4
+
5
+ export const OtelHistogram = createParamDecorator((name: string, options?: MetricOptions) => {
6
+ if (!name || name.length === 0) {
7
+ throw new Error('OtelHistogram need a name argument');
8
+ }
9
+ return getOrCreateHistogram(name, options);
10
+ });
@@ -0,0 +1,28 @@
1
+ import { createParamDecorator } from '@nestjs/common';
2
+ import { MetricOptions } from '@opentelemetry/api-metrics';
3
+ import { getOrCreateObservableCounter, getOrCreateObservableGauge, getOrCreateObservableUpDownCounter } from '../metric-data';
4
+
5
+ export const OtelObservableGauge = createParamDecorator((name: string, options?: MetricOptions) => {
6
+ if (!name || name.length === 0) {
7
+ throw new Error('OtelObservableGauge need a name argument');
8
+ }
9
+ return getOrCreateObservableGauge(name, options);
10
+ });
11
+
12
+ export const OtelObservableCounter = createParamDecorator(
13
+ (name: string, options?: MetricOptions) => {
14
+ if (!name || name.length === 0) {
15
+ throw new Error('OtelObservableCounter need a name argument');
16
+ }
17
+ return getOrCreateObservableCounter(name, options);
18
+ },
19
+ );
20
+
21
+ export const OtelObservableUpDownCounter = createParamDecorator(
22
+ (name: string, options?: MetricOptions) => {
23
+ if (!name || name.length === 0) {
24
+ throw new Error('OtelObservableUpDownCounter need a name argument');
25
+ }
26
+ return getOrCreateObservableUpDownCounter(name, options);
27
+ },
28
+ );
@@ -1,56 +1,116 @@
1
1
  import {
2
- Counter, MetricOptions, metrics, UpDownCounter, ValueRecorder,
2
+ Counter, MetricOptions, metrics, UpDownCounter,
3
+ Histogram, ObservableGauge, ObservableCounter, ObservableUpDownCounter, ObservableResult,
3
4
  } from '@opentelemetry/api-metrics';
4
5
  import { OTEL_METER_NAME } from '../opentelemetry.constants';
5
6
 
6
- export type GenericMetric = Counter | UpDownCounter | ValueRecorder;
7
+ export type GenericMetric =
8
+ Counter |
9
+ UpDownCounter |
10
+ Histogram |
11
+ ObservableGauge |
12
+ ObservableCounter |
13
+ ObservableUpDownCounter;
7
14
 
8
15
  export enum MetricType {
9
16
  'Counter' = 'Counter',
10
17
  'UpDownCounter' = 'UpDownCounter',
11
- 'ValueRecorder' = 'ValueRecorder',
18
+ 'Histogram' = 'Histogram',
19
+ 'ObservableGauge' = 'ObservableGauge',
20
+ 'ObservableCounter' = 'ObservableCounter',
21
+ 'ObservableUpDownCounter' = 'ObservableUpDownCounter',
12
22
  }
13
23
 
14
24
  export const meterData: Map<string, GenericMetric> = new Map();
15
25
 
16
- export function getOrCreateValueRecorder(
17
- name: string, type: MetricType, options: MetricOptions,
18
- ): ValueRecorder {
26
+ export function getOrCreateHistogram(
27
+ name: string,
28
+ options: MetricOptions,
29
+ ): Histogram {
19
30
  if (meterData.has(name)) {
20
- return meterData.get(name) as ValueRecorder;
31
+ return meterData.get(name) as Histogram;
21
32
  }
22
33
 
23
34
  const meter = metrics.getMeterProvider().getMeter(OTEL_METER_NAME);
35
+ const histogram = meter.createHistogram(name, options);
36
+ meterData.set(name, histogram);
37
+ return histogram;
38
+ }
24
39
 
25
- switch (type) {
26
- case MetricType.ValueRecorder:
27
- const valueRecorder = meter.createValueRecorder(name, options);
28
- meterData.set(name, valueRecorder);
29
- return valueRecorder;
30
- default:
31
- throw new Error(`Unknown type: ${type}`);
40
+ export function getOrCreateCounter(
41
+ name: string,
42
+ options: MetricOptions,
43
+ ): Counter {
44
+ if (meterData.has(name)) {
45
+ return meterData.get(name) as Counter;
32
46
  }
47
+
48
+ const meter = metrics.getMeterProvider().getMeter(OTEL_METER_NAME);
49
+
50
+ const counter = meter.createCounter(name, options);
51
+ meterData.set(name, counter);
52
+ return counter;
33
53
  }
34
54
 
35
- export function getOrCreateCounter(
36
- name: string, type: MetricType, options: MetricOptions,
37
- ): Counter | UpDownCounter {
55
+ export function getOrCreateUpDownCounter(
56
+ name: string,
57
+ options: MetricOptions,
58
+ ): UpDownCounter {
38
59
  if (meterData.has(name)) {
39
- return meterData.get(name) as Counter | UpDownCounter;
60
+ return meterData.get(name) as UpDownCounter;
40
61
  }
41
62
 
42
63
  const meter = metrics.getMeterProvider().getMeter(OTEL_METER_NAME);
43
64
 
44
- switch (type) {
45
- case MetricType.Counter:
46
- const counter = meter.createCounter(name, options);
47
- meterData.set(name, counter);
48
- return counter;
49
- case MetricType.UpDownCounter:
50
- const upDownCounter = meter.createUpDownCounter(name, options);
51
- meterData.set(name, upDownCounter);
52
- return upDownCounter;
53
- default:
54
- throw new Error(`Unknown type: ${type}`);
65
+ const upDownCounter = meter.createUpDownCounter(name, options);
66
+ meterData.set(name, upDownCounter);
67
+ return upDownCounter;
68
+ }
69
+
70
+ export function getOrCreateObservableGauge(
71
+ name: string,
72
+ options: MetricOptions,
73
+ callback?: (observableResult: ObservableResult) => void,
74
+ ): ObservableGauge {
75
+ if (meterData.has(name)) {
76
+ return meterData.get(name) as ObservableGauge;
55
77
  }
78
+
79
+ const meter = metrics.getMeterProvider().getMeter(OTEL_METER_NAME);
80
+
81
+ const observableGauge = meter.createObservableGauge(name, options, callback);
82
+ meterData.set(name, observableGauge);
83
+ return observableGauge;
84
+ }
85
+
86
+ export function getOrCreateObservableCounter(
87
+ name: string,
88
+ options: MetricOptions,
89
+ callback?: (observableResult: ObservableResult) => void,
90
+ ): ObservableCounter {
91
+ if (meterData.has(name)) {
92
+ return meterData.get(name) as ObservableCounter;
93
+ }
94
+
95
+ const meter = metrics.getMeterProvider().getMeter(OTEL_METER_NAME);
96
+
97
+ const observableCounter = meter.createObservableCounter(name, options, callback);
98
+ meterData.set(name, observableCounter);
99
+ return observableCounter;
100
+ }
101
+
102
+ export function getOrCreateObservableUpDownCounter(
103
+ name: string,
104
+ options: MetricOptions,
105
+ callback?: (observableResult: ObservableResult) => void,
106
+ ): ObservableUpDownCounter {
107
+ if (meterData.has(name)) {
108
+ return meterData.get(name) as ObservableUpDownCounter;
109
+ }
110
+
111
+ const meter = metrics.getMeterProvider().getMeter(OTEL_METER_NAME);
112
+
113
+ const observableCounter = meter.createObservableCounter(name, options, callback);
114
+ meterData.set(name, observableCounter);
115
+ return observableCounter;
56
116
  }
@@ -1,34 +1,48 @@
1
1
  import { Injectable } from '@nestjs/common';
2
2
  import {
3
- Counter, UpDownCounter, ValueRecorder, MetricOptions,
3
+ MetricOptions, ObservableResult,
4
4
  } from '@opentelemetry/api-metrics';
5
5
  import {
6
- getOrCreateCounter, getOrCreateValueRecorder, MetricType,
6
+ getOrCreateCounter, getOrCreateHistogram,
7
+ getOrCreateObservableCounter, getOrCreateObservableGauge,
8
+ getOrCreateObservableUpDownCounter, getOrCreateUpDownCounter,
7
9
  } from './metric-data';
8
10
 
9
11
  @Injectable()
10
12
  export class MetricService {
11
13
  getCounter(name: string, options?: MetricOptions) {
12
- return this.getOrCreateCounter(name, MetricType.Counter, options);
14
+ return getOrCreateCounter(name, options);
13
15
  }
14
16
 
15
17
  getUpDownCounter(name: string, options?: MetricOptions) {
16
- return this.getOrCreateCounter(name, MetricType.UpDownCounter, options);
18
+ return getOrCreateUpDownCounter(name, options);
17
19
  }
18
20
 
19
- getValueRecorder(name: string, options?: MetricOptions) {
20
- return this.getOrCreateValueRecorder(name, MetricType.ValueRecorder, options);
21
+ getHistogram(name: string, options?: MetricOptions) {
22
+ return getOrCreateHistogram(name, options);
21
23
  }
22
24
 
23
- private getOrCreateValueRecorder(
24
- name: string, type: MetricType, options: MetricOptions,
25
- ): ValueRecorder {
26
- return getOrCreateValueRecorder(name, type, options);
25
+ getObservableCounter(
26
+ name: string,
27
+ options?: MetricOptions,
28
+ callback?: (observableResult: ObservableResult) => void,
29
+ ) {
30
+ return getOrCreateObservableCounter(name, options, callback);
27
31
  }
28
32
 
29
- private getOrCreateCounter(
30
- name: string, type: MetricType, options: MetricOptions,
31
- ): Counter | UpDownCounter {
32
- return getOrCreateCounter(name, type, options);
33
+ getObservableGauge(
34
+ name: string,
35
+ options?: MetricOptions,
36
+ callback?: (observableResult: ObservableResult) => void,
37
+ ) {
38
+ return getOrCreateObservableGauge(name, options, callback);
39
+ }
40
+
41
+ getObservableUpDownCounter(
42
+ name: string,
43
+ options?: MetricOptions,
44
+ callback?: (observableResult: ObservableResult) => void,
45
+ ) {
46
+ return getOrCreateObservableUpDownCounter(name, options, callback);
33
47
  }
34
48
  }
@@ -1,7 +1,7 @@
1
1
  import { Inject, Injectable, NestMiddleware } from '@nestjs/common';
2
2
  import * as responseTime from 'response-time';
3
3
  import * as urlParser from 'url';
4
- import { Counter, Labels, ValueRecorder } from '@opentelemetry/api-metrics';
4
+ import { Counter, Attributes, Histogram } from '@opentelemetry/api-metrics';
5
5
  import { OpenTelemetryModuleOptions } from '../interfaces';
6
6
  import { MetricService } from '../metrics/metric.service';
7
7
  import { OPENTELEMETRY_MODULE_OPTIONS } from '../opentelemetry.constants';
@@ -37,17 +37,19 @@ export class ApiMetricsMiddleware implements NestMiddleware {
37
37
 
38
38
  private serverAbortsTotal: Counter;
39
39
 
40
- private requestDuration: ValueRecorder;
40
+ private requestDuration: Histogram;
41
41
 
42
- private requestSizeValueRecorder: ValueRecorder;
42
+ private requestSizeHistogram: Histogram;
43
43
 
44
- private responseSizeValueRecorder: ValueRecorder;
44
+ private responseSizeHistogram: Histogram;
45
45
 
46
- private defaultLabels: Labels;
46
+ private defaultAttributes: Attributes;
47
+
48
+ private readonly ignoreUndefinedRoutes: boolean;
47
49
 
48
50
  constructor(
49
- @Inject(OPENTELEMETRY_MODULE_OPTIONS) private readonly options: OpenTelemetryModuleOptions = {},
50
51
  @Inject(MetricService) private readonly metricService: MetricService,
52
+ @Inject(OPENTELEMETRY_MODULE_OPTIONS) private readonly options: OpenTelemetryModuleOptions = {},
51
53
  ) {
52
54
  this.requestTotal = this.metricService.getCounter('http_request_total', {
53
55
  description: 'Total number of HTTP requests',
@@ -78,23 +80,25 @@ export class ApiMetricsMiddleware implements NestMiddleware {
78
80
  });
79
81
 
80
82
  const {
81
- timeBuckets = [], requestSizeBuckets = [], responseSizeBuckets = [], defaultLabels = {},
83
+ timeBuckets = [], requestSizeBuckets = [], responseSizeBuckets = [], defaultAttributes = {},
84
+ ignoreUndefinedRoutes = false,
82
85
  } = options?.metrics?.apiMetrics;
83
86
 
84
- this.defaultLabels = defaultLabels;
87
+ this.defaultAttributes = defaultAttributes;
88
+ this.ignoreUndefinedRoutes = ignoreUndefinedRoutes;
85
89
 
86
- this.requestDuration = this.metricService.getValueRecorder('http_request_duration_seconds', {
90
+ this.requestDuration = this.metricService.getHistogram('http_request_duration_seconds', {
87
91
  boundaries: timeBuckets.length > 0 ? timeBuckets : this.defaultLongRunningRequestBuckets,
88
92
  description: 'HTTP latency value recorder in seconds',
89
93
  });
90
94
 
91
- this.requestSizeValueRecorder = this.metricService.getValueRecorder('http_request_size_bytes', {
95
+ this.requestSizeHistogram = this.metricService.getHistogram('http_request_size_bytes', {
92
96
  boundaries:
93
97
  requestSizeBuckets.length > 0 ? requestSizeBuckets : this.defaultRequestSizeBuckets,
94
98
  description: 'Current total of incoming bytes',
95
99
  });
96
100
 
97
- this.responseSizeValueRecorder = this.metricService.getValueRecorder('http_response_size_bytes', {
101
+ this.responseSizeHistogram = this.metricService.getHistogram('http_response_size_bytes', {
98
102
  boundaries:
99
103
  responseSizeBuckets.length > 0 ? responseSizeBuckets : this.defaultResponseSizeBuckets,
100
104
  description: 'Current total of outgoing bytes',
@@ -109,35 +113,27 @@ export class ApiMetricsMiddleware implements NestMiddleware {
109
113
 
110
114
  if (route) {
111
115
  path = route.path;
116
+ } else if (this.ignoreUndefinedRoutes) {
117
+ return;
112
118
  } else {
113
119
  path = urlParser.parse(url).pathname;
114
120
  }
115
121
 
116
- if (path === '/favicon.ico') {
117
- return;
118
- }
119
-
120
- // TODO Support `ignore routes array instead`
121
- const metricPath = '/metrics';
122
- if (path === metricPath) {
123
- return;
124
- }
125
-
126
- this.requestTotal.bind({ method, path }).add(1);
122
+ this.requestTotal.add(1, { method, path });
127
123
 
128
124
  const requestLength = parseInt(req.headers['content-length'], 10) || 0;
129
125
  const responseLength: number = parseInt(res.getHeader('Content-Length'), 10) || 0;
130
126
 
131
127
  const status = res.statusCode || 500;
132
- const labels: Labels = {
133
- method, status, path, ...this.defaultLabels,
128
+ const attributes: Attributes = {
129
+ method, status, path, ...this.defaultAttributes,
134
130
  };
135
131
 
136
- this.requestSizeValueRecorder.bind(labels).record(requestLength);
137
- this.responseSizeValueRecorder.bind(labels).record(responseLength);
132
+ this.requestSizeHistogram.record(requestLength, attributes);
133
+ this.responseSizeHistogram.record(responseLength, attributes);
138
134
 
139
- this.responseTotal.bind(labels).add(1);
140
- this.requestDuration.bind(labels).record(time / 1000);
135
+ this.responseTotal.add(1, attributes);
136
+ this.requestDuration.record(time / 1000, attributes);
141
137
 
142
138
  const codeClass = this.getStatusCodeClass(status);
143
139
 
@@ -147,7 +143,7 @@ export class ApiMetricsMiddleware implements NestMiddleware {
147
143
  this.responseSuccessTotal.add(1);
148
144
  break;
149
145
  case 'redirect':
150
- // TODO: Review what should be appropriate for redirects.
146
+ // TODO: Review what should be appropriate for redirects.
151
147
  this.responseSuccessTotal.add(1);
152
148
  break;
153
149
  case 'client_error':
@@ -27,7 +27,7 @@ export class OpenTelemetryCoreModule implements OnApplicationBootstrap {
27
27
 
28
28
  constructor(
29
29
  @Inject(OPENTELEMETRY_MODULE_OPTIONS) private readonly options: OpenTelemetryModuleOptions = {},
30
- ) {}
30
+ ) { }
31
31
 
32
32
  /**
33
33
  * Bootstraps the internal OpenTelemetry Module with the given options
@@ -82,14 +82,19 @@ export class OpenTelemetryCoreModule implements OnApplicationBootstrap {
82
82
  const {
83
83
  apiMetrics = { enable: false },
84
84
  } = this.options?.metrics;
85
+
85
86
  if (apiMetrics.enable === true) {
86
- consumer.apply(ApiMetricsMiddleware).forRoutes('*');
87
+ if (apiMetrics?.ignoreRoutes && apiMetrics?.ignoreRoutes.length > 0) {
88
+ consumer.apply(ApiMetricsMiddleware).exclude(...apiMetrics.ignoreRoutes).forRoutes('*');
89
+ } else {
90
+ consumer.apply(ApiMetricsMiddleware).forRoutes('*');
91
+ }
87
92
  }
88
93
  }
89
94
 
90
95
  async onApplicationBootstrap() {
91
- let defaultMetrics:boolean = false;
92
- let hostMetrics:boolean = false;
96
+ let defaultMetrics: boolean = false;
97
+ let hostMetrics: boolean = false;
93
98
 
94
99
  if (this.options?.metrics) {
95
100
  defaultMetrics = this.options.metrics.defaultMetrics
@@ -3,12 +3,15 @@ import { Injectable } from '@nestjs/common';
3
3
 
4
4
  @Injectable()
5
5
  export class TraceService {
6
+ public getTracer() {
7
+ return trace.getTracer('default');
8
+ }
9
+
6
10
  public getSpan(): Span {
7
11
  return trace.getSpan(context.active());
8
12
  }
9
13
 
10
14
  public startSpan(name: string): Span {
11
- const tracer = trace.getTracer('default');
12
- return tracer.startSpan(name);
15
+ return this.getTracer().startSpan(name);
13
16
  }
14
17
  }
@@ -1 +0,0 @@
1
- export declare const OtelValueRecorder: (...dataOrPipes: (string | import("@nestjs/common").PipeTransform<any, any> | import("@nestjs/common").Type<import("@nestjs/common").PipeTransform<any, any>>)[]) => ParameterDecorator;
@@ -1,12 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.OtelValueRecorder = void 0;
4
- const common_1 = require("@nestjs/common");
5
- const metric_data_1 = require("../metric-data");
6
- exports.OtelValueRecorder = (0, common_1.createParamDecorator)((name, options) => {
7
- if (!name || name.length === 0) {
8
- throw new Error('OtelValueRecorder need a name argument');
9
- }
10
- return (0, metric_data_1.getOrCreateValueRecorder)(name, metric_data_1.MetricType.ValueRecorder, options);
11
- });
12
- //# sourceMappingURL=value-recorder.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"value-recorder.js","sourceRoot":"","sources":["../../../src/metrics/decorators/value-recorder.ts"],"names":[],"mappings":";;;AAAA,2CAAsD;AAEtD,gDAAsE;AAEzD,QAAA,iBAAiB,GAAG,IAAA,6BAAoB,EAAC,CACpD,IAAY,EAAE,OAAuB,EACrC,EAAE;IACF,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE;QAC9B,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;KAC3D;IACD,OAAO,IAAA,sCAAwB,EAAC,IAAI,EAAE,wBAAU,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;AAC3E,CAAC,CAAC,CAAC"}
@@ -1,12 +0,0 @@
1
- import { createParamDecorator } from '@nestjs/common';
2
- import { MetricOptions } from '@opentelemetry/api-metrics';
3
- import { getOrCreateValueRecorder, MetricType } from '../metric-data';
4
-
5
- export const OtelValueRecorder = createParamDecorator((
6
- name: string, options?: MetricOptions,
7
- ) => {
8
- if (!name || name.length === 0) {
9
- throw new Error('OtelValueRecorder need a name argument');
10
- }
11
- return getOrCreateValueRecorder(name, MetricType.ValueRecorder, options);
12
- });