feat(SPG-126) Monitoring APM

master
ashar 2024-05-30 09:37:12 +07:00
parent 1124307453
commit bbff8352af
5 changed files with 124 additions and 0 deletions

View File

@ -0,0 +1,36 @@
import {
Injectable,
NestInterceptor,
ExecutionContext,
CallHandler,
HttpException,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { ApmService } from './apm.service';
@Injectable()
export class ApmInterceptor implements NestInterceptor {
constructor(private readonly apmService: ApmService) {}
intercept(
context: ExecutionContext,
next: CallHandler,
): Observable<Response> {
const request = context.switchToHttp().getRequest<Request>();
const user = request.headers['e-user'];
const rules = request.headers['e-rules'];
this.apmService.setCustomContext({ user, rules });
return next.handle().pipe(
catchError((error) => {
if (error instanceof HttpException) {
this.apmService.captureError(error.message);
} else {
this.apmService.captureError(error);
}
throw error;
}),
);
}
}

View File

@ -0,0 +1,22 @@
import { DynamicModule } from '@nestjs/common';
import { ApmService } from './apm.service';
import { APP_INTERCEPTOR } from '@nestjs/core';
import { ApmInterceptor } from './apm.interceptor';
// import { startAPM } from './start'
export class ApmModule {
static register(): DynamicModule {
return {
module: ApmModule,
imports: [],
providers: [
ApmService,
{
provide: APP_INTERCEPTOR,
useClass: ApmInterceptor,
},
],
exports: [ApmService],
};
}
}

View File

@ -0,0 +1,35 @@
import { Injectable } from '@nestjs/common';
import * as APM from 'elastic-apm-node';
import apm = require('elastic-apm-node');
@Injectable()
export class ApmService {
private readonly apm: apm.Agent;
constructor() {
this.apm = APM;
}
captureError(data: Error | string): void {
this.apm.captureError(data);
}
startTransaction(
name?: string,
options?: apm.TransactionOptions,
): apm.Transaction | null {
return this.apm.startTransaction(name, options);
}
setTransactionName(name: string): void {
this.apm.setTransactionName(name);
}
startSpan(name?: string, options?: apm.SpanOptions): apm.Span | null {
return this.apm.startSpan(name, options);
}
setCustomContext(context: Record<string, unknown>): void {
this.apm.setCustomContext(context);
}
}

4
src/core/apm/index.ts Normal file
View File

@ -0,0 +1,4 @@
export * from './start';
export * from './apm.module';
export * from './apm.service';
export * from './apm.interceptor';

27
src/core/apm/start.ts Normal file
View File

@ -0,0 +1,27 @@
import * as dotenv from 'dotenv';
dotenv.config(); //
import * as apmAgent from 'elastic-apm-node';
const options: apmAgent.AgentConfigOptions = {
active: process.env.ELASTIC_APM_ACTIVATE === 'true',
};
if (process.env.ELASTIC_APM_SERVICE_NAME) {
options['serviceName'] = process.env.ELASTIC_APM_SERVICE_NAME;
}
if (process.env.ELASTIC_APM_SECRET_TOKEN) {
options['secretToken'] = process.env.ELASTIC_APM_SECRET_TOKEN;
}
if (process.env.ELASTIC_APM_API_KEY) {
options['apiKey'] = process.env.ELASTIC_APM_API_KEY;
}
if (process.env.ELASTIC_APM_SERVER_URL) {
options['serverUrl'] = process.env.ELASTIC_APM_SERVER_URL;
}
if (process.env.ELASTIC_APM_DISABLE_INSTRUMENTATIONS) {
options['disableInstrumentations'] =
process.env.ELASTIC_APM_DISABLE_INSTRUMENTATIONS.split(',');
}
const apm: apmAgent.Agent = apmAgent.start(options);
export { apm };