@jmlq/logger 0.1.0-alpha.20 → 0.1.0-alpha.21
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 +472 -80
- package/architecture.md +112 -90
- package/assets/mongo-log.png +0 -0
- package/assets/pg-log.png +0 -0
- package/dist/application/factory/logger.factory.d.ts +2 -1
- package/dist/application/types/index.d.ts +1 -0
- package/dist/application/types/index.js +17 -0
- package/dist/{domain/ports/logger-factory-config.port.d.ts → application/types/logger-factory-config.type.d.ts} +3 -3
- package/dist/domain/ports/index.d.ts +0 -1
- package/dist/domain/ports/index.js +0 -2
- package/dist/index.d.ts +2 -1
- package/package.json +2 -2
- package/install.md +0 -632
- /package/dist/{domain/ports/logger-factory-config.port.js → application/types/logger-factory-config.type.js} +0 -0
package/install.md
DELETED
|
@@ -1,632 +0,0 @@
|
|
|
1
|
-
# GUÍA DE CONFIGURACIÓN @jmlq/logger Y SUS PLUGINS
|
|
2
|
-
|
|
3
|
-
## PASO 1 – BOOTSTRAP DEL LOGGER CORE
|
|
4
|
-
|
|
5
|
-
### 1.1. OPCIONES DE BOOTSTRAP
|
|
6
|
-
|
|
7
|
-
Path sugerido: `src/infrastructure/adapters/jmlq/logger/core/logger.bootstrap.options.ts`
|
|
8
|
-
|
|
9
|
-
`LoggerBootstrapOptions` es un **contrato de configuración** usado al momento de inicializar el logger. Define:
|
|
10
|
-
|
|
11
|
-
- El **nivel mínimo de log** permitido.
|
|
12
|
-
- Configuración de **redacción de PII** (datos sensibles).
|
|
13
|
-
- Los **datasources** del logger que se deben habilitar (FS, Mongo, PostgreSQL).
|
|
14
|
-
|
|
15
|
-
Describe cómo debe construirse el logger, qué debe censurar, y a dónde deben enviarse los logs.
|
|
16
|
-
|
|
17
|
-
```ts
|
|
18
|
-
import { LogLevel } from "@jmlq/logger";
|
|
19
|
-
import { IFilesystemDatasourceOptions } from "@jmlq/logger-plugin-fs";
|
|
20
|
-
import { MongoPluginConfig } from "@jmlq/logger-plugin-mongo";
|
|
21
|
-
import { PostgresPluginConfig } from "@jmlq/logger-plugin-postgresql";
|
|
22
|
-
|
|
23
|
-
export interface LoggerBootstrapOptions {
|
|
24
|
-
minLevel: LogLevel;
|
|
25
|
-
pii?: {
|
|
26
|
-
enabled?: boolean;
|
|
27
|
-
whitelistKeys?: string[];
|
|
28
|
-
blacklistKeys?: string[];
|
|
29
|
-
patterns?: any[];
|
|
30
|
-
deep?: boolean;
|
|
31
|
-
includeDefaults?: boolean;
|
|
32
|
-
};
|
|
33
|
-
adapters?: {
|
|
34
|
-
// NOTA: Opcionales depende de las necesidades del cliente
|
|
35
|
-
fs?: IFilesystemDatasourceOptions;
|
|
36
|
-
mongo?: MongoPluginConfig;
|
|
37
|
-
pg?: PostgresPluginConfig;
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
- `minLevel`: nivel mínimo de log (ss de tipo `LogLevel` de `@jmlq/logger`).
|
|
43
|
-
- `pii`: configuración de redacción (redaction) de datos sensibles.
|
|
44
|
-
- `adapters`: configuración específica para cada plugin (**todos opcionales**).
|
|
45
|
-
|
|
46
|
-
---
|
|
47
|
-
|
|
48
|
-
### 1.2. IMPLEMENTACIÓN DEL BOOTSTRAP
|
|
49
|
-
|
|
50
|
-
Path sugerido: `src/infrastructure/adapters/jmlq/logger/core/logger.bootstrap.ts`
|
|
51
|
-
|
|
52
|
-
La clase `LoggerBootstrap` es un componente de infraestructura cuyo objetivo es:
|
|
53
|
-
|
|
54
|
-
- Crear e inicializar el **logger principal** de la aplicación.
|
|
55
|
-
- Construir la lista de **datasources** (FS, Mongo, PostgreSQL) según la configuración del usuario.
|
|
56
|
-
- Exponer funciones para **flush** y **dispose**, importantes para apagado limpio.
|
|
57
|
-
- Encapsular la instancia del logger para no exponer detalles internos.
|
|
58
|
-
|
|
59
|
-
La clase completa se encarga de arrancar correctamente el sistema de logging.
|
|
60
|
-
|
|
61
|
-
```ts
|
|
62
|
-
import { type ILogDatasource, LoggerFactory } from "@jmlq/logger";
|
|
63
|
-
import { FsAdapter } from "../fs/fs.adapter";
|
|
64
|
-
import { MongoAdapter } from "../mongo/mongo.adapter";
|
|
65
|
-
import { PgAdapter } from "../pg/pg.adapter";
|
|
66
|
-
import { LoggerBootstrapOptions } from "./logger.bootstrap.options";
|
|
67
|
-
|
|
68
|
-
export class LoggerBootstrap {
|
|
69
|
-
private constructor(
|
|
70
|
-
private readonly _logger: ReturnType<typeof LoggerFactory.create>
|
|
71
|
-
) {}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Este es el punto de entrada para construir el logger.
|
|
75
|
-
*/
|
|
76
|
-
static async create(opts: LoggerBootstrapOptions): Promise<LoggerBootstrap> {
|
|
77
|
-
// Almacena los plugins inicializados.
|
|
78
|
-
const dsList: ILogDatasource[] = [];
|
|
79
|
-
|
|
80
|
-
// Inyectar datasources
|
|
81
|
-
// NOTA: Opcionales depende de las necesidades del cliente
|
|
82
|
-
// FS si está configurado
|
|
83
|
-
if (opts.adapters?.fs) {
|
|
84
|
-
const fs = FsAdapter.create(opts.adapters.fs);
|
|
85
|
-
if (fs) dsList.push(fs.datasource);
|
|
86
|
-
}
|
|
87
|
-
// MongoDB si está configurado
|
|
88
|
-
if (opts.adapters?.mongo) {
|
|
89
|
-
const mongo = await MongoAdapter.create(opts.adapters.mongo);
|
|
90
|
-
if (mongo) dsList.push(mongo.datasource);
|
|
91
|
-
}
|
|
92
|
-
// Postgresql si está configurado
|
|
93
|
-
if (opts.adapters?.pg) {
|
|
94
|
-
const pg = await PgAdapter.create(opts.adapters.pg);
|
|
95
|
-
if (pg) dsList.push(pg.datasource);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (dsList.length === 0)
|
|
99
|
-
throw new Error("[logger] No hay datasources válidos.");
|
|
100
|
-
|
|
101
|
-
// Crear el logger central
|
|
102
|
-
const logger = LoggerFactory.create({
|
|
103
|
-
datasources: dsList,
|
|
104
|
-
minLevel: opts.minLevel,
|
|
105
|
-
redactorOptions: {
|
|
106
|
-
enabled: opts.pii?.enabled ?? false,
|
|
107
|
-
deep: opts.pii?.deep ?? true,
|
|
108
|
-
patterns: opts.pii?.patterns ?? [],
|
|
109
|
-
},
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
console.log("✅ Logger creado correctamente.\n");
|
|
113
|
-
|
|
114
|
-
// Retornar la instancia final
|
|
115
|
-
return new LoggerBootstrap(logger);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Permite acceder al logger real pero manteniendo encapsulación.
|
|
119
|
-
get logger() {
|
|
120
|
-
return this._logger;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Fuerza a que todos los logs pendientes se escriban en los datasources.
|
|
124
|
-
// Lo usa el ciclo de vida del servidor (shutdown, errores críticos, etc.).
|
|
125
|
-
async flush() {
|
|
126
|
-
const logs = this._logger as any;
|
|
127
|
-
if (typeof logs.flush === "function") await logs.flush();
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Cierra conexiones y limpia recursos (como conexiones a BD).
|
|
131
|
-
// Asegura un apagado ordenado.
|
|
132
|
-
async dispose() {
|
|
133
|
-
const logs = this._logger as any;
|
|
134
|
-
if (typeof logs.dispose === "function") await logs.dispose();
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
---
|
|
140
|
-
|
|
141
|
-
## PASO 2 – CONFIGURAR PLUGINS / DATASOURCES
|
|
142
|
-
|
|
143
|
-
### 2.1. Plugin FileSystem (`@jmlq/logger-plugin-fs`)
|
|
144
|
-
|
|
145
|
-
Path sugerido: `src/infrastructure/adapters/jmlq/logger/fs/fs.adapter.ts`
|
|
146
|
-
|
|
147
|
-
`FsAdapter` es un **adaptador de infraestructura** cuyo propósito es `crear`, `inicializar` y `exponer` un `datasource` de logs basado en archivos (FileSystem) usando el plugin `@jmlq/logger-plugin-fs`.
|
|
148
|
-
|
|
149
|
-
```ts
|
|
150
|
-
import {
|
|
151
|
-
// Función del plugin que crea realmente el datasource de FileSystem.
|
|
152
|
-
createFsDatasource,
|
|
153
|
-
// Tipo de configuración que el plugin requiere
|
|
154
|
-
IFilesystemDatasourceOptions,
|
|
155
|
-
} from "@jmlq/logger-plugin-fs";
|
|
156
|
-
// Interfaz estándar que todos los datasources deben implementar para funcionar dentro de @jmlq/logger
|
|
157
|
-
import type { ILogDatasource } from "@jmlq/logger";
|
|
158
|
-
|
|
159
|
-
export class FsAdapter {
|
|
160
|
-
private constructor(private readonly ds: ILogDatasource) {}
|
|
161
|
-
|
|
162
|
-
// Este es el punto central del adaptador.
|
|
163
|
-
static create(opts: IFilesystemDatasourceOptions): FsAdapter | undefined {
|
|
164
|
-
try {
|
|
165
|
-
// Se inicializa el plugin.
|
|
166
|
-
// Se configuran rutas, políticas de rotación, serializer, etc.
|
|
167
|
-
// El datasource queda listo para recibir logs.
|
|
168
|
-
const ds = createFsDatasource(opts);
|
|
169
|
-
console.log("[logger] Conectado a FS para logs");
|
|
170
|
-
return new FsAdapter(ds);
|
|
171
|
-
} catch (e: any) {
|
|
172
|
-
console.warn("[logger] FS deshabilitado:", e?.message ?? e);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
// Permite a otras capas (especialmente LoggerBootstrap) obtener el datasource real que necesita LoggerFactory.create
|
|
176
|
-
get datasource(): ILogDatasource {
|
|
177
|
-
return this.ds;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
---
|
|
183
|
-
|
|
184
|
-
### 2.2. Plugin MongoDB (`@jmlq/logger-plugin-mongo`)
|
|
185
|
-
|
|
186
|
-
Path sugerido: `src/infrastructure/adapters/jmlq/logger/mongo/mongo.adapter.ts`
|
|
187
|
-
|
|
188
|
-
`MongoAdapter` es un **adaptador de infraestructura** cuyo propósito es `crear`, `inicializar` y `exponer` un `datasource` de los logs almacenados en `MongoDB` usando el plugin `@jmlq/logger-plugin-mongo`.
|
|
189
|
-
|
|
190
|
-
```ts
|
|
191
|
-
// Interfaz estándar que todos los datasources deben implementar para funcionar dentro de @jmlq/logger
|
|
192
|
-
import type { ILogDatasource } from "@jmlq/logger";
|
|
193
|
-
import {
|
|
194
|
-
// Función del plugin que crea realmente el datasource de MongoDb.
|
|
195
|
-
createMongoDatasource,
|
|
196
|
-
// Tipo de configuración que el plugin requiere
|
|
197
|
-
MongoPluginConfig,
|
|
198
|
-
} from "@jmlq/logger-plugin-mongo";
|
|
199
|
-
|
|
200
|
-
export class MongoAdapter {
|
|
201
|
-
private constructor(private readonly ds: ILogDatasource) {}
|
|
202
|
-
|
|
203
|
-
// Este es el punto central del adaptador.
|
|
204
|
-
static async create(
|
|
205
|
-
opts: MongoPluginConfig
|
|
206
|
-
): Promise<MongoAdapter | undefined> {
|
|
207
|
-
try {
|
|
208
|
-
const ds = await createMongoDatasource(opts);
|
|
209
|
-
console.log("[logger] Conectado a MongoDB para logs");
|
|
210
|
-
return new MongoAdapter(ds);
|
|
211
|
-
} catch (e: any) {
|
|
212
|
-
console.warn("[logger] MongoDB deshabilitado:", e?.message ?? e);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
get datasource(): ILogDatasource {
|
|
216
|
-
return this.ds;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
---
|
|
222
|
-
|
|
223
|
-
### 2.3. Plugin PostgreSQL (`@jmlq/logger-plugin-postgresql`)
|
|
224
|
-
|
|
225
|
-
Path sugerido: `src/infrastructure/adapters/jmlq/logger/pg/pg.adapter.ts`
|
|
226
|
-
|
|
227
|
-
`PgAdapter` es un **adaptador de infraestructura** cuyo propósito es `crear`, `inicializar` y `exponer` un `datasource` de los logs almacenados en `Postgresal` usando el plugin `@jmlq/logger-plugin-postgresql`.
|
|
228
|
-
|
|
229
|
-
```ts
|
|
230
|
-
// Interfaz estándar que todos los datasources deben implementar para funcionar dentro de @jmlq/logger
|
|
231
|
-
import type { ILogDatasource } from "@jmlq/logger";
|
|
232
|
-
import {
|
|
233
|
-
// Función del plugin que crea realmente el datasource de Postgresql.
|
|
234
|
-
createPostgresDatasource,
|
|
235
|
-
// Tipo de configuración que el plugin requiere
|
|
236
|
-
PostgresPluginConfig,
|
|
237
|
-
} from "@jmlq/logger-plugin-postgresql";
|
|
238
|
-
|
|
239
|
-
export class PgAdapter {
|
|
240
|
-
private constructor(private readonly ds: ILogDatasource) {}
|
|
241
|
-
|
|
242
|
-
// Este es el punto central del adaptador.
|
|
243
|
-
static async create(
|
|
244
|
-
opts: PostgresPluginConfig
|
|
245
|
-
): Promise<PgAdapter | undefined> {
|
|
246
|
-
try {
|
|
247
|
-
const ds = await createPostgresDatasource(opts);
|
|
248
|
-
console.log("[logger] Conectado a PostgreSQL para logs");
|
|
249
|
-
return new PgAdapter(ds);
|
|
250
|
-
} catch (e: any) {
|
|
251
|
-
console.warn("[logger] PostgreSQL deshabilitado:", e?.message ?? e);
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
get datasource(): ILogDatasource {
|
|
256
|
-
return this.ds;
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
### 2.4. Configuración Global @jmlq/Logger
|
|
262
|
-
|
|
263
|
-
Path sugerido: `src/infrastructure/adapters/jmlq/logger/index.ts`
|
|
264
|
-
|
|
265
|
-
Este archivo implementa el **bootstrap global** y singleton del sistema de logging de tu proyecto.
|
|
266
|
-
En términos simples: garantiza que **el logger, sus adapters y sus reglas de PII se inicialicen una sola vez**, sin importar cuántas veces se importe este archivo en distintos módulos.
|
|
267
|
-
|
|
268
|
-
```ts
|
|
269
|
-
import { envs } from "../../../../config/plugins";
|
|
270
|
-
import { LoggerBootstrap } from "./core/logger.bootstrap";
|
|
271
|
-
import { parseLogLevel } from "./helper";
|
|
272
|
-
|
|
273
|
-
// Promesa Singleton
|
|
274
|
-
declare global {
|
|
275
|
-
var __LOGGER_BOOT__: Promise<LoggerBootstrap> | undefined;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// Construye toda la infraestructura del logger
|
|
279
|
-
async function init() {
|
|
280
|
-
// Envía la configuración del logger en envs (se puede usar .env)
|
|
281
|
-
return LoggerBootstrap.create({
|
|
282
|
-
// Nivel mínimo de log
|
|
283
|
-
minLevel: parseLogLevel(envs.logger.LOGGER_LEVEL),
|
|
284
|
-
// Configuración de PII (ocultamiento de datos sensibles)
|
|
285
|
-
pii: {
|
|
286
|
-
// Activación global
|
|
287
|
-
enabled: envs.logger.LOGGER_PII_ENABLED,
|
|
288
|
-
// Incluir reglas de validación declaradas en @jmlq/logger
|
|
289
|
-
includeDefaults: envs.logger.LOGGER_PII_INCLUDE_DEFAULTS,
|
|
290
|
-
// Incluye recursión profunda
|
|
291
|
-
deep: true,
|
|
292
|
-
patterns: [
|
|
293
|
-
// Patrón para ocultar tarjetas
|
|
294
|
-
{
|
|
295
|
-
pattern: "\\b\\d{4}-\\d{4}-\\d{4}-\\d{4}\\b",
|
|
296
|
-
replaceWith: "****-****-****-****",
|
|
297
|
-
},
|
|
298
|
-
// Patrón para ocultar correos
|
|
299
|
-
{
|
|
300
|
-
pattern: "[\\w.-]+@[\\w.-]+",
|
|
301
|
-
replaceWith: "***@***",
|
|
302
|
-
},
|
|
303
|
-
// NOTA: Se pueden agregar mas reglas
|
|
304
|
-
],
|
|
305
|
-
},
|
|
306
|
-
adapters: {
|
|
307
|
-
// NOTA: Opcionales depende de las necesidades del cliente
|
|
308
|
-
// Adapter FS
|
|
309
|
-
fs: envs.logger.LOGGER_FS_PATH
|
|
310
|
-
? {
|
|
311
|
-
// Path donde se guarda el log
|
|
312
|
-
basePath: envs.logger.LOGGER_FS_PATH,
|
|
313
|
-
// Patrón de archivo por fecha
|
|
314
|
-
fileNamePattern: "app-{yyyy}{MM}{dd}.log",
|
|
315
|
-
// Tipo de Rotacion (Generación de un nuevo archivo puede ser por día, peso, etc)
|
|
316
|
-
rotation: { by: "day" },
|
|
317
|
-
// Crear directorio nuevo
|
|
318
|
-
mkdir: true,
|
|
319
|
-
// Darle formato a la trama
|
|
320
|
-
serializer: {
|
|
321
|
-
serialize(log: any) {
|
|
322
|
-
// Serializer personalizado
|
|
323
|
-
return JSON.stringify(log, null, 2);
|
|
324
|
-
},
|
|
325
|
-
},
|
|
326
|
-
// Se ejecuta acción cuando se genera un nuevo archivo (Se podría enviar un email por ejempo)
|
|
327
|
-
onRotate: (oldPath, newPath) => {
|
|
328
|
-
console.log(
|
|
329
|
-
` [Rotate] Rotación completada: ${oldPath.absolutePath} → ${newPath.absolutePath}`
|
|
330
|
-
);
|
|
331
|
-
},
|
|
332
|
-
// Se ejecuta acción cuando se presenta un error
|
|
333
|
-
onError: (err) => {
|
|
334
|
-
console.error(" [Error Handler]", err.message);
|
|
335
|
-
},
|
|
336
|
-
}
|
|
337
|
-
: undefined,
|
|
338
|
-
// Adapter MongoDB
|
|
339
|
-
mongo: envs.logger.LOGGER_MONGO_DB_URL
|
|
340
|
-
? {
|
|
341
|
-
url: envs.logger.LOGGER_MONGO_DB_URL,
|
|
342
|
-
dbName: envs.logger.LOGGER_MONGO_DB_NAME ?? "logs",
|
|
343
|
-
collectionName:
|
|
344
|
-
envs.logger.LOGGER_MONGO_COLLECTION_NAME ?? "app_logs",
|
|
345
|
-
ensureIndexes: true,
|
|
346
|
-
// Tiempo de retención del log
|
|
347
|
-
retentionDays: 7,
|
|
348
|
-
// Crear Index extra (opcional)
|
|
349
|
-
extraIndexes: [{ key: { level: 1 } }],
|
|
350
|
-
// writeOrdered: true,
|
|
351
|
-
createIfMissing: true,
|
|
352
|
-
}
|
|
353
|
-
: undefined,
|
|
354
|
-
// Adapter Postgresql
|
|
355
|
-
pg: envs.logger.LOGGER_PG_CONNECTION_STRING
|
|
356
|
-
? {
|
|
357
|
-
connectionString: envs.logger.LOGGER_PG_CONNECTION_STRING,
|
|
358
|
-
table: envs.logger.LOGGER_PG_TABLE_NAME ?? "app_logs",
|
|
359
|
-
schema: envs.logger.LOGGER_PG_SCHEMA ?? "public",
|
|
360
|
-
createIfMissing: true,
|
|
361
|
-
// Tiempo de retención del log
|
|
362
|
-
retentionDays: 7,
|
|
363
|
-
}
|
|
364
|
-
: undefined,
|
|
365
|
-
//----
|
|
366
|
-
},
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
// Inicializar promesa Singleton
|
|
371
|
-
export const bootReady: Promise<LoggerBootstrap> =
|
|
372
|
-
globalThis.__LOGGER_BOOT__ ?? (globalThis.__LOGGER_BOOT__ = init());
|
|
373
|
-
|
|
374
|
-
// Acceso directo al logger
|
|
375
|
-
export const loggerReady = bootReady.then((b) => b.logger);
|
|
376
|
-
|
|
377
|
-
// Vaciar buffers antes de terminar el proceso
|
|
378
|
-
export async function flushLogs() {
|
|
379
|
-
const boot = await bootReady;
|
|
380
|
-
await boot.flush();
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
// Cerrar recursos
|
|
384
|
-
export async function disposeLogs() {
|
|
385
|
-
const boot = await bootReady;
|
|
386
|
-
await boot.dispose();
|
|
387
|
-
}
|
|
388
|
-
```
|
|
389
|
-
|
|
390
|
-
**NOTA**: `parseLogLevel` es un helper para convertir un `string` en `LogLevel`:
|
|
391
|
-
|
|
392
|
-
```ts
|
|
393
|
-
import { LogLevel } from "@jmlq/logger";
|
|
394
|
-
|
|
395
|
-
export function parseLogLevel(value?: string): LogLevel {
|
|
396
|
-
if (!value) return LogLevel.DEBUG; // fallback seguro
|
|
397
|
-
|
|
398
|
-
const normalized = value.toUpperCase();
|
|
399
|
-
|
|
400
|
-
const map: Record<string, LogLevel> = {
|
|
401
|
-
TRACE: LogLevel.TRACE,
|
|
402
|
-
DEBUG: LogLevel.DEBUG,
|
|
403
|
-
INFO: LogLevel.INFO,
|
|
404
|
-
WARN: LogLevel.WARN,
|
|
405
|
-
ERROR: LogLevel.ERROR,
|
|
406
|
-
FATAL: LogLevel.FATAL,
|
|
407
|
-
};
|
|
408
|
-
|
|
409
|
-
return map[normalized] ?? LogLevel.DEBUG; // fallback en caso de valor inválido
|
|
410
|
-
}
|
|
411
|
-
```
|
|
412
|
-
|
|
413
|
-
---
|
|
414
|
-
|
|
415
|
-
## PASO 3 – INTEGRACIÓN CON EXPRESS
|
|
416
|
-
|
|
417
|
-
### 3.1. Extender `Request` con logger y requestId
|
|
418
|
-
|
|
419
|
-
Path sugerido: `src/types/express.d.ts`
|
|
420
|
-
|
|
421
|
-
```ts
|
|
422
|
-
import "express";
|
|
423
|
-
import type { ILogger } from "@jmlq/logger";
|
|
424
|
-
|
|
425
|
-
// Cada req trae un logger y un requestId.
|
|
426
|
-
declare module "express-serve-static-core" {
|
|
427
|
-
interface Request {
|
|
428
|
-
logger?: ILogger;
|
|
429
|
-
requestId?: string;
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
Esto permite escribir `req.logger` y `req.requestId` con type safety.
|
|
435
|
-
|
|
436
|
-
### 3.2. Crear el servidor y adjuntar el logger
|
|
437
|
-
|
|
438
|
-
Path sugerido: `src/presentation/server.ts`
|
|
439
|
-
|
|
440
|
-
```ts
|
|
441
|
-
import { ILogger } from "@jmlq/logger";
|
|
442
|
-
import express, { Request, Response, NextFunction } from "express";
|
|
443
|
-
import router from "./routes";
|
|
444
|
-
import { randomUUID } from "crypto";
|
|
445
|
-
|
|
446
|
-
// Middleware para adjuntar logger por request
|
|
447
|
-
function attachLogger(base: ILogger) {
|
|
448
|
-
return async (req: Request, _res: Response, next: NextFunction) => {
|
|
449
|
-
req.logger = base;
|
|
450
|
-
req.requestId = (req.headers["x-request-id"] as string) ?? randomUUID();
|
|
451
|
-
next();
|
|
452
|
-
};
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
export function createServer(base: ILogger) {
|
|
456
|
-
const app = express();
|
|
457
|
-
app.use(express.json());
|
|
458
|
-
app.use(attachLogger(base));
|
|
459
|
-
|
|
460
|
-
// Aquí se montan las rutas que llamarán a tus casos de uso
|
|
461
|
-
// <-- los handlers pueden usar req.logger
|
|
462
|
-
|
|
463
|
-
app.get("/health", (_req, res) => res.json({ ok: true }));
|
|
464
|
-
|
|
465
|
-
// IMPORTANTE: Siempre colocar un errorHandler al final
|
|
466
|
-
|
|
467
|
-
return app;
|
|
468
|
-
}
|
|
469
|
-
```
|
|
470
|
-
|
|
471
|
-
En tus controladores, la idea es:
|
|
472
|
-
|
|
473
|
-
```ts
|
|
474
|
-
// controlador de ejemplo (no incluido en este archivo)
|
|
475
|
-
req.logger?.info("user.create.request", {
|
|
476
|
-
requestId: req.requestId,
|
|
477
|
-
// aquí podrías pasar info de la request (sin PII sensible)
|
|
478
|
-
});
|
|
479
|
-
|
|
480
|
-
// aquí llamarías al caso de uso correspondiente (no se implementa en este doc)
|
|
481
|
-
```
|
|
482
|
-
|
|
483
|
-
---
|
|
484
|
-
|
|
485
|
-
## PASO 4 – BOOTSTRAP DE LA APLICACIÓN (Express + logger)
|
|
486
|
-
|
|
487
|
-
Path sugerido: `src/app.ts`
|
|
488
|
-
|
|
489
|
-
Aquí se orquesta todo:
|
|
490
|
-
|
|
491
|
-
```ts
|
|
492
|
-
import { envs } from "./config/plugins";
|
|
493
|
-
import {
|
|
494
|
-
disposeLogs,
|
|
495
|
-
flushLogs,
|
|
496
|
-
loggerReady,
|
|
497
|
-
} from "./infrastructure/adapters/jmlq/logger";
|
|
498
|
-
import { createServer } from "./presentation/server";
|
|
499
|
-
|
|
500
|
-
async function bootstrap() {
|
|
501
|
-
const logger = await loggerReady;
|
|
502
|
-
|
|
503
|
-
const app = createServer(logger);
|
|
504
|
-
const server = app.listen(envs.PORT, envs.HOST, () => {
|
|
505
|
-
console.log(`🚀 Server running at http://${envs.HOST}:${envs.PORT}`);
|
|
506
|
-
logger.info("http.start", { host: envs.HOST, port: envs.PORT });
|
|
507
|
-
});
|
|
508
|
-
|
|
509
|
-
server.on("error", async (err: any) => {
|
|
510
|
-
logger.error("http.listen.error", {
|
|
511
|
-
message: err?.message,
|
|
512
|
-
stack: err?.stack,
|
|
513
|
-
});
|
|
514
|
-
await flushLogs().catch(() => {});
|
|
515
|
-
await disposeLogs().catch(() => {});
|
|
516
|
-
process.exit(1);
|
|
517
|
-
});
|
|
518
|
-
|
|
519
|
-
// Señales de apagado limpio
|
|
520
|
-
const shutdown = async (reason: string, code = 0) => {
|
|
521
|
-
logger.info("app.shutdown.begin", { reason });
|
|
522
|
-
server.close(async () => {
|
|
523
|
-
try {
|
|
524
|
-
await flushLogs();
|
|
525
|
-
} catch {}
|
|
526
|
-
try {
|
|
527
|
-
await disposeLogs();
|
|
528
|
-
} catch {}
|
|
529
|
-
logger.info("app.shutdown.end", { code });
|
|
530
|
-
process.exit(code);
|
|
531
|
-
});
|
|
532
|
-
};
|
|
533
|
-
|
|
534
|
-
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
535
|
-
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
536
|
-
|
|
537
|
-
// Fallos no controlados
|
|
538
|
-
process.on("unhandledRejection", async (err) => {
|
|
539
|
-
const e = err as any;
|
|
540
|
-
logger.error("unhandled.rejection", {
|
|
541
|
-
message: e?.message,
|
|
542
|
-
stack: e?.stack,
|
|
543
|
-
});
|
|
544
|
-
// Aquí se podría enviar una notificación (email, etc.)
|
|
545
|
-
await shutdown("unhandledRejection", 1);
|
|
546
|
-
});
|
|
547
|
-
|
|
548
|
-
process.on("uncaughtException", async (err) => {
|
|
549
|
-
logger.error("uncaught.exception", {
|
|
550
|
-
message: err.message,
|
|
551
|
-
stack: err.stack,
|
|
552
|
-
});
|
|
553
|
-
// Aquí se podría enviar una notificación (email, etc.)
|
|
554
|
-
await shutdown("uncaughtException", 1);
|
|
555
|
-
});
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
bootstrap().catch(async (err) => {
|
|
559
|
-
// Falla durante el bootstrap
|
|
560
|
-
const logger = await loggerReady.catch(() => null);
|
|
561
|
-
if (logger) {
|
|
562
|
-
logger.error("bootstrap.fatal", {
|
|
563
|
-
message: (err as Error)?.message,
|
|
564
|
-
stack: (err as Error)?.stack,
|
|
565
|
-
});
|
|
566
|
-
await flushLogs().catch(() => {});
|
|
567
|
-
await disposeLogs().catch(() => {});
|
|
568
|
-
} else {
|
|
569
|
-
// Último recurso si el logger no llegó a inicializar
|
|
570
|
-
console.error("Fatal error (no logger):", err);
|
|
571
|
-
}
|
|
572
|
-
process.exit(1);
|
|
573
|
-
});
|
|
574
|
-
```
|
|
575
|
-
|
|
576
|
-
Puntos clave:
|
|
577
|
-
|
|
578
|
-
- `loggerReady` asegura que el logger está inicializado antes de levantar HTTP.
|
|
579
|
-
- `flushLogs` y `disposeLogs` se usan:
|
|
580
|
-
- En errores de `server.listen`.
|
|
581
|
-
- En apagado por señales.
|
|
582
|
-
- En fallos no controlados.
|
|
583
|
-
|
|
584
|
-
---
|
|
585
|
-
|
|
586
|
-
### Filtros Avanzados
|
|
587
|
-
|
|
588
|
-
```ts
|
|
589
|
-
// Paginación
|
|
590
|
-
const page1 = await logger.getLogs({ limit: 10, offset: 0 });
|
|
591
|
-
const page2 = await logger.getLogs({ limit: 10, offset: 10 });
|
|
592
|
-
```
|
|
593
|
-
|
|
594
|
-
## 🔧 Configuración con Variables de Entorno
|
|
595
|
-
|
|
596
|
-
```env
|
|
597
|
-
###############################################
|
|
598
|
-
# @jmlq/logger – Dummy Environment Variables
|
|
599
|
-
###############################################
|
|
600
|
-
|
|
601
|
-
# ---------------------------------------------
|
|
602
|
-
# Nivel mínimo del logger
|
|
603
|
-
# Valores: TRACE, DEBUG, INFO, WARN, ERROR, FATAL
|
|
604
|
-
# ---------------------------------------------
|
|
605
|
-
LOG_LEVEL=INFO
|
|
606
|
-
|
|
607
|
-
# ---------------------------------------------
|
|
608
|
-
# Protección de PII (datos sensibles)
|
|
609
|
-
# ---------------------------------------------
|
|
610
|
-
LOGGER_PII_ENABLED=true
|
|
611
|
-
LOGGER_PII_INCLUDE_DEFAULTS=false
|
|
612
|
-
|
|
613
|
-
# ---------------------------------------------
|
|
614
|
-
# FileSystem Plugin
|
|
615
|
-
# ---------------------------------------------
|
|
616
|
-
LOGGER_FS_PATH=./logs
|
|
617
|
-
|
|
618
|
-
# ---------------------------------------------
|
|
619
|
-
# MongoDB Plugin
|
|
620
|
-
# ---------------------------------------------
|
|
621
|
-
LOGGER_MONGO_DB_URL=mongodb://demo_user:demo_pass@localhost:27018/?authSource=admin
|
|
622
|
-
LOGGER_MONGO_DB_NAME=logger_demo_db
|
|
623
|
-
LOGGER_MONGO_COLLECTION_NAME=logger_demo_logs
|
|
624
|
-
|
|
625
|
-
# ---------------------------------------------
|
|
626
|
-
# PostgreSQL Plugin
|
|
627
|
-
# ---------------------------------------------
|
|
628
|
-
LOGGER_PG_CONNECTION_STRING=postgres://demo_user:demo_pass@localhost:5436/logger_demo_db
|
|
629
|
-
LOGGER_PG_TABLE_NAME=logger_demo_table
|
|
630
|
-
LOGGER_PG_SCHEMA=public
|
|
631
|
-
|
|
632
|
-
```
|
|
File without changes
|