directus

Форк
0
/
logger.ts 
173 строки · 4.1 Кб
1
import { useEnv } from '@directus/env';
2
import { REDACTED_TEXT, toArray } from '@directus/utils';
3
import type { Request, RequestHandler } from 'express';
4
import { merge } from 'lodash-es';
5
import { URL } from 'node:url';
6
import { pino, type Logger, type LoggerOptions } from 'pino';
7
import { pinoHttp, stdSerializers, type AutoLoggingOptions } from 'pino-http';
8
import { getConfigFromEnv } from './utils/get-config-from-env.js';
9

10
export const _cache: {
11
	logger: Logger<never> | undefined;
12
} = { logger: undefined };
13

14
export const useLogger = () => {
15
	if (_cache.logger) {
16
		return _cache.logger;
17
	}
18

19
	_cache.logger = createLogger();
20

21
	return _cache.logger;
22
};
23

24
export const createLogger = () => {
25
	const env = useEnv();
26

27
	const pinoOptions: LoggerOptions = {
28
		level: (env['LOG_LEVEL'] as string) || 'info',
29
		redact: {
30
			paths: ['req.headers.authorization', 'req.headers.cookie'],
31
			censor: REDACTED_TEXT,
32
		},
33
	};
34

35
	if (env['LOG_STYLE'] !== 'raw') {
36
		pinoOptions.transport = {
37
			target: 'pino-pretty',
38
			options: {
39
				ignore: 'hostname,pid',
40
				sync: true,
41
			},
42
		};
43
	}
44

45
	const loggerEnvConfig = getConfigFromEnv('LOGGER_', 'LOGGER_HTTP');
46

47
	// Expose custom log levels into formatter function
48
	if (loggerEnvConfig['levels']) {
49
		const customLogLevels: { [key: string]: string } = {};
50

51
		for (const el of toArray(loggerEnvConfig['levels'])) {
52
			const key_val = el.split(':');
53
			customLogLevels[key_val[0].trim()] = key_val[1].trim();
54
		}
55

56
		pinoOptions.formatters = {
57
			level(label: string, number: any) {
58
				return {
59
					severity: customLogLevels[label] || 'info',
60
					level: number,
61
				};
62
			},
63
		};
64

65
		delete loggerEnvConfig['levels'];
66
	}
67

68
	return pino(merge(pinoOptions, loggerEnvConfig));
69
};
70

71
export const createExpressLogger = () => {
72
	const env = useEnv();
73

74
	const httpLoggerEnvConfig = getConfigFromEnv('LOGGER_HTTP', ['LOGGER_HTTP_LOGGER']);
75
	const loggerEnvConfig = getConfigFromEnv('LOGGER_', 'LOGGER_HTTP');
76

77
	const httpLoggerOptions: LoggerOptions = {
78
		level: (env['LOG_LEVEL'] as string) || 'info',
79
		redact: {
80
			paths: ['req.headers.authorization', 'req.headers.cookie'],
81
			censor: REDACTED_TEXT,
82
		},
83
	};
84

85
	if (env['LOG_STYLE'] !== 'raw') {
86
		httpLoggerOptions.transport = {
87
			target: 'pino-http-print',
88
			options: {
89
				all: true,
90
				translateTime: 'SYS:HH:MM:ss',
91
				relativeUrl: true,
92
				prettyOptions: {
93
					ignore: 'hostname,pid',
94
					sync: true,
95
				},
96
			},
97
		};
98
	}
99

100
	if (env['LOG_STYLE'] === 'raw') {
101
		httpLoggerOptions.redact = {
102
			paths: ['req.headers.authorization', 'req.headers.cookie', 'res.headers'],
103
			censor: (value, pathParts) => {
104
				const path = pathParts.join('.');
105

106
				if (path === 'res.headers') {
107
					if ('set-cookie' in value) {
108
						value['set-cookie'] = REDACTED_TEXT;
109
					}
110

111
					return value;
112
				}
113

114
				return REDACTED_TEXT;
115
			},
116
		};
117
	}
118

119
	// Expose custom log levels into formatter function
120
	if (loggerEnvConfig['levels']) {
121
		const customLogLevels: { [key: string]: string } = {};
122

123
		for (const el of toArray(loggerEnvConfig['levels'])) {
124
			const key_val = el.split(':');
125
			customLogLevels[key_val[0].trim()] = key_val[1].trim();
126
		}
127

128
		httpLoggerOptions.formatters = {
129
			level(label: string, number: any) {
130
				return {
131
					severity: customLogLevels[label] || 'info',
132
					level: number,
133
				};
134
			},
135
		};
136

137
		delete loggerEnvConfig['levels'];
138
	}
139

140
	if (env['LOG_HTTP_IGNORE_PATHS']) {
141
		const ignorePathsSet = new Set(env['LOG_HTTP_IGNORE_PATHS'] as string);
142

143
		httpLoggerEnvConfig['autoLogging'] = {
144
			ignore: (req) => {
145
				if (!req.url) return false;
146
				const { pathname } = new URL(req.url, 'http://example.com/');
147
				return ignorePathsSet.has(pathname);
148
			},
149
		} as AutoLoggingOptions;
150
	}
151

152
	return pinoHttp({
153
		logger: pino(merge(httpLoggerOptions, loggerEnvConfig)),
154
		...httpLoggerEnvConfig,
155
		serializers: {
156
			req(request: Request) {
157
				const output = stdSerializers.req(request);
158
				output.url = redactQuery(output.url);
159
				return output;
160
			},
161
		},
162
	}) as RequestHandler;
163
};
164

165
function redactQuery(originalPath: string) {
166
	const url = new URL(originalPath, 'http://example.com/');
167

168
	if (url.searchParams.has('access_token')) {
169
		url.searchParams.set('access_token', REDACTED_TEXT);
170
	}
171

172
	return url.pathname + url.search;
173
}
174

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.