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:
daedalus
2023-11-16 15:31:42 -05:00
committed by GitHub
parent a011095aeb
commit fdb2e42b29
5 changed files with 84 additions and 6 deletions

View File

@@ -0,0 +1,5 @@
---
'@directus/api': minor
---
Added the ability to ignore specific paths from HTTP logger

View File

@@ -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> = {

View File

@@ -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();
});
});

View File

@@ -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)),

View File

@@ -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` |