2

可以使用 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

布鲁扬 - https://www.npmjs.com/package/loopback-component-bunyan

4

2 回答 2

2

可以实现自定义登录,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 的 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.js在根文件夹中,import看起来像:

// index.js

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

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

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

这应该让你开始。

PS我确信答案(和方法)可以改进,因此非常愿意接受任何有用的建议。

于 2019-05-06T22:09:19.527 回答
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(一个 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...]
于 2020-01-05T23:43:03.363 回答