@adatechnology/logger 0.0.2
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/CHANGELOG.md +7 -0
- package/README.md +151 -0
- package/agents/skills/SKILL.md +47 -0
- package/dist/index.d.ts +103 -0
- package/dist/index.js +567 -0
- package/package.json +27 -0
- package/src/context/async-context.service.ts +17 -0
- package/src/context/async-context.types.ts +1 -0
- package/src/implementations/winston/winston.logger.constants.ts +1 -0
- package/src/implementations/winston/winston.logger.module.ts +305 -0
- package/src/implementations/winston/winston.logger.provider.ts +98 -0
- package/src/implementations/winston/winston.logger.token.ts +3 -0
- package/src/implementations/winston/winston.logger.types.ts +16 -0
- package/src/index.ts +11 -0
- package/src/logger.config.ts +57 -0
- package/src/logger.constant.ts +32 -0
- package/src/logger.interface.ts +25 -0
- package/src/logger.module.ts +50 -0
- package/src/logger.provider.ts +53 -0
- package/src/logger.token.ts +1 -0
- package/src/middleware/request-context.middleware.ts +26 -0
- package/src/middleware/request-context.types.ts +11 -0
- package/src/obfuscator.ts +96 -0
- package/src/obfuscator.types.ts +6 -0
- package/src/request-id.constants.ts +7 -0
- package/tsconfig.json +14 -0
- package/tsconfig.tsup.json +6 -0
package/CHANGELOG.md
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Logger provider
|
|
2
|
+
|
|
3
|
+
Pacote que expõe um provider de logs seguindo o padrão das outras libs do monorepo.
|
|
4
|
+
|
|
5
|
+
Exporta `LoggerModule` e o token `LOGGER_PROVIDER`.
|
|
6
|
+
|
|
7
|
+
## Exemplos de uso
|
|
8
|
+
|
|
9
|
+
1. Uso padrão (Winston como implementação padrão):
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { Module } from "@nestjs/common";
|
|
13
|
+
import { LoggerModule } from "@adatechnology/logger";
|
|
14
|
+
|
|
15
|
+
@Module({
|
|
16
|
+
imports: [
|
|
17
|
+
// Opção 1: Configuração estática (forRoot)
|
|
18
|
+
LoggerModule.forRoot({
|
|
19
|
+
level: 'debug',
|
|
20
|
+
context: 'MyService',
|
|
21
|
+
isProduction: process.env.NODE_ENV === 'production',
|
|
22
|
+
colorize: true,
|
|
23
|
+
}),
|
|
24
|
+
|
|
25
|
+
// Opção 2: Configuração dinâmica (forRootAsync)
|
|
26
|
+
LoggerModule.forRootAsync({
|
|
27
|
+
useFactory: (configService: ConfigService) => ({
|
|
28
|
+
level: configService.get('LOG_LEVEL'),
|
|
29
|
+
isProduction: configService.get('NODE_ENV') === 'production',
|
|
30
|
+
}),
|
|
31
|
+
inject: [ConfigService],
|
|
32
|
+
}),
|
|
33
|
+
],
|
|
34
|
+
})
|
|
35
|
+
export class AppModule {}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
2. Customizando o Winston e adicionando chaves sensíveis para ofuscar:
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { Module } from "@nestjs/common";
|
|
42
|
+
import { LoggerModule } from "@adatechnology/logger";
|
|
43
|
+
import { transports } from "winston";
|
|
44
|
+
|
|
45
|
+
const loggerOptions = {
|
|
46
|
+
level: "debug",
|
|
47
|
+
transports: [new transports.Console()],
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
@Module({
|
|
51
|
+
imports: [
|
|
52
|
+
LoggerModule.forRoot({
|
|
53
|
+
loggerOptions,
|
|
54
|
+
// obfuscatorKeys aceita strings ou objetos { key, obfuscator }
|
|
55
|
+
obfuscatorKeys: [
|
|
56
|
+
"password",
|
|
57
|
+
{
|
|
58
|
+
key: "creditCard",
|
|
59
|
+
obfuscator: (v: any) =>
|
|
60
|
+
typeof v === "string" ? v.replace(/\d(?=\d{4})/g, "*") : "****",
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
}),
|
|
64
|
+
],
|
|
65
|
+
})
|
|
66
|
+
export class AppModule {}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
3. Injetando o provider e usando nos serviços:
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
import { Inject, Injectable } from "@nestjs/common";
|
|
73
|
+
import { LOGGER_PROVIDER } from "@adatechnology/logger";
|
|
74
|
+
import type { LoggerProviderInterface } from "@adatechnology/logger";
|
|
75
|
+
|
|
76
|
+
@Injectable()
|
|
77
|
+
export class FooService {
|
|
78
|
+
constructor(
|
|
79
|
+
@Inject(LOGGER_PROVIDER) private readonly logger: LoggerProviderInterface,
|
|
80
|
+
) {
|
|
81
|
+
this.logger.info({ message: "iniciando FooService" });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Observação: o obfuscator padrão é aplicado recursivamente em objetos e aceita entradas do tipo string (nome do campo) ou objetos `{ key, obfuscator }` para permitir funções customizadas do usuário.
|
|
87
|
+
|
|
88
|
+
Se quiser, eu adiciono um exemplo de teste unitário para validar o comportamento do obfuscator.
|
|
89
|
+
|
|
90
|
+
## Padrão de Logging (Desenvolvimento)
|
|
91
|
+
|
|
92
|
+
Este pacote implementa um formato de log padronizado para todo o monorepo, facilitando o rastreamento de chamadas entre múltiplas bibliotecas e serviços.
|
|
93
|
+
|
|
94
|
+
### Formato Final
|
|
95
|
+
`[App-name@version][lib-name:version][requestId][timestamp][source][libMethod][LEVEL] - message - {payload}`
|
|
96
|
+
|
|
97
|
+
### Propriedades do Payload
|
|
98
|
+
Ao realizar um log, você pode passar as seguintes propriedades para enriquecer o contexto:
|
|
99
|
+
|
|
100
|
+
- `message`: A mensagem principal do log.
|
|
101
|
+
- `context`: O contexto geral (ex: nome da classe da biblioteca).
|
|
102
|
+
- `source`: O chamador original (breadcrumb). Ex: `HttpClientController.listPokemon`.
|
|
103
|
+
- `lib`: Nome da biblioteca que está gerando o log. Ex: `@adatechnology/http-client`.
|
|
104
|
+
- `libVersion`: Versão da biblioteca.
|
|
105
|
+
- `libMethod`: O método interno da biblioteca sendo executado. Ex: `get`.
|
|
106
|
+
- `meta`: Objeto com metadados adicionais (será exibido em uma única linha compacta).
|
|
107
|
+
|
|
108
|
+
### Exemplo de Log de Biblioteca
|
|
109
|
+
Para uma biblioteca que segue o padrão:
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
this.logger.info({
|
|
113
|
+
message: 'HTTP Request GET https://pokeapi.co/api/v2/pokemon',
|
|
114
|
+
context: 'HttpRedisClient',
|
|
115
|
+
lib: '@adatechnology/http-client',
|
|
116
|
+
libVersion: '0.0.2',
|
|
117
|
+
libMethod: 'get',
|
|
118
|
+
source: 'HttpClientController.listPokemon', // vindo do logContext da chamada
|
|
119
|
+
meta: { ... }
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Resultado visual:
|
|
124
|
+
`[App-example@0.0.3][@adatechnology/http-client:0.0.2][req-id][2026-03-29...][HttpClientController.listPokemon][HttpRedisClient.get][INFO] - HTTP Request... - { headers: ... }`
|
|
125
|
+
|
|
126
|
+
## Middleware e contexto automático
|
|
127
|
+
|
|
128
|
+
O pacote oferece um middleware `RequestContextMiddleware` que injeta um `requestId` (a partir do header `x-request-id` ou gerando um UUID) e executa a request dentro de um contexto assíncrono (AsyncLocalStorage). Para usá-lo no NestJS:
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
|
|
132
|
+
import { RequestContextMiddleware, LoggerModule } from '@adatechnology/logger';
|
|
133
|
+
|
|
134
|
+
@Module({ imports: [LoggerModule.forRoot()] })
|
|
135
|
+
export class AppModule implements NestModule {
|
|
136
|
+
configure(consumer: MiddlewareConsumer) {
|
|
137
|
+
consumer.apply(RequestContextMiddleware).forRoutes('*');
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Com o middleware ativo, o logger automaticamente incluirá `requestId` nos metadados quando não for passado explicitamente.
|
|
143
|
+
|
|
144
|
+
## Request-scoped provider
|
|
145
|
+
|
|
146
|
+
Se desejar um provider por request (cada request recebe uma instância com `setContext` isolado), passe `requestScoped: true` ao `forRoot`:
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
LoggerModule.forRoot({ requestScoped: true })
|
|
150
|
+
```
|
|
151
|
+
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: logger
|
|
3
|
+
description: Patterns for @adatechnology/logger. Use when configuring Winston logging, managing Request-ID context (AsyncLocalStorage), or obfuscating sensitive data.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 📝 Logger Standards
|
|
7
|
+
|
|
8
|
+
## 🚀 Setup Example
|
|
9
|
+
```typescript
|
|
10
|
+
@Module({
|
|
11
|
+
imports: [
|
|
12
|
+
LoggerModule.forRoot({
|
|
13
|
+
level: 'info',
|
|
14
|
+
sensitiveKeys: ['password', 'token', 'clientSecret'],
|
|
15
|
+
}),
|
|
16
|
+
],
|
|
17
|
+
})
|
|
18
|
+
export class AppModule implements NestModule {
|
|
19
|
+
configure(consumer: MiddlewareConsumer) {
|
|
20
|
+
// Required to enable Request-ID tracking across async calls
|
|
21
|
+
consumer.apply(RequestContextMiddleware).forRoutes('*');
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## 🏗️ Usage Pattern
|
|
27
|
+
```typescript
|
|
28
|
+
@Injectable()
|
|
29
|
+
export class MyService {
|
|
30
|
+
constructor(
|
|
31
|
+
@Inject(LOGGER_PROVIDER) private readonly logger: LoggerProviderInterface
|
|
32
|
+
) {}
|
|
33
|
+
|
|
34
|
+
doWork() {
|
|
35
|
+
this.logger.info('Action performed', { userId: '123' });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## 🔐 Obfuscation
|
|
41
|
+
The logger automatically hides values for keys defined in `sensitiveKeys`.
|
|
42
|
+
- **Default Keys**: `password`, `token`, `authorization`, `cookie`, `secret`.
|
|
43
|
+
- **Custom Keys**: Add via `LoggerModule.forRoot()`.
|
|
44
|
+
|
|
45
|
+
## 🛠️ Internal Mechanics
|
|
46
|
+
- **Async Context**: Uses `AsyncLocalStorage` to store the Request-ID.
|
|
47
|
+
- **Context Access**: Use `getContext()` to retrieve the current request state anywhere in the call stack.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { DynamicModule, NestMiddleware } from '@nestjs/common';
|
|
2
|
+
import { LoggerOptions } from 'winston';
|
|
3
|
+
|
|
4
|
+
interface ObfuscatorKey {
|
|
5
|
+
key: string;
|
|
6
|
+
obfuscator: Obfuscator;
|
|
7
|
+
}
|
|
8
|
+
interface WinstonModuleConfig {
|
|
9
|
+
loggerOptions?: LoggerOptions;
|
|
10
|
+
obfuscator?: Obfuscator;
|
|
11
|
+
obfuscatorKeys?: Array<string | ObfuscatorKey>;
|
|
12
|
+
}
|
|
13
|
+
type Obfuscator = (value: unknown) => unknown;
|
|
14
|
+
|
|
15
|
+
declare enum LoggerLevel {
|
|
16
|
+
DEBUG = "debug",
|
|
17
|
+
INFO = "info",
|
|
18
|
+
WARN = "warn",
|
|
19
|
+
ERROR = "error"
|
|
20
|
+
}
|
|
21
|
+
interface LogPayload {
|
|
22
|
+
message: string;
|
|
23
|
+
context?: string;
|
|
24
|
+
meta?: Record<string, unknown>;
|
|
25
|
+
}
|
|
26
|
+
interface LoggerProviderInterface {
|
|
27
|
+
debug(payload: LogPayload): void;
|
|
28
|
+
info(payload: LogPayload): void;
|
|
29
|
+
warn(payload: LogPayload): void;
|
|
30
|
+
error(payload: LogPayload): void;
|
|
31
|
+
setContext?(context: string): void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface LoggerConfig extends WinstonModuleConfig {
|
|
35
|
+
/**
|
|
36
|
+
* Define se o provider do logger deve ser request-scoped
|
|
37
|
+
*/
|
|
38
|
+
requestScoped?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Nível de log padrão (string compatível com winston)
|
|
41
|
+
*/
|
|
42
|
+
level?: LoggerLevel | string;
|
|
43
|
+
/**
|
|
44
|
+
* Contexto padrão para os logs
|
|
45
|
+
*/
|
|
46
|
+
context?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Define se o log deve ser formatado para produção (ex.: JSON)
|
|
49
|
+
*/
|
|
50
|
+
isProduction?: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Define se deve colorir a saída (útil para desenvolvimento local)
|
|
53
|
+
*/
|
|
54
|
+
colorize?: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Nome da aplicação para exibição nos logs
|
|
57
|
+
*/
|
|
58
|
+
appName?: string;
|
|
59
|
+
/**
|
|
60
|
+
* Versão da aplicação para exibição nos logs
|
|
61
|
+
*/
|
|
62
|
+
appVersion?: string;
|
|
63
|
+
/**
|
|
64
|
+
* Identificação da biblioteca/módulo que está gerando o log
|
|
65
|
+
*/
|
|
66
|
+
lib?: string;
|
|
67
|
+
/**
|
|
68
|
+
* Versão da biblioteca/módulo que está gerando o log
|
|
69
|
+
*/
|
|
70
|
+
libVersion?: string;
|
|
71
|
+
}
|
|
72
|
+
declare const DEFAULT_LOGGER_CONFIG: LoggerConfig;
|
|
73
|
+
|
|
74
|
+
declare const LOGGER_PROVIDER = "LOGGER_PROVIDER";
|
|
75
|
+
|
|
76
|
+
declare class LoggerModule {
|
|
77
|
+
static forRoot(config?: LoggerConfig): DynamicModule;
|
|
78
|
+
static forRootAsync(options: {
|
|
79
|
+
imports?: any[];
|
|
80
|
+
useFactory: (...args: any[]) => Promise<LoggerConfig> | LoggerConfig;
|
|
81
|
+
inject?: any[];
|
|
82
|
+
}): DynamicModule;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
type RequestLike = {
|
|
86
|
+
headers?: Record<string, unknown> | undefined;
|
|
87
|
+
[key: string]: unknown;
|
|
88
|
+
};
|
|
89
|
+
type ResponseLike = {
|
|
90
|
+
[key: string]: unknown;
|
|
91
|
+
};
|
|
92
|
+
type NextFunctionLike = (err?: unknown) => void;
|
|
93
|
+
|
|
94
|
+
declare class RequestContextMiddleware implements NestMiddleware {
|
|
95
|
+
use(req: RequestLike, _res: ResponseLike, next: NextFunctionLike): void;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
type RequestContext = Record<string, unknown> | undefined;
|
|
99
|
+
|
|
100
|
+
declare function getContext(): RequestContext;
|
|
101
|
+
declare function runWithContext<T>(ctx: Record<string, unknown>, fn: () => T): T;
|
|
102
|
+
|
|
103
|
+
export { DEFAULT_LOGGER_CONFIG, LOGGER_PROVIDER, type LogPayload, type LoggerConfig, LoggerLevel, LoggerModule, type LoggerProviderInterface, RequestContextMiddleware, getContext, runWithContext };
|