Работя като разработчик на пълен стек от 2015 г. и съм добре запознат с добрите техники за регистриране, колко полезни биха били те в случай на отстраняване на грешки или проверка на регистрационен файл и т.н...
Така че в тази публикация бих искал да споделя моите мисли за това какво всичко трябва да бъде включено в регистрационните файлове и как да се изгради един такъв регистратор, така че да бъде полезно и лесно да се отстраняват грешки в приложения в мащаб (микро-услуги).
Също така да спомена, че използвах няколко библиотеки като morgan (API регистратор), tracer (Дневници с произход на журнал), log4js (Много популярна, подобна на log4j) и още няколко. Но това, което липсваше във всички горепосочени библиотеки, беше, че нямаше начин да се разшири библиотеката, например: да се задейства предупреждение на Sentry в регистрационни файлове с ниво на грешка и т.н. Също така не можехме да се намесваме във формата на регистрационните файлове според нашето удобство. Затова реших да продължа напред и да създам такъв за собствената си цел. (Връзката е достъпна в края на този блог).

От моя гледна точка не е достатъчно само библиотеката за регистриране да ви позволява да пишете журнал с различно ниво на журнал към stdout, тя също трябва да предостави следните възможности:
1. Час на събитието
2. Произход на дневника
3. Идентичност на дневника
4. Кой/какво е инициирало събитието
5. Описание
6. Проследимост на събитието
7. Важност от дневника
8. Друга информация, ако има такава...

Класификация на компонентите на дневника:

Можем да класифицираме общо отговорността за регистриране на библиотеката на две части:
1. Информация, която ще бъде добавена от библиотеката
2. Информация, която трябва да бъде написана от разработчика

Няма познат метод (поне доколкото ми е известно) за налагане на програмист да пише регистрационни файлове в определен формат. Но библиотеката може да бъде конфигурирана да извежда съществена информация, свързана с всеки журнал.

Така че това, което можем да очакваме в края на този блог, е да изведем лог, подобен на снимката по-долу:

2019-03-31T17:39:33.544Z [DEBUG] <test.js:32:16> test...

Все още няма нищо специално за горния ред на журнал, защото почти всяка библиотека за регистриране предлага същия формат, но това, което е интересно за персонализираната библиотека, е в раздела по-долу

Регистриране на сървъра

Това е възрастта на микроуслугата, така че е много важно да свържете всеки регистрационен файл с request_id, който задейства действието. Ако това е друг език като java, python и т.н., няма да е проблем да го направите, но Nodejs е с една нишка и всеки процес се изпълнява асинхронно, така че не можем да поддържаме контекст спрямо заявка, като например request_it. Но благодарение на Nodejs Async Hooks и библиотеката async-local-storage, която създава много лесен интерфейс към Async Hooks.

Забележка: По-долу кодовите фрагменти ще бъдат написани за Koa (Nodejs framework), можете да приложите нещо подобно, за да отговаря на вашите изисквания.

Първо имаме нужда от междинен софтуер, който създава request_id:

export default function RequestId() {
  return async (ctx, next) => {
    ctx.state.id = uuid.v1();
    ctx.req.id = ctx.state.id;
    ctx.set('X-Request-Id', ctx.state.id);
    await next();
  };
}

Сега с помощта на библиотеката async-local-storage ще свържем този request_id през целия жизнен цикъл на заявката:

import als from 'async-local-storage';
als.enable();
export default function LogReqId() {
  return async (ctx, next) => {
    als.scope();
    als.set('reqId', ctx.state.id);
    await next();
  };
}

Уверете се, че сте поставили междинните софтуери в същия ред, както по-горе.
Сега е време да създадете свой собствен регистър или да напишете обвивка около любимата си библиотека за регистриране, за да включите request_id във всеки журнал.
По-долу съм използвайте tracer (един от любимите ми!) и създадох обвивка около него:

import tracer from 'tracer';
import als from 'async-local-storage';
const tlogger = tracer.colorConsole({
  level: 'debug',
  format: '{{timestamp}} <{{title}}> {{file}}:{{line}} {{message}}',
  stackIndex: 1
});
const getReqId = () => als.get('reqId') || '';
const logger = {
  trace(...args) {
    tlogger.trace(getReqId(), ...args);
  },
  debug(...args) {
    tlogger.debug(getReqId(), ...args);
  },
  info(...args) {
    tlogger.info(getReqId(), ...args);
  },
  warn(...args) {
    tlogger.warn(getReqId(), ...args);
  },
  error(...args) {
    tlogger.error(getReqId(), ...args);
  },
};
export default logger;

С настройката по-горе вашите регистрационни файлове ще изглеждат по-долу:

2019-03-31T09:33:52+0000 <debug> auth.js:36 18ab1370-5398-11e9-ac88-133bbfd057ff Authenticated!

Но това все още не е достатъчно, защото много често в производството се предлага да има система за уведомяване за грешки, така че програмистът да бъде уведомен, когато нещо лошо се случи на сървъра. Обикновено нещо Лошо ще бъде регистрирано с logLevel: грешка. Така че защо не свържем двете, като заменим функцията errorсъздадена по-горе с това:

error(...args) {
    tlogger.error(getReqId(), ...args);
    const err = args.find(e => e instanceof Error);
    err && Sentry.captureException(err)
},

Така че това ще гарантира, че всеки път, когато се регистрира регистър с ниво: грешка, сигналите на Sentry също се задействат паралелно.

За тези, които търсят допълнително предимство в персонализирането на вашата библиотека за регистриране, моля, проверете logler, който предоставя пълна гъвкавост по отношение на създаването на ваша собствена библиотека, която да отговаря на нуждите на всеки човек.

Така че, ако харесвате публикацията, моля, ръкопляскайте я така, че и други да могат да прочетат това. Благодаря ти :)