让我有点困扰的是,如果我的一条路由抛出异常,则输出只能记录到 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
定义的 in@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...]