feat: add metrics
This commit is contained in:
parent
a692a96998
commit
4dcbf62ead
@ -31,10 +31,12 @@
|
||||
"@teacinema/common": "^1.0.0",
|
||||
"@teacinema/contracts": "^1.0.0",
|
||||
"@teacinema/passport": "^1.0.0",
|
||||
"@willsoto/nestjs-prometheus": "^6.0.2",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.3",
|
||||
"cookie-parser": "^1.4.7",
|
||||
"cross-env": "^10.1.0",
|
||||
"prom-client": "^15.1.3",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"rxjs": "^7.8.1"
|
||||
},
|
||||
|
||||
@ -5,6 +5,7 @@ import { PassportModule } from '@teacinema/passport'
|
||||
import { AccountModule } from '../modules/account/account.module'
|
||||
import { AuthModule } from '../modules/auth/auth.module'
|
||||
import { UsersModule } from '../modules/users/users.module'
|
||||
import { ObservabilityModule } from '../observability/observability.module'
|
||||
|
||||
import { AppController } from './app.controller'
|
||||
import { AppService } from './app.service'
|
||||
@ -20,6 +21,7 @@ import { getPassportConfig } from './config'
|
||||
'.env'
|
||||
]
|
||||
}),
|
||||
ObservabilityModule,
|
||||
AuthModule,
|
||||
AccountModule,
|
||||
UsersModule,
|
||||
|
||||
62
src/observability/metrics/http-metrics.interceptor.ts
Normal file
62
src/observability/metrics/http-metrics.interceptor.ts
Normal file
@ -0,0 +1,62 @@
|
||||
import {
|
||||
CallHandler,
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
NestInterceptor
|
||||
} from '@nestjs/common'
|
||||
import { InjectMetric } from '@willsoto/nestjs-prometheus'
|
||||
import { Request, Response } from 'express'
|
||||
import { Counter, Gauge, Histogram } from 'prom-client'
|
||||
import { finalize, Observable } from 'rxjs'
|
||||
|
||||
@Injectable()
|
||||
export class HttpMetricsInterceptor implements NestInterceptor {
|
||||
private readonly SERVICE_NAME: string
|
||||
|
||||
public constructor(
|
||||
@InjectMetric('http_request_duration_seconds')
|
||||
private readonly histogram: Histogram<string>,
|
||||
@InjectMetric('http_requests_in_flight')
|
||||
private readonly inFlight: Gauge<string>,
|
||||
@InjectMetric('http_request_total')
|
||||
private readonly counter: Counter<string>
|
||||
) {
|
||||
this.SERVICE_NAME = 'gateway'
|
||||
}
|
||||
|
||||
public intercept(
|
||||
context: ExecutionContext,
|
||||
next: CallHandler
|
||||
): Observable<any> {
|
||||
const req = context.switchToHttp().getRequest<Request>()
|
||||
const res = context.switchToHttp().getResponse<Response>()
|
||||
|
||||
const method = req.method
|
||||
const route = (req.route.path || 'unknown') as string
|
||||
|
||||
this.inFlight.inc({ service: this.SERVICE_NAME })
|
||||
|
||||
const entTimer = this.histogram.startTimer()
|
||||
|
||||
return next.handle().pipe(
|
||||
finalize(() => {
|
||||
const status = res.statusCode.toString()
|
||||
this.counter.inc({
|
||||
service: this.SERVICE_NAME,
|
||||
method,
|
||||
route,
|
||||
status
|
||||
})
|
||||
|
||||
entTimer({
|
||||
service: this.SERVICE_NAME,
|
||||
method,
|
||||
route,
|
||||
status
|
||||
})
|
||||
|
||||
this.inFlight.dec({ service: this.SERVICE_NAME })
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
46
src/observability/metrics/metrics.module.ts
Normal file
46
src/observability/metrics/metrics.module.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { Global, Module } from '@nestjs/common'
|
||||
import { APP_INTERCEPTOR } from '@nestjs/core'
|
||||
import {
|
||||
makeCounterProvider,
|
||||
makeGaugeProvider,
|
||||
makeHistogramProvider,
|
||||
PrometheusModule
|
||||
} from '@willsoto/nestjs-prometheus'
|
||||
|
||||
import { HttpMetricsInterceptor } from './http-metrics.interceptor'
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
imports: [
|
||||
PrometheusModule.register({
|
||||
path: '/metrics',
|
||||
defaultMetrics: {
|
||||
enabled: true
|
||||
}
|
||||
})
|
||||
],
|
||||
providers: [
|
||||
makeHistogramProvider({
|
||||
name: 'http_request_duration_seconds',
|
||||
help: 'HTTP request latency',
|
||||
labelNames: ['service', 'method', 'route', 'status'],
|
||||
buckets: [0.05, 0.1, 0.2, 0.3, 0.5, 1, 1.5, 2, 3, 5]
|
||||
}),
|
||||
makeGaugeProvider({
|
||||
name: 'http_requests_in_flight',
|
||||
help: 'Current number of http requests in flight',
|
||||
labelNames: ['service']
|
||||
}),
|
||||
makeCounterProvider({
|
||||
name: 'http_request_total',
|
||||
help: 'Total http requests',
|
||||
labelNames: ['service', 'method', 'route', 'status']
|
||||
}),
|
||||
HttpMetricsInterceptor,
|
||||
{
|
||||
provide: APP_INTERCEPTOR,
|
||||
useClass: HttpMetricsInterceptor
|
||||
}
|
||||
]
|
||||
})
|
||||
export class MetricsModule {}
|
||||
8
src/observability/observability.module.ts
Normal file
8
src/observability/observability.module.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
|
||||
import { MetricsModule } from './metrics/metrics.module'
|
||||
|
||||
@Module({
|
||||
imports: [MetricsModule]
|
||||
})
|
||||
export class ObservabilityModule {}
|
||||
30
yarn.lock
30
yarn.lock
@ -1154,6 +1154,11 @@
|
||||
dependencies:
|
||||
consola "^3.2.3"
|
||||
|
||||
"@opentelemetry/api@^1.4.0":
|
||||
version "1.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.9.0.tgz#d03eba68273dc0f7509e2a3d5cba21eae10379fe"
|
||||
integrity sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==
|
||||
|
||||
"@paralleldrive/cuid2@^2.2.2":
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz#3d62ea9e7be867d3fa94b9897fab5b0ae187d784"
|
||||
@ -1882,6 +1887,11 @@
|
||||
"@webassemblyjs/ast" "1.14.1"
|
||||
"@xtuc/long" "4.2.2"
|
||||
|
||||
"@willsoto/nestjs-prometheus@^6.0.2":
|
||||
version "6.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@willsoto/nestjs-prometheus/-/nestjs-prometheus-6.0.2.tgz#d603764a923848442ed092411716c0bf211de01f"
|
||||
integrity sha512-ePyLZYdIrOOdlOWovzzMisIgviXqhPVzFpSMKNNhn6xajhRHeBsjAzSdpxZTc6pnjR9hw1lNAHyKnKl7lAPaVg==
|
||||
|
||||
"@xtuc/ieee754@^1.2.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790"
|
||||
@ -2132,6 +2142,11 @@ baseline-browser-mapping@^2.9.0:
|
||||
resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.14.tgz#3b6af0bc032445bca04de58caa9a87cfe921cbb3"
|
||||
integrity sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg==
|
||||
|
||||
bintrees@1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/bintrees/-/bintrees-1.0.2.tgz#49f896d6e858a4a499df85c38fb399b9aff840f8"
|
||||
integrity sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==
|
||||
|
||||
bl@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a"
|
||||
@ -4453,6 +4468,14 @@ pretty-format@30.2.0, pretty-format@^30.0.0:
|
||||
ansi-styles "^5.2.0"
|
||||
react-is "^18.3.1"
|
||||
|
||||
prom-client@^15.1.3:
|
||||
version "15.1.3"
|
||||
resolved "https://registry.yarnpkg.com/prom-client/-/prom-client-15.1.3.tgz#69fa8de93a88bc9783173db5f758dc1c69fa8fc2"
|
||||
integrity sha512-6ZiOBfCywsD4k1BN9IX0uZhF+tJkV8q8llP64G5Hajs4JOeVLPCwpPVcpXy3BwYiUGgyJzsJJQeOIv7+hDSq8g==
|
||||
dependencies:
|
||||
"@opentelemetry/api" "^1.4.0"
|
||||
tdigest "^0.1.1"
|
||||
|
||||
protobufjs@^7.5.3:
|
||||
version "7.5.4"
|
||||
resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.5.4.tgz#885d31fe9c4b37f25d1bb600da30b1c5b37d286a"
|
||||
@ -4957,6 +4980,13 @@ tapable@^2.2.0, tapable@^2.2.1, tapable@^2.3.0:
|
||||
resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.3.0.tgz#7e3ea6d5ca31ba8e078b560f0d83ce9a14aa8be6"
|
||||
integrity sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==
|
||||
|
||||
tdigest@^0.1.1:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/tdigest/-/tdigest-0.1.2.tgz#96c64bac4ff10746b910b0e23b515794e12faced"
|
||||
integrity sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA==
|
||||
dependencies:
|
||||
bintrees "1.0.2"
|
||||
|
||||
terser-webpack-plugin@^5.3.16:
|
||||
version "5.3.16"
|
||||
resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.16.tgz#741e448cc3f93d8026ebe4f7ef9e4afacfd56330"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user