feat:保存调用日志

This commit is contained in:
Jerry 2025-10-15 15:16:21 +08:00
parent c2194e6140
commit fc2ffeb145
3 changed files with 112 additions and 3 deletions

View File

@ -0,0 +1,108 @@
import {
CallHandler,
ExecutionContext,
Injectable,
Logger,
NestInterceptor,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import * as fs from 'fs';
import * as path from 'path';
interface RequestLogEntry {
timestamp: string;
ip: string | string[] | undefined;
method: string;
url: string;
controller: string;
handler: string;
userAgent?: string;
params?: any;
query?: any;
body?: any;
statusCode?: number;
durationMs?: number;
}
@Injectable()
export class RequestLogInterceptor implements NestInterceptor {
private readonly logger = new Logger(RequestLogInterceptor.name);
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const http = context.switchToHttp();
const req = http.getRequest();
const res = http.getResponse();
const now = Date.now();
const controller = context.getClass().name;
const handler = context.getHandler().name;
const ip = req.headers['x-forwarded-for'] || req.ip;
const method = req.method;
const url = req.originalUrl || req.url;
const userAgent = req.headers['user-agent'];
this.logger.log(
`${method} ${url} - ${controller}.${handler} - ip=${Array.isArray(ip) ? ip[0] : ip}`,
);
return next.handle().pipe(
tap({
next: () => {
this.writeLog({
timestamp: new Date(now).toISOString(),
ip,
method,
url,
controller,
handler,
userAgent,
params: req.params,
query: req.query,
body: req.body,
statusCode: res.statusCode,
durationMs: Date.now() - now,
});
},
error: () => {
this.writeLog({
timestamp: new Date(now).toISOString(),
ip,
method,
url,
controller,
handler,
userAgent,
params: req.params,
query: req.query,
body: req.body,
statusCode: res.statusCode,
durationMs: Date.now() - now,
});
},
}),
);
}
private writeLog(entry: RequestLogEntry) {
try {
const baseDir = path.resolve(process.cwd(), 'logs');
if (!fs.existsSync(baseDir)) {
fs.mkdirSync(baseDir, { recursive: true });
}
const fileName = `access-${this.getDateStr()}.jsonl`;
const filePath = path.join(baseDir, fileName);
const serialized = JSON.stringify(entry) + '\n';
fs.appendFile(filePath, serialized, { encoding: 'utf8' }, () => {});
} catch (err) {
this.logger.warn(`写入访问日志失败: ${err?.message || err}`);
}
}
private getDateStr(): string {
const d = new Date();
const yyyy = d.getFullYear();
const mm = String(d.getMonth() + 1).padStart(2, '0');
const dd = String(d.getDate()).padStart(2, '0');
return `${yyyy}-${mm}-${dd}`;
}
}

View File

@ -3,6 +3,7 @@ import { AppModule } from './app.module';
import { Logger, RequestMethod } from '@nestjs/common';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { ResponseInterceptor } from './common/interceptors/response.interceptor';
import { RequestLogInterceptor } from './common/interceptors/request-log.interceptor';
import { AllExceptionsFilter } from './common/filters/all-exception.filter';
import { SystemService } from './core/system/system.service';
import { WsAdapter } from '@nestjs/platform-ws';
@ -35,7 +36,7 @@ async function bootstrap() {
{ path: 'public/(.*)', method: RequestMethod.ALL },
],
});
app.useGlobalInterceptors(new ResponseInterceptor());
app.useGlobalInterceptors(new RequestLogInterceptor(), new ResponseInterceptor());
app.useGlobalFilters(new AllExceptionsFilter());
const systemService = app.get(SystemService);
const restartDuration = systemService.checkRestartTime();

View File

@ -101,7 +101,7 @@ export class WordsService {
const safeType = this.safePathSegment(type);
const safeName = this.safePathSegment(name);
const cacheKey = `${safeType}/${safeName}`;
this.logger.log(`重载文案: ${cacheKey}..`);
//this.logger.log(`重载文案: ${cacheKey}..`);
const filePath = path.join(
this.paths.get('words'),
safeType,
@ -136,7 +136,7 @@ export class WordsService {
const names = files
.filter((f) => f.isFile() && f.name.endsWith('.json'))
.map((f) => f.name.replace(/\.json$/, ''));
this.logger.log(`扫描文案类型 ${safeType} 下的文件: ${names.join(', ')}`);
//this.logger.log(`扫描文案类型 ${safeType} 下的文件: ${names.join(', ')}`);
return names;
} catch (e) {
this.logger.error(`读取文案目录失败: ${safeType}`, e);