在环回4中实现自定义日志记录

时间:2019-05-03 17:40:36

标签: node.js loopbackjs winston bunyan

是否可以使用Winston,Pino或Bunyan登录Loopback4?如果是这样,在Loopback4中实现它们的基本步骤是什么?

在查看本教程时,我能够使用Express使Winston正常工作: https://www.digitalocean.com/community/tutorials/how-to-use-winston-to-log-node-js-applications

有适用于Winston和Brunyan的Loopback模块。但是,我的印象是(因为最近的更新已经超过10个月了)它们一定适用于旧版本的Loopback(因为v4于10月18日发布)?

温斯顿-https://www.npmjs.com/package/loopback-component-winston

Brunyan-https://www.npmjs.com/package/loopback-component-bunyan

2 个答案:

答案 0 :(得分:1)

可以在Loopback 4中实现自定义日志记录,这样做应该与Express没什么不同。 我已经对winston进行了实验,因此,将对其进行详细说明,但这也可以使用bunyan来实现。

首先,您可以在项目的根目录中创建一个utils文件夹,以保留您的自定义记录器。使用LB4 CLI搭建支架的应用采用典型结构,并带有utils文件夹,如下所示:

.
|
|-- public
|-- src  
|-- utils
|   |-- logger
|       |-- index.js  <-- custom logger can be defined here.
|-- node_modules
|-- index.js
|--
.

我正在使用示例 如winston's github repo中所述,用于定义记录器:

// utils/logger/index.js

const winston = require('winston');

const logger = winston.createLogger({
    level: 'info',
    format: winston.format.json(),
    defaultMeta: { service: 'user-service' },
    transports: [
        //
        // - Write to all logs with level `info` and below to `combined.log` 
        // - Write all logs error (and below) to `error.log`.
        //
        new winston.transports.File({ filename: 'error.log', level: 'error' }),
        new winston.transports.File({ filename: 'combined.log' })
    ]
});

module.exports = logger;

您现在可以通过在整个应用程序中“导入”记录器来开始使用它。对于根文件夹中的index.jsimport如下所示:

// index.js

const logger = require('./utils/logger');

对于先前定义的记录器,以下语句会将I am logged.记录到名为combined.log的文件中:

logger.info('I am logged.');

这应该让您入门。

P.S。我确信答案(和方法)可以改善,因此非常欢迎任何有用的建议。

答案 1 :(得分:0)

令我有些困扰的是,如果我的一条路线引发异常,则输出只能记录到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 (环回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绑定我们的自定义提供程序。在内部,回送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...]