【问题标题】:Implement custom logging in Loopback 4在 Loopback 4 中实现自定义日志记录
【发布时间】:2019-09-22 07:02:05
【问题描述】:

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

【问题讨论】:

    标签: node.js loopbackjs winston bunyan


    【解决方案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.');
    

    这应该让你开始。

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

    【讨论】:

    • 谢谢。这仅涵盖手动调用的行对吗?如果我想记录所有发生的事情,请求、控制台、stderr、stdout,该怎么办?
    【解决方案2】:

    让我有点困扰的是,如果我的一条路由抛出异常,则输出只能记录到 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 使用@loopback/rest/src/providers/reject.provider.ts 中定义的RejectProvider 来处理在 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...]
    

    【讨论】:

    • 这看起来正是我需要的解决方案,但是当我尝试在日志错误提供程序中为这一行运行它时出现错误:this.logger.logger.error( 响应是“对象”类型上不存在属性“错误”。我知道自从您发布此内容以来已经很长时间了,但如果您能提供帮助,将不胜感激。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-04-23
    • 1970-01-01
    • 1970-01-01
    • 2016-08-29
    • 2019-11-15
    • 2023-03-12
    • 1970-01-01
    相关资源
    最近更新 更多