mirror of
https://github.com/immich-app/immich.git
synced 2025-12-22 17:24:56 +03:00
feat(server): log http exceptions (#9996)
This commit is contained in:
37
server/src/middleware/http-exception.filter.ts
Normal file
37
server/src/middleware/http-exception.filter.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { ArgumentsHost, Catch, ExceptionFilter, HttpException, Inject } from '@nestjs/common';
|
||||
import { Response } from 'express';
|
||||
import { ClsService } from 'nestjs-cls';
|
||||
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
||||
|
||||
@Catch(HttpException)
|
||||
export class HttpExceptionFilter implements ExceptionFilter {
|
||||
constructor(
|
||||
@Inject(ILoggerRepository) private logger: ILoggerRepository,
|
||||
private cls: ClsService,
|
||||
) {
|
||||
this.logger.setContext(HttpExceptionFilter.name);
|
||||
}
|
||||
|
||||
catch(exception: HttpException, host: ArgumentsHost) {
|
||||
const ctx = host.switchToHttp();
|
||||
const response = ctx.getResponse<Response>();
|
||||
const status = exception.getStatus();
|
||||
|
||||
this.logger.debug(`HttpException(${status}) ${JSON.stringify(exception.getResponse())}`);
|
||||
|
||||
let responseBody = exception.getResponse();
|
||||
// unclear what circumstances would return a string
|
||||
if (typeof responseBody === 'string') {
|
||||
responseBody = {
|
||||
error: 'Unknown',
|
||||
message: responseBody,
|
||||
statusCode: status,
|
||||
};
|
||||
}
|
||||
|
||||
response.status(status).json({
|
||||
...responseBody,
|
||||
correlationId: this.cls.getId(),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,21 @@
|
||||
import { CallHandler, ExecutionContext, Inject, Injectable, NestInterceptor } from '@nestjs/common';
|
||||
import { Request, Response } from 'express';
|
||||
import { Observable, finalize } from 'rxjs';
|
||||
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
||||
|
||||
const maxArrayLength = 100;
|
||||
const replacer = (key: string, value: unknown) => {
|
||||
if (key.toLowerCase().includes('password')) {
|
||||
return '********';
|
||||
}
|
||||
|
||||
if (Array.isArray(value) && value.length > maxArrayLength) {
|
||||
return [...value.slice(0, maxArrayLength), `...and ${value.length - maxArrayLength} more`];
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class LoggingInterceptor implements NestInterceptor {
|
||||
constructor(@Inject(ILoggerRepository) private logger: ILoggerRepository) {
|
||||
@@ -10,18 +24,23 @@ export class LoggingInterceptor implements NestInterceptor {
|
||||
|
||||
intercept(context: ExecutionContext, next: CallHandler<any>): Observable<any> {
|
||||
const handler = context.switchToHttp();
|
||||
const req = handler.getRequest();
|
||||
const res = handler.getResponse();
|
||||
const req = handler.getRequest<Request>();
|
||||
const res = handler.getResponse<Response>();
|
||||
|
||||
const { method, ip, path } = req;
|
||||
const { method, ip, url } = req;
|
||||
|
||||
const start = performance.now();
|
||||
|
||||
return next.handle().pipe(
|
||||
finalize(() => {
|
||||
const finish = performance.now();
|
||||
const duration = (finish - start).toFixed(2);
|
||||
const { statusCode } = res;
|
||||
this.logger.verbose(`${method} ${path} ${statusCode} ${duration}ms ${ip}`);
|
||||
|
||||
this.logger.debug(`${method} ${url} ${statusCode} ${duration}ms ${ip}`);
|
||||
if (req.body && Object.keys(req.body).length > 0) {
|
||||
this.logger.verbose(JSON.stringify(req.body, replacer));
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user