Меня немного беспокоило то, что если один из моих маршрутов вызывал исключение, вывод мог быть зарегистрирован только в stderr. Поэтому я сделал следующее, чтобы исправить это, и вместо этого использовал Winston для ведения журнала, оставаясь при этом полностью независимым от базовой системы ведения журнала, которая фактически используется.
Предположим, что в одном из моих контроллеров есть следующая конечная точка REST:
@post('/myendpoint')
async handleEndpoint(): Promise<void> {
throw new Error('I am an error!');
}
Теперь, чтобы добавить настраиваемый регистратор, я создал для него новую службу и привязал ее вариант Winston к моему приложению.
src/services/logger.service.ts (абстрактная служба ведения журнала и ее конкретная реализация, использующая Winston)
import winston from 'winston';
export interface LoggerService {
logger: object;
}
export class WinstonLoggerService implements LoggerService {
logger: winston.Logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json(),
),
transports: [
new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(),
winston.format.printf(info => {
return `[${info.timestamp}] ${info.level}: ${info.message}`;
}),
),
}),
],
});
}
src/keys.ts
export namespace LoggerBindings {
export const LOGGER = BindingKey.create<LoggerService>('services.logger');
}
src/providers/log-error.provider.ts (класс провайдера Loopback 4, в который вводится класс регистратора, связанный с приложением, и который затем может его использовать)
import {Provider} from '@loopback/context';
import {LogError, Request} from '@loopback/rest';
import {inject} from '@loopback/core';
import {LoggerBindings} from '../keys';
import {LoggerService} from '../services/logger.service';
export class LogErrorProvider implements Provider<LogError> {
constructor(@inject(LoggerBindings.LOGGER) protected logger: LoggerService) {}
value(): LogError {
return (err, statusCode, req) => this.action(err, statusCode, req);
}
action(err: Error, statusCode: number, req: Request) {
if (statusCode < 500) {
return;
}
this.logger.logger.error(
`HTTP ${statusCode} on ${req.method} ${req.url}: ${err.stack ?? err}`,
);
}
}
src/application.ts (операторы привязки входят в конструктор)
import {WinstonLoggerService} from './services/logger.service';
import {LogErrorProvider} from './providers/log-error.provider';
this.bind(LoggerBindings.LOGGER).toClass(WinstonLoggerService);
this.bind(RestBindings.SequenceActions.LOG_ERROR).toProvider(LogErrorProvider);
Последняя строка в предыдущем блоке кода является здесь ключевой, поскольку она обеспечивает привязку нашего пользовательского провайдера для LOG_ERROR
. Внутри Loopback 4 использует RejectProvider
, определенный в @loopback/rest/src/providers/reject.provider.ts
, для обработки ошибок, возникающих в конечных точках REST. В этот провайдер вводится RestBindings.SequenceActions.LOG_ERROR
, который по умолчанию берется из @loopback/rest/src/providers/log-error.provider.ts
и который мы здесь переопределяем. Таким образом, нам не нужно переписывать весь провайдер отклонения, а только его крошечную часть, которая обрабатывает регистрацию ошибок REST.
При вызове примера маршрута в консоли отображается следующее:
[2020-01-05T23:41:28.604Z] error: HTTP 500 on POST /myendpoint: Error: I am an error!
at [...long stacktrace...]
person
sigalor
schedule
05.01.2020