mirror of
https://github.com/directus/directus.git
synced 2026-04-25 03:00:53 -04:00
Add ability to ignore specific paths from the HTTP logger (#20368)
* add changeset * Add the ability to ignore specific paths from HTTP logger * fix linting * Cast env, clean-up, add test * Use dedicated env config name * Add docs * Clean-up --------- Co-authored-by: Pascal Jufer <pascal-jufer@bluewin.ch>
This commit is contained in:
5
.changeset/sixty-toys-nail.md
Normal file
5
.changeset/sixty-toys-nail.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@directus/api': minor
|
||||
---
|
||||
|
||||
Added the ability to ignore specific paths from HTTP logger
|
||||
@@ -26,6 +26,7 @@ const allowedEnvironmentVars = [
|
||||
'PUBLIC_URL',
|
||||
'LOG_LEVEL',
|
||||
'LOG_STYLE',
|
||||
'LOG_HTTP_IGNORE_PATHS',
|
||||
'MAX_PAYLOAD_SIZE',
|
||||
'ROOT_REDIRECT',
|
||||
'SERVE_APP',
|
||||
@@ -370,6 +371,8 @@ const typeMap: Record<string, string> = {
|
||||
MAX_BATCH_MUTATION: 'number',
|
||||
|
||||
SERVER_SHUTDOWN_TIMEOUT: 'number',
|
||||
|
||||
LOG_HTTP_IGNORE_PATHS: 'array',
|
||||
};
|
||||
|
||||
let env: Record<string, any> = {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { REDACTED_TEXT } from '@directus/utils';
|
||||
import http from 'node:http';
|
||||
import type { AddressInfo } from 'node:net';
|
||||
import { Writable } from 'node:stream';
|
||||
import { pino } from 'pino';
|
||||
import { pinoHttp, type HttpLogger } from 'pino-http';
|
||||
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
||||
import { doMockEnv } from './__utils__/mock-env.js';
|
||||
|
||||
@@ -15,7 +18,7 @@ const MOCK_ENV = {
|
||||
LOG_STYLE: 'raw',
|
||||
};
|
||||
|
||||
doMockEnv({ env: MOCK_ENV });
|
||||
const { setEnv } = doMockEnv({ env: MOCK_ENV });
|
||||
|
||||
const { httpLoggerOptions } = await import('./logger.js');
|
||||
|
||||
@@ -145,3 +148,58 @@ describe('res.headers', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('ignored paths', () => {
|
||||
afterEach(() => {
|
||||
vi.resetModules();
|
||||
});
|
||||
|
||||
const doRequest = (logger: HttpLogger) =>
|
||||
new Promise((resolve) => {
|
||||
const server = http.createServer((req, res) => {
|
||||
logger(req, res);
|
||||
res.end();
|
||||
});
|
||||
|
||||
server.listen(0, '127.0.0.1', () => {
|
||||
const address = server.address() as AddressInfo;
|
||||
const path = '/server/ping';
|
||||
|
||||
http.get('http://' + address.address + ':' + address.port + path, () => {
|
||||
server.close(resolve);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('should log request with no ignored paths specified', async () => {
|
||||
const { httpLoggerEnvConfig } = await import('./logger.js');
|
||||
|
||||
const logger = pinoHttp({
|
||||
logger: pino(httpLoggerOptions, stream),
|
||||
...httpLoggerEnvConfig,
|
||||
});
|
||||
|
||||
await doRequest(logger);
|
||||
|
||||
expect(logOutput.mock.calls[0][0]).toMatchObject({
|
||||
req: {
|
||||
url: '/server/ping',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('should not log request when it matches ignored path', async () => {
|
||||
setEnv({ LOG_HTTP_IGNORE_PATHS: '/server/ping' });
|
||||
|
||||
const { httpLoggerEnvConfig } = await import('./logger.js');
|
||||
|
||||
const logger = pinoHttp({
|
||||
logger: pino(httpLoggerOptions, stream),
|
||||
...httpLoggerEnvConfig,
|
||||
});
|
||||
|
||||
await doRequest(logger);
|
||||
|
||||
expect(logOutput).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { REDACTED_TEXT, toArray } from '@directus/utils';
|
||||
import type { Request, RequestHandler } from 'express';
|
||||
import { merge } from 'lodash-es';
|
||||
import type { LoggerOptions } from 'pino';
|
||||
import { pino } from 'pino';
|
||||
import { pinoHttp, stdSerializers } from 'pino-http';
|
||||
import { URL } from 'url';
|
||||
import { URL } from 'node:url';
|
||||
import { pino, type LoggerOptions } from 'pino';
|
||||
import { pinoHttp, stdSerializers, type AutoLoggingOptions } from 'pino-http';
|
||||
import env from './env.js';
|
||||
import { getConfigFromEnv } from './utils/get-config-from-env.js';
|
||||
|
||||
@@ -100,7 +99,19 @@ if (loggerEnvConfig['levels']) {
|
||||
|
||||
const logger = pino(merge(pinoOptions, loggerEnvConfig));
|
||||
|
||||
const httpLoggerEnvConfig = getConfigFromEnv('LOGGER_HTTP', ['LOGGER_HTTP_LOGGER']);
|
||||
export const httpLoggerEnvConfig = getConfigFromEnv('LOGGER_HTTP', ['LOGGER_HTTP_LOGGER']);
|
||||
|
||||
if (env['LOG_HTTP_IGNORE_PATHS']) {
|
||||
const ignorePathsSet = new Set(env['LOG_HTTP_IGNORE_PATHS']);
|
||||
|
||||
httpLoggerEnvConfig['autoLogging'] = {
|
||||
ignore: (req) => {
|
||||
if (!req.url) return false;
|
||||
const { pathname } = new URL(req.url, 'http://example.com/');
|
||||
return ignorePathsSet.has(pathname);
|
||||
},
|
||||
} as AutoLoggingOptions;
|
||||
}
|
||||
|
||||
export const expressLogger = pinoHttp({
|
||||
logger: pino(merge(httpLoggerOptions, loggerEnvConfig)),
|
||||
|
||||
@@ -235,6 +235,7 @@ prefixing the value with `{type}:`. The following types are available:
|
||||
| `PUBLIC_URL`<sup>[1]</sup> | URL where your API can be reached on the web. | `/` |
|
||||
| `LOG_LEVEL` | What level of detail to log. One of `fatal`, `error`, `warn`, `info`, `debug`, `trace` or `silent`. | `info` |
|
||||
| `LOG_STYLE` | Render the logs human readable (pretty) or as JSON. One of `pretty`, `raw`. | `pretty` |
|
||||
| `LOG_HTTP_IGNORE_PATHS` | List of HTTP request paths which should not appear in the log, for example `/server/ping`. | -- |
|
||||
| `MAX_PAYLOAD_SIZE` | Controls the maximum request body size. Accepts number of bytes, or human readable string. | `1mb` |
|
||||
| `ROOT_REDIRECT` | Redirect the root of the application `/` to a specific route. Accepts a relative path, absolute URL, or `false` to disable. | `./admin` |
|
||||
| `SERVE_APP` | Whether or not to serve the Admin application | `true` |
|
||||
|
||||
Reference in New Issue
Block a user