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.
- package/CHANGELOG.md +44 -0
- package/README.md +28 -9
- package/examples/nestjs-prom-grafana-tempo/package.json +40 -37
- package/examples/nestjs-prom-grafana-tempo/src/tracing.ts +5 -5
- package/lib/interfaces/opentelemetry-options.interface.d.ts +5 -2
- package/lib/metrics/decorators/common.js +2 -2
- package/lib/metrics/decorators/common.js.map +1 -1
- package/lib/metrics/decorators/counter.js +2 -2
- package/lib/metrics/decorators/counter.js.map +1 -1
- package/lib/metrics/decorators/histogram.d.ts +1 -0
- package/lib/metrics/decorators/histogram.js +12 -0
- package/lib/metrics/decorators/histogram.js.map +1 -0
- package/lib/metrics/decorators/index.d.ts +2 -1
- package/lib/metrics/decorators/index.js +2 -1
- package/lib/metrics/decorators/index.js.map +1 -1
- package/lib/metrics/decorators/observable.d.ts +3 -0
- package/lib/metrics/decorators/observable.js +24 -0
- package/lib/metrics/decorators/observable.js.map +1 -0
- package/lib/metrics/metric-data.d.ts +12 -5
- package/lib/metrics/metric-data.js +53 -24
- package/lib/metrics/metric-data.js.map +1 -1
- package/lib/metrics/metric.service.d.ts +7 -6
- package/lib/metrics/metric.service.js +11 -8
- package/lib/metrics/metric.service.js.map +1 -1
- package/lib/middleware/api-metrics.middleware.d.ts +6 -5
- package/lib/middleware/api-metrics.middleware.js +21 -24
- package/lib/middleware/api-metrics.middleware.js.map +1 -1
- package/lib/opentelemetry-core.module.js +6 -1
- package/lib/opentelemetry-core.module.js.map +1 -1
- package/lib/tracing/trace.service.d.ts +1 -0
- package/lib/tracing/trace.service.js +4 -2
- package/lib/tracing/trace.service.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +32 -32
- package/src/interfaces/opentelemetry-options.interface.ts +6 -3
- package/src/metrics/decorators/common.ts +7 -5
- package/src/metrics/decorators/counter.ts +5 -9
- package/src/metrics/decorators/histogram.ts +10 -0
- package/src/metrics/decorators/observable.ts +28 -0
- package/src/metrics/metric-data.ts +89 -29
- package/src/metrics/metric.service.ts +28 -14
- package/src/middleware/api-metrics.middleware.ts +25 -29
- package/src/opentelemetry-core.module.ts +9 -4
- package/src/tracing/trace.service.ts +5 -2
- package/lib/metrics/decorators/value-recorder.d.ts +0 -1
- package/lib/metrics/decorators/value-recorder.js +0 -12
- package/lib/metrics/decorators/value-recorder.js.map +0 -1
- 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": "
|
|
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 --
|
|
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
|
|
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.
|
|
41
|
-
"@opentelemetry/api": "^1.0.
|
|
42
|
-
"@opentelemetry/api-metrics": "^0.
|
|
43
|
-
"
|
|
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": "^
|
|
49
|
-
"@commitlint/config-conventional": "^
|
|
50
|
-
"@nestjs/common": "^8.
|
|
51
|
-
"@nestjs/core": "^8.
|
|
52
|
-
"@nestjs/platform-express": "^8.
|
|
53
|
-
"@nestjs/platform-fastify": "^8.
|
|
54
|
-
"@nestjs/testing": "^8.
|
|
55
|
-
"@opentelemetry/exporter-prometheus": "^0.
|
|
56
|
-
"@opentelemetry/sdk-node": "^0.
|
|
57
|
-
"@types/jest": "^27.0.
|
|
58
|
-
"@types/node": "^
|
|
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": "
|
|
61
|
-
"@typescript-eslint/parser": "
|
|
62
|
-
"eslint": "
|
|
63
|
-
"eslint-config-airbnb-
|
|
64
|
-
"eslint-
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
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.
|
|
69
|
+
"rxjs": "^7.5.1",
|
|
71
70
|
"supertest": "^6.1.6",
|
|
72
|
-
"ts-jest": "^27.
|
|
73
|
-
"typescript": "4.4
|
|
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 {
|
|
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
|
-
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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 =
|
|
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
|
-
'
|
|
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
|
|
17
|
-
name: string,
|
|
18
|
-
|
|
26
|
+
export function getOrCreateHistogram(
|
|
27
|
+
name: string,
|
|
28
|
+
options: MetricOptions,
|
|
29
|
+
): Histogram {
|
|
19
30
|
if (meterData.has(name)) {
|
|
20
|
-
return meterData.get(name) as
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
|
36
|
-
name: string,
|
|
37
|
-
|
|
55
|
+
export function getOrCreateUpDownCounter(
|
|
56
|
+
name: string,
|
|
57
|
+
options: MetricOptions,
|
|
58
|
+
): UpDownCounter {
|
|
38
59
|
if (meterData.has(name)) {
|
|
39
|
-
return meterData.get(name) as
|
|
60
|
+
return meterData.get(name) as UpDownCounter;
|
|
40
61
|
}
|
|
41
62
|
|
|
42
63
|
const meter = metrics.getMeterProvider().getMeter(OTEL_METER_NAME);
|
|
43
64
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
3
|
+
MetricOptions, ObservableResult,
|
|
4
4
|
} from '@opentelemetry/api-metrics';
|
|
5
5
|
import {
|
|
6
|
-
getOrCreateCounter,
|
|
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
|
|
14
|
+
return getOrCreateCounter(name, options);
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
getUpDownCounter(name: string, options?: MetricOptions) {
|
|
16
|
-
return
|
|
18
|
+
return getOrCreateUpDownCounter(name, options);
|
|
17
19
|
}
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
return
|
|
21
|
+
getHistogram(name: string, options?: MetricOptions) {
|
|
22
|
+
return getOrCreateHistogram(name, options);
|
|
21
23
|
}
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
name: string,
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
30
|
-
name: string,
|
|
31
|
-
|
|
32
|
-
|
|
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,
|
|
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:
|
|
40
|
+
private requestDuration: Histogram;
|
|
41
41
|
|
|
42
|
-
private
|
|
42
|
+
private requestSizeHistogram: Histogram;
|
|
43
43
|
|
|
44
|
-
private
|
|
44
|
+
private responseSizeHistogram: Histogram;
|
|
45
45
|
|
|
46
|
-
private
|
|
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 = [],
|
|
83
|
+
timeBuckets = [], requestSizeBuckets = [], responseSizeBuckets = [], defaultAttributes = {},
|
|
84
|
+
ignoreUndefinedRoutes = false,
|
|
82
85
|
} = options?.metrics?.apiMetrics;
|
|
83
86
|
|
|
84
|
-
this.
|
|
87
|
+
this.defaultAttributes = defaultAttributes;
|
|
88
|
+
this.ignoreUndefinedRoutes = ignoreUndefinedRoutes;
|
|
85
89
|
|
|
86
|
-
this.requestDuration = this.metricService.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
|
133
|
-
method, status, path, ...this.
|
|
128
|
+
const attributes: Attributes = {
|
|
129
|
+
method, status, path, ...this.defaultAttributes,
|
|
134
130
|
};
|
|
135
131
|
|
|
136
|
-
this.
|
|
137
|
-
this.
|
|
132
|
+
this.requestSizeHistogram.record(requestLength, attributes);
|
|
133
|
+
this.responseSizeHistogram.record(responseLength, attributes);
|
|
138
134
|
|
|
139
|
-
this.responseTotal.
|
|
140
|
-
this.requestDuration.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
});
|