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

问题描述 投票:0回答:3

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

node.js loopbackjs winston bunyan
3个回答
4
投票

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

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 存储库中所述,用于定义记录器:

// 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.');

这应该可以帮助您开始。

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


0
投票

我遵循了 @sigalor 的回答,但有一个变化是我使用内置的 Loopback 日志库来执行此操作。我在

logger.service.ts
中有一个 WinstonLogger 的实例,而不是
application.ts
,它是我按照 本指南 创建的:

import {LogErrorProvider} from './providers/log-error.provider';
import {format} from 'winston';
import {
  LoggingBindings,
  LoggingComponent,
  WINSTON_TRANSPORT,
  WinstonTransports,
} from '@loopback/logging';

export class VibratoRestV2Application extends BootMixin(
  ServiceMixin(RepositoryMixin(RestApplication)),
) {
    //...
    // Configure logging
    this.configure(LoggingBindings.COMPONENT).to({
      enableFluent: false, // default to true
      enableHttpAccessLog: false, // default to true
    });
    this.configure(LoggingBindings.WINSTON_LOGGER).to({
      level: logConfig.logLevel ?? 'info',
      format: format.combine(format.timestamp(), format.json()),
      defaultMeta: {framework: 'Loopback'},
    });
    const fileTransport = new WinstonTransports.File({
      level: logConfig.logLevel ?? 'info',
      lazy: false,
      filename: logConfig.location ?? 'file.log',
      maxsize: 1000000, // 1MB
      maxFiles: 5,
      tailable: true,
      zippedArchive: true,
    });
    this.bind('logger.winston.transports.file')
      .to(fileTransport)
      .apply(extensionFor(WINSTON_TRANSPORT));
    this.component(LoggingComponent);

    // Log errors through provider
    this.bind(RestBindings.SequenceActions.LOG_ERROR) .toProvider(
      LogErrorProvider,
    );
}

然后,您所需要的就是配置他的

log-error.provider.ts
,这就是真正的魔法发生的地方:

import {Provider} from '@loopback/context';
import {inject} from '@loopback/core';
import {LoggingBindings, WinstonLogger} from '@loopback/logging';
import {LogError, Request} from '@loopback/rest';
import {createLogger, Logger} from 'winston';

export class LogErrorProvider implements Provider<LogError> {
  constructor(
    @inject(LoggingBindings.WINSTON_LOGGER) protected logger: WinstonLogger,
  ) {createLogger()}

  value(): LogError {
    return (err, statusCode, req) => this.action(err, statusCode, req);
  }

  action(err: Error, statusCode: number, req: Request) {
    if (statusCode < 500) {
      return;
    }

    this.logger.error(
      `HTTP ${statusCode} on ${req.method} ${req.url}: ${err.stack ?? err}`,
    );
  }
}

然后您可以获得错误日志记录和自定义日志记录。您还可以使用 Loopback4 中日志库附带的

@logInvocation
装饰器,但这只会在应用程序日志级别为
DEBUG
或更高时打印日志 - 文档在这里:

import {logInvocation} from '@loopback/logging';

@logInvocation()
export class PingController {
    //...
}
© www.soinside.com 2019 - 2024. All rights reserved.