Реализовать пользовательское ведение журнала в Loopback 4

Можно ли использовать Winston, Pino или Bunyan для входа в Loopback4? Если да, то каковы основные шаги для их реализации в Loopback4?

Мне удалось заставить Winston работать с помощью Express во время просмотра этого руководства: https://www.digitalocean.com/community/tutorials/how-to-use-winston-to-log-node-js-applications

Есть модули Loopback для Winston и Brunyan. Однако у меня сложилось впечатление (поскольку последние обновления старше 10 месяцев), они должны быть для более старых версий Loopback (поскольку v4 вышла 18 октября)?

Уинстон — https://www.npmjs.com/package/loopback-component-winston< /а>

Брунян — https://www.npmjs.com/package/loopback-component-bunyan< /а>


person TekiusFanatikus    schedule 03.05.2019    source источник


Ответы (2)


В Loopback 4 можно реализовать пользовательское ведение журнала, и это не должно сильно отличаться от Express. Я экспериментировал с winston и, следовательно, хотел бы детализировать то же самое, но это должно быть достижимо и с использованием bunyan.

Для начала вы можете создать папку utils в корне вашего проекта, чтобы сохранить свой пользовательский регистратор. Приложение, сформированное с помощью интерфейса командной строки LB4, имеет типичную структуру и с папкой 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.');

Это должно помочь вам начать.

P.S. Я уверен, что ответ (и подход) можно улучшить и, следовательно, очень открыт для любых полезных предложений.

person mrkm    schedule 06.05.2019
comment
Спасибо. Это касается только строк, вызываемых вручную, верно? Что, если бы я хотел записывать в файл все, что происходит, запросы, консоль, стандартный вывод, стандартный вывод? - person TekiusFanatikus; 07.05.2019

Меня немного беспокоило то, что если один из моих маршрутов вызывал исключение, вывод мог быть зарегистрирован только в 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...]
person sigalor    schedule 05.01.2020
comment
Похоже, это именно то решение, которое мне нужно, но я получаю сообщение об ошибке при попытке запустить его для этой строки в поставщике ошибок журнала: this.logger.logger.error (ответ: свойство «ошибка» не существует на типе «объект». Я знаю, что прошла целая вечность с тех пор, как вы опубликовали это, но был бы очень признателен, если бы вы могли помочь. - person llhilton; 23.10.2020