feat(api): Protokollzeile je Anfrage und je Nextcloud-Pruefung im Docker-Log
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { formatRequestLine } from './request-log';
|
||||
|
||||
describe('formatRequestLine', () => {
|
||||
it('schreibt Methode, Pfad, Status, Dauer und Benutzer', () => {
|
||||
expect(
|
||||
formatRequestLine({
|
||||
method: 'GET',
|
||||
url: '/modules/x',
|
||||
status: 200,
|
||||
ms: 12,
|
||||
username: 'admin',
|
||||
}),
|
||||
).toBe('GET /modules/x 200 12 ms user=admin');
|
||||
});
|
||||
|
||||
it('laesst den Abfrageteil weg', () => {
|
||||
expect(
|
||||
formatRequestLine({ method: 'GET', url: '/auth/reset?token=geheim', status: 200, ms: 1 }),
|
||||
).toBe('GET /auth/reset 200 1 ms user=-');
|
||||
});
|
||||
|
||||
it('markiert langsame Anfragen', () => {
|
||||
expect(formatRequestLine({ method: 'POST', url: '/a', status: 201, ms: 4500 })).toContain(
|
||||
'LANGSAM',
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,51 @@
|
||||
import { Logger } from '@nestjs/common';
|
||||
import type { NextFunction, Request, Response } from 'express';
|
||||
|
||||
/**
|
||||
* Eine Protokollzeile je API-Anfrage im Docker-Log (quick-261002):
|
||||
* `GET /modules/nextcloud-status/instances 200 34 ms user=admin`.
|
||||
*
|
||||
* Nur Methode, Pfad OHNE Abfrageteil (dort koennten Kennungen oder Token
|
||||
* stehen), Status, Dauer und Benutzername — nie Koerper, Cookies oder
|
||||
* Kopfzeilen. `/health` wird ausgelassen, der Docker-Healthcheck fragt es
|
||||
* alle paar Sekunden ab. Fehler (>= 500) als `error`, Abweisungen (>= 400)
|
||||
* als `warn`, langsame Anfragen (>= 3 s) mit Vermerk.
|
||||
*/
|
||||
const logger = new Logger('HTTP');
|
||||
export const SLOW_REQUEST_MS = 3000;
|
||||
|
||||
export function formatRequestLine(input: {
|
||||
method: string;
|
||||
url: string;
|
||||
status: number;
|
||||
ms: number;
|
||||
username?: string | null;
|
||||
}): string {
|
||||
const path = input.url.split('?')[0] ?? input.url;
|
||||
const slow = input.ms >= SLOW_REQUEST_MS ? ' LANGSAM' : '';
|
||||
return `${input.method} ${path} ${input.status} ${input.ms} ms user=${input.username ?? '-'}${slow}`;
|
||||
}
|
||||
|
||||
export function requestLogMiddleware(req: Request, res: Response, next: NextFunction): void {
|
||||
const url = req.originalUrl ?? req.url;
|
||||
if (url === '/health' || url.startsWith('/health?') || url.startsWith('/health/')) {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
const started = process.hrtime.bigint();
|
||||
res.on('finish', () => {
|
||||
const ms = Number((process.hrtime.bigint() - started) / 1_000_000n);
|
||||
const username = (req as Request & { user?: { username?: string } }).user?.username;
|
||||
const line = formatRequestLine({
|
||||
method: req.method,
|
||||
url,
|
||||
status: res.statusCode,
|
||||
ms,
|
||||
username,
|
||||
});
|
||||
if (res.statusCode >= 500) logger.error(line);
|
||||
else if (res.statusCode >= 400 || ms >= SLOW_REQUEST_MS) logger.warn(line);
|
||||
else logger.log(line);
|
||||
});
|
||||
next();
|
||||
}
|
||||
Reference in New Issue
Block a user