@gyramais/log-transport 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +90 -0
- package/dist/console-redirect.d.ts +5 -0
- package/dist/console-redirect.d.ts.map +1 -0
- package/dist/console-redirect.js +37 -0
- package/dist/console-redirect.js.map +1 -0
- package/dist/fatal-handlers.d.ts +21 -0
- package/dist/fatal-handlers.d.ts.map +1 -0
- package/dist/fatal-handlers.js +185 -0
- package/dist/fatal-handlers.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +36 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +176 -0
- package/dist/logger.js.map +1 -0
- package/dist/options.d.ts +32 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +25 -0
- package/dist/options.js.map +1 -0
- package/package.json +35 -0
- package/src/console-redirect.ts +50 -0
- package/src/fatal-handlers.ts +232 -0
- package/src/index.ts +34 -0
- package/src/logger.ts +255 -0
- package/src/options.ts +153 -0
package/README.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# @gyramais/log-transport
|
|
2
|
+
|
|
3
|
+
Como o log **chega ao destino**: monta os transports (Console, File, Loki) a
|
|
4
|
+
partir de configuração explícita, e registra exceção fatal com flush antes do
|
|
5
|
+
exit.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import {
|
|
9
|
+
buildNestLogger,
|
|
10
|
+
redirectConsoleToNestLogger,
|
|
11
|
+
registerFatalHandlers,
|
|
12
|
+
} from '@gyramais/log-transport';
|
|
13
|
+
|
|
14
|
+
const { logger, lokiTransport } = buildNestLogger({
|
|
15
|
+
service: 'gyra-core', // obrigatório
|
|
16
|
+
level: 'debug',
|
|
17
|
+
pretty: false,
|
|
18
|
+
loki: { host: 'http://loki:3100', basicAuth: '...' },
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
registerFatalHandlers(logger, lokiTransport);
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Serviço com mais de um logger
|
|
25
|
+
|
|
26
|
+
Se o serviço monta **vários loggers no mesmo processo**, crie o transport uma vez e
|
|
27
|
+
compartilhe. O worker do gyra-core monta quatro — um para a app e três para
|
|
28
|
+
conexões RMQ (`src/main.worker.ts`):
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import { createLokiTransport, buildNestLogger } from '@gyramais/log-transport';
|
|
32
|
+
|
|
33
|
+
// UM transport para o processo inteiro
|
|
34
|
+
const lokiTransport = createLokiTransport({
|
|
35
|
+
service: 'gyra-core',
|
|
36
|
+
host: process.env.LOKI_URL,
|
|
37
|
+
basicAuth: process.env.LOKI_BASIC_AUTH,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const { logger } = buildNestLogger({ service: 'gyra-core', lokiTransport });
|
|
41
|
+
|
|
42
|
+
// e o mesmo transport nos outros loggers
|
|
43
|
+
const rmqLogger = buildNestLogger({ service: 'gyra-core', lokiTransport }).logger;
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Não é conveniência, é correção: com um transport por logger há **um batcher por
|
|
47
|
+
logger**, e o `registerFatalHandlers` drena apenas o que recebeu — as linhas
|
|
48
|
+
bufferizadas nos outros morrem com o processo, que é o sintoma que a entrega
|
|
49
|
+
confiável existe para impedir.
|
|
50
|
+
|
|
51
|
+
Passar `loki` e `lokiTransport` juntos lança erro: um pede transport novo, o outro
|
|
52
|
+
pede reuso, e escolher em silêncio deixaria o consumidor com dois batchers sem
|
|
53
|
+
saber.
|
|
54
|
+
|
|
55
|
+
**`registerFatalHandlers` é chamado UMA vez por processo**, com o transport
|
|
56
|
+
compartilhado — não um por logger. Handler de `uncaughtException` é recurso único
|
|
57
|
+
do processo: registros duplicados produziriam linhas `[FATAL]` repetidas e flushes
|
|
58
|
+
concorrentes no mesmo transport. O pacote ignora a chamada extra e emite
|
|
59
|
+
`[FATAL-HANDLERS-DUP]` no stdout, mas o certo é chamar uma vez. Ele devolve uma
|
|
60
|
+
função de `unregister`, útil em teste.
|
|
61
|
+
|
|
62
|
+
## O pacote não lê `process.env`
|
|
63
|
+
|
|
64
|
+
Toda configuração entra por parâmetro. Três razões, em ordem de peso:
|
|
65
|
+
|
|
66
|
+
1. **Correção.** Os serviços não leem os mesmos envs: `gyra-export` lê `APP_NAME`,
|
|
67
|
+
e `gyra-file` e `gyra-websocket-server` não leem `ENVIRONMENT` nem `LOG_LEVEL`.
|
|
68
|
+
Um helper de env compartilhado mudaria o comportamento desses em silêncio.
|
|
69
|
+
2. **Testabilidade.** Ler env obriga o teste a mutar estado global do processo, e
|
|
70
|
+
a suíte passa a depender da ordem de execução.
|
|
71
|
+
3. **Contrato visível.** A assinatura diz o que o pacote precisa.
|
|
72
|
+
|
|
73
|
+
Quem lê env é o serviço, no entrypoint. Há um teste que compila o pacote com
|
|
74
|
+
`removeComments` e falha se `process.env` aparecer no código emitido.
|
|
75
|
+
|
|
76
|
+
## Detalhes que não são arbitrários
|
|
77
|
+
|
|
78
|
+
- **`service` é obrigatório no tipo.** O `gyra-nest-boilerplate` rotula hoje
|
|
79
|
+
`service: 'gyra-credit-policy'` por cópia de arquivo, e todo serviço novo nasce
|
|
80
|
+
logando sob o rótulo errado. Agora isso é erro de compilação.
|
|
81
|
+
- **A presença de `loki` é o gate de envio**, no lugar de um booleano em string.
|
|
82
|
+
- **`app` tem default `saas`**: as consultas e dashboards em produção usam
|
|
83
|
+
`{app="saas", service="gyra-x"}`.
|
|
84
|
+
- **`registerFatalHandlers` é opt-in.** Instalar handler de `uncaughtException` é
|
|
85
|
+
efeito colateral no ciclo de vida do processo — importar um pacote não deve
|
|
86
|
+
mudar como o processo morre.
|
|
87
|
+
- **`winston-loki: ^6.1.6`** é dependência daqui. São dois bugs corrigidos lá:
|
|
88
|
+
metadado estruturado não-string fazia o Loki responder 400 e descartar o lote
|
|
89
|
+
inteiro, e a resposta de erro era tratada como sucesso, tornando a perda 100%
|
|
90
|
+
silenciosa. O consumidor recebe a correção transitivamente.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"console-redirect.d.ts","sourceRoot":"","sources":["../src/console-redirect.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,sBAAsB;IAOrC,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,eAAO,MAAM,2BAA2B,GACtC,UAAS,sBAA2B,KACnC,IAwBF,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.redirectConsoleToNestLogger = void 0;
|
|
5
|
+
/*
|
|
6
|
+
Redireciona `console.*` para o Logger do Nest, para que biblioteca de terceiro (e
|
|
7
|
+
código legado) que escreve em `console` também chegue aos transports.
|
|
8
|
+
|
|
9
|
+
Saiu do `buildNestLogger` de propósito: trocar os métodos de `console` é efeito
|
|
10
|
+
colateral global do processo, e importar um pacote não deve mudar o que
|
|
11
|
+
`console.log` faz no serviço inteiro. Quem quer, chama.
|
|
12
|
+
*/
|
|
13
|
+
const common_1 = require("@nestjs/common");
|
|
14
|
+
const redirectConsoleToNestLogger = (options = {}) => {
|
|
15
|
+
console.log = (message, ...optionalParams) => {
|
|
16
|
+
if (options.pretty) {
|
|
17
|
+
common_1.Logger.log(message, ...optionalParams);
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
common_1.Logger.debug(message, ...optionalParams);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
console.error = (message, data, ...optionalParams) => {
|
|
24
|
+
common_1.Logger.error(message, { data }, ...optionalParams);
|
|
25
|
+
};
|
|
26
|
+
console.warn = (message, ...optionalParams) => {
|
|
27
|
+
common_1.Logger.warn(message, ...optionalParams);
|
|
28
|
+
};
|
|
29
|
+
console.debug = (message, ...optionalParams) => {
|
|
30
|
+
common_1.Logger.debug(message, ...optionalParams);
|
|
31
|
+
};
|
|
32
|
+
console.info = (message, ...optionalParams) => {
|
|
33
|
+
common_1.Logger.verbose(message, ...optionalParams);
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
exports.redirectConsoleToNestLogger = redirectConsoleToNestLogger;
|
|
37
|
+
//# sourceMappingURL=console-redirect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"console-redirect.js","sourceRoot":"","sources":["../src/console-redirect.ts"],"names":[],"mappings":";AAAA,uDAAuD;;;AAEvD;;;;;;;EAOE;AAEF,2CAAwC;AAYjC,MAAM,2BAA2B,GAAG,CACzC,UAAkC,EAAE,EAC9B,EAAE;IACR,OAAO,CAAC,GAAG,GAAG,CAAC,OAAa,EAAE,GAAG,cAAqB,EAAE,EAAE;QACxD,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACnB,eAAM,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,cAAc,CAAC,CAAC;QACzC,CAAC;aAAM,CAAC;YACN,eAAM,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,cAAc,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,CAAC,KAAK,GAAG,CAAC,OAAa,EAAE,IAAU,EAAE,GAAG,cAAqB,EAAE,EAAE;QACtE,eAAM,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,EAAE,GAAG,cAAc,CAAC,CAAC;IACrD,CAAC,CAAC;IAEF,OAAO,CAAC,IAAI,GAAG,CAAC,OAAa,EAAE,GAAG,cAAqB,EAAE,EAAE;QACzD,eAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,cAAc,CAAC,CAAC;IAC1C,CAAC,CAAC;IAEF,OAAO,CAAC,KAAK,GAAG,CAAC,OAAa,EAAE,GAAG,cAAqB,EAAE,EAAE;QAC1D,eAAM,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,cAAc,CAAC,CAAC;IAC3C,CAAC,CAAC;IAEF,OAAO,CAAC,IAAI,GAAG,CAAC,OAAa,EAAE,GAAG,cAAqB,EAAE,EAAE;QACzD,eAAM,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,cAAc,CAAC,CAAC;IAC7C,CAAC,CAAC;AACJ,CAAC,CAAC;AA1BW,QAAA,2BAA2B,+BA0BtC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { LoggerService } from '@nestjs/common';
|
|
2
|
+
import LokiTransport = require('winston-loki');
|
|
3
|
+
interface FatalHandlersOptions {
|
|
4
|
+
flushTimeoutMs?: number;
|
|
5
|
+
exit?: (code: number) => void;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Entrega ao Loki o que está bufferizado e só então devolve o controle,
|
|
9
|
+
* desistindo depois de `timeoutMs`. Nunca lança: no caminho de saída do
|
|
10
|
+
* processo, uma falha de flush não pode virar um segundo erro.
|
|
11
|
+
*
|
|
12
|
+
* O `flush()` do winston-loki apenas *aguarda* o laço periódico de envio, que
|
|
13
|
+
* pode estar a um intervalo inteiro de distância; quem está encerrando precisa
|
|
14
|
+
* *forçar* o envio agora. `close()` faz exatamente isso — interrompe a espera do
|
|
15
|
+
* laço e manda o batch pendente — e o `flush()` seguinte resolve quando esse
|
|
16
|
+
* envio termina.
|
|
17
|
+
*/
|
|
18
|
+
declare const flushLokiTransport: (transport: LokiTransport | undefined, timeoutMs?: number) => Promise<void>;
|
|
19
|
+
declare const registerFatalHandlers: (logger: LoggerService, transport: LokiTransport | undefined, options?: FatalHandlersOptions) => (() => void);
|
|
20
|
+
export { registerFatalHandlers, flushLokiTransport };
|
|
21
|
+
//# sourceMappingURL=fatal-handlers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fatal-handlers.d.ts","sourceRoot":"","sources":["../src/fatal-handlers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,aAAa,GAAG,QAAQ,cAAc,CAAC,CAAC;AAU/C,UAAU,oBAAoB;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/B;AAwBD;;;;;;;;;;GAUG;AACH,QAAA,MAAM,kBAAkB,GACtB,WAAW,aAAa,GAAG,SAAS,EACpC,kBAA4B,KAC3B,OAAO,CAAC,IAAI,CA6Bd,CAAC;AAmDF,QAAA,MAAM,qBAAqB,GACzB,QAAQ,aAAa,EACrB,WAAW,aAAa,GAAG,SAAS,EACpC,UAAS,oBAAyB,KACjC,CAAC,MAAM,IAAI,CA6Fb,CAAC;AAEF,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,CAAC"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.flushLokiTransport = exports.registerFatalHandlers = void 0;
|
|
4
|
+
/*
|
|
5
|
+
O flush no caminho do crash é limitado no tempo de propósito: um Loki
|
|
6
|
+
indisponível não pode impedir o pod de morrer e reiniciar.
|
|
7
|
+
*/
|
|
8
|
+
const FLUSH_TIMEOUT_MS = 2000;
|
|
9
|
+
/*
|
|
10
|
+
Winston entrega a linha ao transport por stream, num tick posterior ao
|
|
11
|
+
`logger.error()`. Sem esperar por essa entrega, o batch ainda está vazio quando
|
|
12
|
+
o flush é pedido — e aí o flush resolve na hora e o processo sai levando a linha
|
|
13
|
+
embora. O transport emite `logged` quando recebe.
|
|
14
|
+
*/
|
|
15
|
+
const LOGGED_HANDOFF_TIMEOUT_MS = 100;
|
|
16
|
+
const waitForHandoff = (transport, timeoutMs) => new Promise((resolve) => {
|
|
17
|
+
const done = () => {
|
|
18
|
+
clearTimeout(timer);
|
|
19
|
+
transport.off?.('logged', done);
|
|
20
|
+
resolve();
|
|
21
|
+
};
|
|
22
|
+
const timer = setTimeout(done, timeoutMs);
|
|
23
|
+
transport.once?.('logged', done);
|
|
24
|
+
});
|
|
25
|
+
/**
|
|
26
|
+
* Entrega ao Loki o que está bufferizado e só então devolve o controle,
|
|
27
|
+
* desistindo depois de `timeoutMs`. Nunca lança: no caminho de saída do
|
|
28
|
+
* processo, uma falha de flush não pode virar um segundo erro.
|
|
29
|
+
*
|
|
30
|
+
* O `flush()` do winston-loki apenas *aguarda* o laço periódico de envio, que
|
|
31
|
+
* pode estar a um intervalo inteiro de distância; quem está encerrando precisa
|
|
32
|
+
* *forçar* o envio agora. `close()` faz exatamente isso — interrompe a espera do
|
|
33
|
+
* laço e manda o batch pendente — e o `flush()` seguinte resolve quando esse
|
|
34
|
+
* envio termina.
|
|
35
|
+
*/
|
|
36
|
+
const flushLokiTransport = async (transport, timeoutMs = FLUSH_TIMEOUT_MS) => {
|
|
37
|
+
if (!transport?.flush)
|
|
38
|
+
return;
|
|
39
|
+
let timer;
|
|
40
|
+
const drain = async () => {
|
|
41
|
+
await waitForHandoff(transport, Math.min(LOGGED_HANDOFF_TIMEOUT_MS, timeoutMs));
|
|
42
|
+
transport.close?.();
|
|
43
|
+
await transport.flush();
|
|
44
|
+
};
|
|
45
|
+
try {
|
|
46
|
+
await Promise.race([
|
|
47
|
+
drain(),
|
|
48
|
+
new Promise((resolve) => {
|
|
49
|
+
timer = setTimeout(resolve, timeoutMs);
|
|
50
|
+
}),
|
|
51
|
+
]);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
/*
|
|
55
|
+
Silencioso por escolha: quem chama já está encerrando o processo e a linha
|
|
56
|
+
de erro do flush não teria para onde ir.
|
|
57
|
+
*/
|
|
58
|
+
}
|
|
59
|
+
finally {
|
|
60
|
+
if (timer)
|
|
61
|
+
clearTimeout(timer);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
exports.flushLokiTransport = flushLokiTransport;
|
|
65
|
+
const describeReason = (reason) => {
|
|
66
|
+
if (reason instanceof Error) {
|
|
67
|
+
return {
|
|
68
|
+
message: `${reason.name}: ${reason.message}`,
|
|
69
|
+
stack: reason.stack ?? '',
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
return { message: String(reason), stack: '' };
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Registra os handlers de exceção fatal do processo.
|
|
76
|
+
*
|
|
77
|
+
* Sem eles, o handler padrão do Node imprime a stack direto no stderr — sem
|
|
78
|
+
* passar por nenhum transport do winston — e o crash fica invisível na
|
|
79
|
+
* observabilidade, encontrável apenas por `kubectl logs --previous`.
|
|
80
|
+
*
|
|
81
|
+
* O processo continua morrendo: depois de uma exceção não capturada o estado é
|
|
82
|
+
* indefinido, e reiniciar é o comportamento correto. O que muda é o crash passar
|
|
83
|
+
* a ficar registrado.
|
|
84
|
+
*/
|
|
85
|
+
/*
|
|
86
|
+
Handler de exceção fatal é recurso ÚNICO do processo, e o guard de reentrância
|
|
87
|
+
vive no closure de cada chamada — então dois registros são dois guards, e um
|
|
88
|
+
único erro fatal dispara os dois. Medido: quatro registros produziram quatro
|
|
89
|
+
linhas `[FATAL]`, quatro flushes concorrentes no mesmo transport e quatro
|
|
90
|
+
chamadas de exit.
|
|
91
|
+
|
|
92
|
+
O risco cresceu quando o pacote passou a suportar vários loggers com um
|
|
93
|
+
transport compartilhado (ver `createLokiTransport`): fica natural registrar um
|
|
94
|
+
handler por logger.
|
|
95
|
+
|
|
96
|
+
Este é o único estado global do pacote, e a distinção importa: ele descreve o
|
|
97
|
+
PROCESSO — se os handlers já estão instalados nele —, não configuração. Config
|
|
98
|
+
em variável global é o que `options.ts` recusa; isto é outra coisa.
|
|
99
|
+
|
|
100
|
+
`Symbol.for` e não um `let` de módulo: sobrevive a duas instâncias do pacote na
|
|
101
|
+
árvore de dependências, que é exatamente o caso em que o registro duplicado
|
|
102
|
+
aconteceria sem ninguém perceber.
|
|
103
|
+
*/
|
|
104
|
+
const REGISTERED = Symbol.for('@gyramais/log-transport.fatalHandlersRegistered');
|
|
105
|
+
const registerFatalHandlers = (logger, transport, options = {}) => {
|
|
106
|
+
const target = globalThis;
|
|
107
|
+
if (target[REGISTERED]) {
|
|
108
|
+
/*
|
|
109
|
+
Avisa em vez de ignorar em silêncio: registro duplicado é erro de
|
|
110
|
+
integração do consumidor, e descobrir isso durante um crash em produção é
|
|
111
|
+
o pior momento possível.
|
|
112
|
+
*/
|
|
113
|
+
process.stdout.write(`${JSON.stringify({
|
|
114
|
+
level: 'warn',
|
|
115
|
+
message: '[FATAL-HANDLERS-DUP] registerFatalHandlers chamado mais de uma vez ' +
|
|
116
|
+
'neste processo; a chamada extra foi ignorada. Chame uma vez, com o ' +
|
|
117
|
+
'transport compartilhado.',
|
|
118
|
+
timestamp: new Date().toISOString(),
|
|
119
|
+
})}\n`);
|
|
120
|
+
return () => undefined;
|
|
121
|
+
}
|
|
122
|
+
target[REGISTERED] = true;
|
|
123
|
+
const flushTimeoutMs = options.flushTimeoutMs ?? FLUSH_TIMEOUT_MS;
|
|
124
|
+
const exit = options.exit ?? ((code) => process.exit(code));
|
|
125
|
+
/*
|
|
126
|
+
Um segundo erro fatal enquanto o primeiro ainda está sendo flushado não pode
|
|
127
|
+
reiniciar o ciclo — senão o processo nunca chega ao exit.
|
|
128
|
+
*/
|
|
129
|
+
let handling = false;
|
|
130
|
+
const handleFatal = async (origin, reason) => {
|
|
131
|
+
if (handling)
|
|
132
|
+
return;
|
|
133
|
+
handling = true;
|
|
134
|
+
/*
|
|
135
|
+
O exit vai no finally porque o guard de reentrância já está levantado: se
|
|
136
|
+
registrar ou flushar falhasse aqui, uma segunda passagem sairia na primeira
|
|
137
|
+
linha e o processo ficaria pendurado — travado num estado indefinido em vez
|
|
138
|
+
de reiniciar.
|
|
139
|
+
*/
|
|
140
|
+
try {
|
|
141
|
+
const { message, stack } = describeReason(reason);
|
|
142
|
+
logger.error(`[FATAL] ${origin}: ${message}`, {
|
|
143
|
+
origin,
|
|
144
|
+
/*
|
|
145
|
+
String, não array: o Loki exige valores string na structured metadata e
|
|
146
|
+
rejeita a requisição inteira quando recebe outra coisa.
|
|
147
|
+
*/
|
|
148
|
+
stack,
|
|
149
|
+
});
|
|
150
|
+
await flushLokiTransport(transport, flushTimeoutMs);
|
|
151
|
+
}
|
|
152
|
+
finally {
|
|
153
|
+
exit(1);
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
/*
|
|
157
|
+
O catch fecha o laço: sem ele, uma falha dentro do handler de
|
|
158
|
+
unhandledRejection viraria uma nova unhandledRejection.
|
|
159
|
+
*/
|
|
160
|
+
const onFatal = (origin) => (reason) => {
|
|
161
|
+
handleFatal(origin, reason).catch(() => {
|
|
162
|
+
/*
|
|
163
|
+
Vazio de propósito: o processo já está encerrando, e uma linha de erro
|
|
164
|
+
daqui não teria para onde ir.
|
|
165
|
+
*/
|
|
166
|
+
});
|
|
167
|
+
};
|
|
168
|
+
const onUncaught = onFatal('uncaughtException');
|
|
169
|
+
const onRejection = onFatal('unhandledRejection');
|
|
170
|
+
process.on('uncaughtException', onUncaught);
|
|
171
|
+
process.on('unhandledRejection', onRejection);
|
|
172
|
+
/*
|
|
173
|
+
Desfaz o registro e libera o guard. Em produção o processo não desregistra —
|
|
174
|
+
ele morre. Isto existe para o teste poder registrar de novo na próxima
|
|
175
|
+
execução, e é o que torna o guard acima verificável em vez de um efeito
|
|
176
|
+
global impossível de exercitar.
|
|
177
|
+
*/
|
|
178
|
+
return () => {
|
|
179
|
+
process.off('uncaughtException', onUncaught);
|
|
180
|
+
process.off('unhandledRejection', onRejection);
|
|
181
|
+
delete target[REGISTERED];
|
|
182
|
+
};
|
|
183
|
+
};
|
|
184
|
+
exports.registerFatalHandlers = registerFatalHandlers;
|
|
185
|
+
//# sourceMappingURL=fatal-handlers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fatal-handlers.js","sourceRoot":"","sources":["../src/fatal-handlers.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { buildNestLogger, createLokiTransport } from './logger';
|
|
2
|
+
export type { BuiltLogger, CreateLokiTransportOptions } from './logger';
|
|
3
|
+
export { redirectConsoleToNestLogger } from './console-redirect';
|
|
4
|
+
export type { ConsoleRedirectOptions } from './console-redirect';
|
|
5
|
+
export { registerFatalHandlers, flushLokiTransport } from './fatal-handlers';
|
|
6
|
+
export { DEFAULT_APP_LABEL, DEFAULT_LEVEL, DEFAULT_LOKI_INTERVAL_SECONDS, DEFAULT_LOKI_TIMEOUT_MS, } from './options';
|
|
7
|
+
export type { LoggerOptions, LokiOptions, FileOptions, LokiTransportLike, } from './options';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAChE,YAAY,EAAE,WAAW,EAAE,0BAA0B,EAAE,MAAM,UAAU,CAAC;AAExE,OAAO,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAC;AACjE,YAAY,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAEjE,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAE7E,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,6BAA6B,EAC7B,uBAAuB,GACxB,MAAM,WAAW,CAAC;AACnB,YAAY,EACV,aAAa,EACb,WAAW,EACX,WAAW,EACX,iBAAiB,GAClB,MAAM,WAAW,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Entrega de log dos serviços gyra-*: monta os transports (Console, File, Loki) a
|
|
4
|
+
partir de configuração explícita, e registra exceção fatal com flush antes do
|
|
5
|
+
exit.
|
|
6
|
+
|
|
7
|
+
Nada aqui lê variável de ambiente — ver `options.ts` para o porquê. Quem lê é o
|
|
8
|
+
serviço, no entrypoint dele.
|
|
9
|
+
|
|
10
|
+
O exemplo de uso completo está no README do pacote. Ele NÃO fica aqui de
|
|
11
|
+
propósito: o exemplo mostra o consumidor lendo `process.env`, e como o build
|
|
12
|
+
publica os comentários (`removeComments: false`), essas linhas apareceriam no
|
|
13
|
+
`dist/` e fariam um grep de auditoria acusar o pacote de ler ambiente.
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.DEFAULT_LOKI_TIMEOUT_MS = exports.DEFAULT_LOKI_INTERVAL_SECONDS = exports.DEFAULT_LEVEL = exports.DEFAULT_APP_LABEL = exports.flushLokiTransport = exports.registerFatalHandlers = exports.redirectConsoleToNestLogger = exports.createLokiTransport = exports.buildNestLogger = void 0;
|
|
17
|
+
var logger_1 = require("./logger");
|
|
18
|
+
Object.defineProperty(exports, "buildNestLogger", { enumerable: true, get: function () { return logger_1.buildNestLogger; } });
|
|
19
|
+
Object.defineProperty(exports, "createLokiTransport", { enumerable: true, get: function () { return logger_1.createLokiTransport; } });
|
|
20
|
+
var console_redirect_1 = require("./console-redirect");
|
|
21
|
+
Object.defineProperty(exports, "redirectConsoleToNestLogger", { enumerable: true, get: function () { return console_redirect_1.redirectConsoleToNestLogger; } });
|
|
22
|
+
var fatal_handlers_1 = require("./fatal-handlers");
|
|
23
|
+
Object.defineProperty(exports, "registerFatalHandlers", { enumerable: true, get: function () { return fatal_handlers_1.registerFatalHandlers; } });
|
|
24
|
+
Object.defineProperty(exports, "flushLokiTransport", { enumerable: true, get: function () { return fatal_handlers_1.flushLokiTransport; } });
|
|
25
|
+
var options_1 = require("./options");
|
|
26
|
+
Object.defineProperty(exports, "DEFAULT_APP_LABEL", { enumerable: true, get: function () { return options_1.DEFAULT_APP_LABEL; } });
|
|
27
|
+
Object.defineProperty(exports, "DEFAULT_LEVEL", { enumerable: true, get: function () { return options_1.DEFAULT_LEVEL; } });
|
|
28
|
+
Object.defineProperty(exports, "DEFAULT_LOKI_INTERVAL_SECONDS", { enumerable: true, get: function () { return options_1.DEFAULT_LOKI_INTERVAL_SECONDS; } });
|
|
29
|
+
Object.defineProperty(exports, "DEFAULT_LOKI_TIMEOUT_MS", { enumerable: true, get: function () { return options_1.DEFAULT_LOKI_TIMEOUT_MS; } });
|
|
30
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;EAYE;;;AAEF,mCAAgE;AAAvD,yGAAA,eAAe,OAAA;AAAE,6GAAA,mBAAmB,OAAA;AAG7C,uDAAiE;AAAxD,+HAAA,2BAA2B,OAAA;AAGpC,mDAA6E;AAApE,uHAAA,qBAAqB,OAAA;AAAE,oHAAA,kBAAkB,OAAA;AAElD,qCAKmB;AAJjB,4GAAA,iBAAiB,OAAA;AACjB,wGAAA,aAAa,OAAA;AACb,wHAAA,6BAA6B,OAAA;AAC7B,kHAAA,uBAAuB,OAAA"}
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import LokiTransport = require('winston-loki');
|
|
2
|
+
import type { LoggerService } from '@nestjs/common';
|
|
3
|
+
import { LoggerOptions, LokiOptions } from './options';
|
|
4
|
+
export interface BuiltLogger {
|
|
5
|
+
logger: LoggerService;
|
|
6
|
+
lokiTransport?: LokiTransport;
|
|
7
|
+
}
|
|
8
|
+
export interface CreateLokiTransportOptions extends LokiOptions {
|
|
9
|
+
service: string;
|
|
10
|
+
app?: string;
|
|
11
|
+
instance?: string;
|
|
12
|
+
pretty?: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Constrói o transport do Loki isoladamente, para o consumidor COMPARTILHAR um
|
|
16
|
+
* único batcher entre vários loggers do mesmo processo.
|
|
17
|
+
*
|
|
18
|
+
* Use isto quando o serviço monta mais de um logger — o worker do gyra-core monta
|
|
19
|
+
* quatro. Com um transport por logger, cada um tem seu batcher, e o flush de
|
|
20
|
+
* exceção fatal cobre só um deles.
|
|
21
|
+
*
|
|
22
|
+
* const lokiTransport = createLokiTransport({ service: 'gyra-core', host });
|
|
23
|
+
* const { logger } = buildNestLogger({ service: 'gyra-core', lokiTransport });
|
|
24
|
+
* // ...e o mesmo `lokiTransport` nos outros loggers do processo
|
|
25
|
+
*/
|
|
26
|
+
export declare const createLokiTransport: (options: CreateLokiTransportOptions) => LokiTransport;
|
|
27
|
+
/**
|
|
28
|
+
* Monta o logger do Nest com os transports pedidos e devolve, junto, o transport
|
|
29
|
+
* do Loki — necessário para `registerFatalHandlers` dar flush antes do exit.
|
|
30
|
+
*
|
|
31
|
+
* Não instala nada no processo: nem handler de exceção, nem redirecionamento de
|
|
32
|
+
* `console.*`. Esses são opt-in explícito, porque mexem no ciclo de vida do
|
|
33
|
+
* processo.
|
|
34
|
+
*/
|
|
35
|
+
export declare const buildNestLogger: (options: LoggerOptions) => BuiltLogger;
|
|
36
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AA4BA,OAAO,aAAa,GAAG,QAAQ,cAAc,CAAC,CAAC;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAGpD,OAAO,EAKL,aAAa,EACb,WAAW,EACZ,MAAM,WAAW,CAAC;AAQnB,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,aAAa,CAAC;IAOtB,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAoED,MAAM,WAAW,0BAA2B,SAAQ,WAAW;IAC7D,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,mBAAmB,GAC9B,SAAS,0BAA0B,KAClC,aAeF,CAAC;AAkCF;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,GAAI,SAAS,aAAa,KAAG,WAoDxD,CAAC"}
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.buildNestLogger = exports.createLokiTransport = void 0;
|
|
5
|
+
/*
|
|
6
|
+
PROVENIÊNCIA: derivado de gyra-core/src/common/logger/LoggerFactory.ts na branch
|
|
7
|
+
GYR-1555 (tag `backup/GYR-1555-pre-close`), onde o defeito de entrega ao Loki foi
|
|
8
|
+
diagnosticado e provado contra um Loki 3.0 real: antes, 21 linhas enviadas e 0
|
|
9
|
+
chegando, sem nenhum erro reportado; depois, 21 de 21.
|
|
10
|
+
|
|
11
|
+
Diferenças em relação à fonte, todas deliberadas:
|
|
12
|
+
|
|
13
|
+
- Nenhuma leitura de `process.env` — ver `options.ts`.
|
|
14
|
+
- Sem singleton de módulo. A fonte guardava o transport numa variável de módulo
|
|
15
|
+
para o handler de exceção fatal alcançá-lo; aqui ele volta no retorno, porque
|
|
16
|
+
estado global no pacote é o mesmo problema de ler env com outra roupa.
|
|
17
|
+
|
|
18
|
+
O singleton da fonte, porém, resolvia um problema real que o retorno sozinho
|
|
19
|
+
não resolve: serviço que monta VÁRIOS loggers no mesmo processo — o worker do
|
|
20
|
+
gyra-core monta quatro — teria um batcher por logger, e o flush de exceção
|
|
21
|
+
fatal drenaria só um deles. Para isso existe `createLokiTransport`: o
|
|
22
|
+
consumidor cria o transport uma vez e passa o mesmo em `lokiTransport`. O
|
|
23
|
+
compartilhamento fica explícito no call site, em vez de escondido no módulo.
|
|
24
|
+
- O redirecionamento de `console.*` saiu para `console-redirect.ts`, como opt-in
|
|
25
|
+
explícito: é efeito colateral de processo, e importar um pacote não deve mudar
|
|
26
|
+
o que `console.log` faz.
|
|
27
|
+
*/
|
|
28
|
+
const winston_1 = require("winston");
|
|
29
|
+
const nest_winston_1 = require("nest-winston");
|
|
30
|
+
const LokiTransport = require("winston-loki");
|
|
31
|
+
const os_1 = require("os");
|
|
32
|
+
const options_1 = require("./options");
|
|
33
|
+
const formatMeta = (meta) => {
|
|
34
|
+
const splat = meta[Symbol.for('splat')];
|
|
35
|
+
if (splat?.[0].stack) {
|
|
36
|
+
return ' ' + JSON.stringify(splat?.[0].stack?.[0], null, 2);
|
|
37
|
+
}
|
|
38
|
+
if (splat && splat.length) {
|
|
39
|
+
const obj = splat?.[0]?.context ?? splat;
|
|
40
|
+
if (typeof obj === 'string') {
|
|
41
|
+
return ' ' + obj;
|
|
42
|
+
}
|
|
43
|
+
if (Array.isArray(obj) &&
|
|
44
|
+
obj.length === 1 &&
|
|
45
|
+
typeof obj[0] === 'object' &&
|
|
46
|
+
Object.keys(obj[0]).length === 1 &&
|
|
47
|
+
obj[0].context === undefined) {
|
|
48
|
+
return '';
|
|
49
|
+
}
|
|
50
|
+
return ' ' + JSON.stringify(obj, null, 2);
|
|
51
|
+
}
|
|
52
|
+
return '';
|
|
53
|
+
};
|
|
54
|
+
const buildPrettyFormat = () => {
|
|
55
|
+
const localFormat = winston_1.format.printf(({ message, level, ..._meta }) => {
|
|
56
|
+
const color = {
|
|
57
|
+
white: '\x1B[38;2;173;190;203m',
|
|
58
|
+
blue: '\x1B[34m',
|
|
59
|
+
red: '\x1B[31m',
|
|
60
|
+
yellow: '\x1B[33m',
|
|
61
|
+
cyan: '\x1B[36m',
|
|
62
|
+
magenta: '\x1B[35m',
|
|
63
|
+
reset: '\x1B[0m',
|
|
64
|
+
};
|
|
65
|
+
const levelColor = {
|
|
66
|
+
info: color.white,
|
|
67
|
+
error: color.red,
|
|
68
|
+
warn: color.yellow,
|
|
69
|
+
debug: color.cyan,
|
|
70
|
+
verbose: color.magenta,
|
|
71
|
+
};
|
|
72
|
+
const currentColor = levelColor[level];
|
|
73
|
+
const meta = formatMeta(_meta);
|
|
74
|
+
/*
|
|
75
|
+
`verbose` é o maior level, com 7 letras; +1 para sempre sobrar espaço.
|
|
76
|
+
*/
|
|
77
|
+
let finalMessage = `${level.padEnd(8)} ${message}${meta ?? ''}`;
|
|
78
|
+
finalMessage = finalMessage.replaceAll('\n', `\n${currentColor ?? 'red'}`);
|
|
79
|
+
return `${currentColor}` + finalMessage + `${color.reset}`;
|
|
80
|
+
});
|
|
81
|
+
return winston_1.format.combine(winston_1.format.splat(), localFormat);
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Constrói o transport do Loki isoladamente, para o consumidor COMPARTILHAR um
|
|
85
|
+
* único batcher entre vários loggers do mesmo processo.
|
|
86
|
+
*
|
|
87
|
+
* Use isto quando o serviço monta mais de um logger — o worker do gyra-core monta
|
|
88
|
+
* quatro. Com um transport por logger, cada um tem seu batcher, e o flush de
|
|
89
|
+
* exceção fatal cobre só um deles.
|
|
90
|
+
*
|
|
91
|
+
* const lokiTransport = createLokiTransport({ service: 'gyra-core', host });
|
|
92
|
+
* const { logger } = buildNestLogger({ service: 'gyra-core', lokiTransport });
|
|
93
|
+
* // ...e o mesmo `lokiTransport` nos outros loggers do processo
|
|
94
|
+
*/
|
|
95
|
+
const createLokiTransport = (options) => {
|
|
96
|
+
const { service, app, instance, pretty, ...loki } = options;
|
|
97
|
+
return buildLokiTransport(loki, {
|
|
98
|
+
service,
|
|
99
|
+
app: app ?? options_1.DEFAULT_APP_LABEL,
|
|
100
|
+
instance: instance ?? (0, os_1.hostname)(),
|
|
101
|
+
...loki.labels,
|
|
102
|
+
}, pretty
|
|
103
|
+
? buildPrettyFormat()
|
|
104
|
+
: winston_1.format.combine(winston_1.format.timestamp(), winston_1.format.json()));
|
|
105
|
+
};
|
|
106
|
+
exports.createLokiTransport = createLokiTransport;
|
|
107
|
+
const buildLokiTransport = (loki, labels, lineFormat) => new LokiTransport({
|
|
108
|
+
host: loki.host,
|
|
109
|
+
basicAuth: loki.basicAuth,
|
|
110
|
+
json: true,
|
|
111
|
+
labels,
|
|
112
|
+
interval: loki.interval ?? options_1.DEFAULT_LOKI_INTERVAL_SECONDS,
|
|
113
|
+
batching: true,
|
|
114
|
+
timeout: loki.timeout ?? options_1.DEFAULT_LOKI_TIMEOUT_MS,
|
|
115
|
+
format: lineFormat,
|
|
116
|
+
/*
|
|
117
|
+
A falha de entrega tem que ser audível: enquanto era silenciosa, o serviço
|
|
118
|
+
perdeu ~99,5% dos seus logs sem ninguém notar. Escreve direto no console
|
|
119
|
+
porque passar pelo logger completo realimentaria o Loki num laço de erro.
|
|
120
|
+
*/
|
|
121
|
+
onConnectionError: (err) => process.stdout.write(`${JSON.stringify({
|
|
122
|
+
level: 'error',
|
|
123
|
+
message: `[LOKI-SHIP-FAIL] status=${err?.statusCode ?? 'none'} ${err?.message ?? err}`,
|
|
124
|
+
timestamp: new Date().toISOString(),
|
|
125
|
+
})}\n`),
|
|
126
|
+
});
|
|
127
|
+
/**
|
|
128
|
+
* Monta o logger do Nest com os transports pedidos e devolve, junto, o transport
|
|
129
|
+
* do Loki — necessário para `registerFatalHandlers` dar flush antes do exit.
|
|
130
|
+
*
|
|
131
|
+
* Não instala nada no processo: nem handler de exceção, nem redirecionamento de
|
|
132
|
+
* `console.*`. Esses são opt-in explícito, porque mexem no ciclo de vida do
|
|
133
|
+
* processo.
|
|
134
|
+
*/
|
|
135
|
+
const buildNestLogger = (options) => {
|
|
136
|
+
const lineFormat = options.pretty
|
|
137
|
+
? buildPrettyFormat()
|
|
138
|
+
: winston_1.format.combine(winston_1.format.timestamp(), winston_1.format.json());
|
|
139
|
+
/*
|
|
140
|
+
Passar os dois é ambíguo: um pede para construir um transport novo, o outro
|
|
141
|
+
para reusar um existente. Falhar alto é melhor que escolher em silêncio e
|
|
142
|
+
deixar o consumidor com dois batchers sem saber.
|
|
143
|
+
*/
|
|
144
|
+
if (options.loki && options.lokiTransport) {
|
|
145
|
+
throw new Error('[log-transport] `loki` e `lokiTransport` sao mutuamente exclusivos: ' +
|
|
146
|
+
'use `loki` para construir um transport novo, ou `lokiTransport` para ' +
|
|
147
|
+
'reusar um ja construido por createLokiTransport().');
|
|
148
|
+
}
|
|
149
|
+
const transportsList = [
|
|
150
|
+
new winston_1.transports.Console({ format: lineFormat }),
|
|
151
|
+
];
|
|
152
|
+
let lokiTransport;
|
|
153
|
+
if (options.lokiTransport) {
|
|
154
|
+
lokiTransport = options.lokiTransport;
|
|
155
|
+
transportsList.push(lokiTransport);
|
|
156
|
+
}
|
|
157
|
+
else if (options.loki) {
|
|
158
|
+
lokiTransport = buildLokiTransport(options.loki, {
|
|
159
|
+
service: options.service,
|
|
160
|
+
app: options.app ?? options_1.DEFAULT_APP_LABEL,
|
|
161
|
+
instance: options.instance ?? (0, os_1.hostname)(),
|
|
162
|
+
...options.loki.labels,
|
|
163
|
+
}, lineFormat);
|
|
164
|
+
transportsList.push(lokiTransport);
|
|
165
|
+
}
|
|
166
|
+
else if (options.file) {
|
|
167
|
+
transportsList.push(new winston_1.transports.File({ filename: options.file.filename }));
|
|
168
|
+
}
|
|
169
|
+
const logger = nest_winston_1.WinstonModule.createLogger({
|
|
170
|
+
level: options.level ?? options_1.DEFAULT_LEVEL,
|
|
171
|
+
transports: transportsList,
|
|
172
|
+
});
|
|
173
|
+
return { logger, lokiTransport };
|
|
174
|
+
};
|
|
175
|
+
exports.buildNestLogger = buildNestLogger;
|
|
176
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"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"}
|