@jmlq/logger 0.1.0-alpha.2 → 0.1.0-alpha.4
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 +347 -0
- package/dist/application/use-cases/flush-buffers.d.ts +6 -0
- package/dist/application/use-cases/flush-buffers.js +13 -0
- package/dist/application/use-cases/get-logs.d.ts +7 -0
- package/dist/application/use-cases/get-logs.js +24 -0
- package/dist/application/use-cases/index.d.ts +3 -0
- package/dist/application/use-cases/index.js +19 -0
- package/dist/application/use-cases/save-log.d.ts +13 -0
- package/dist/application/use-cases/save-log.js +30 -0
- package/dist/domain/contracts/index.d.ts +3 -0
- package/dist/domain/contracts/index.js +19 -0
- package/dist/domain/contracts/log.datasource.d.ts +8 -0
- package/dist/{config/interfaces/index.js → domain/contracts/log.datasource.js} +0 -1
- package/dist/domain/contracts/logger.d.ts +19 -0
- package/dist/domain/contracts/logger.js +2 -0
- package/dist/domain/contracts/pii.d.ts +5 -0
- package/dist/domain/contracts/pii.js +2 -0
- package/dist/domain/services/pii-redactor.d.ts +7 -24
- package/dist/domain/services/pii-redactor.js +55 -117
- package/dist/domain/types/index.d.ts +1 -0
- package/dist/{presentation → domain/types}/index.js +1 -1
- package/dist/domain/types/log.types.d.ts +28 -0
- package/dist/domain/types/log.types.js +2 -0
- package/dist/domain/value-objects/index.d.ts +1 -0
- package/dist/domain/{index.js → value-objects/index.js} +1 -1
- package/dist/domain/value-objects/log-level.d.ts +9 -0
- package/dist/domain/value-objects/log-level.js +37 -0
- package/dist/index.d.ts +5 -4
- package/dist/index.js +8 -18
- package/dist/infrastructure/adapters/composite.datasource.d.ts +11 -0
- package/dist/infrastructure/adapters/composite.datasource.js +46 -0
- package/dist/infrastructure/adapters/index.d.ts +1 -0
- package/dist/{config → infrastructure/adapters}/index.js +1 -2
- package/dist/presentation/factory/create-logger.d.ts +2 -0
- package/dist/presentation/factory/create-logger.js +29 -0
- package/dist/presentation/factory/index.d.ts +1 -2
- package/dist/presentation/factory/index.js +15 -72
- package/package.json +14 -2
- package/dist/Composite/index.d.ts +0 -9
- package/dist/Composite/index.js +0 -54
- package/dist/Factory/index.d.ts +0 -5
- package/dist/Factory/index.js +0 -23
- package/dist/config/index.d.ts +0 -2
- package/dist/config/interfaces/index.d.ts +0 -67
- package/dist/config/types/index.d.ts +0 -10
- package/dist/config/types/index.js +0 -13
- package/dist/domain/index.d.ts +0 -1
- package/dist/interfaces/index.d.ts +0 -35
- package/dist/interfaces/index.js +0 -13
- package/dist/presentation/index.d.ts +0 -1
package/README.md
ADDED
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
# @jmlq/logger
|
|
2
|
+
|
|
3
|
+
Paquete de `logging` extensible y desacoplado, diseñado con principios de Arquitectura Limpia.
|
|
4
|
+
Permite registrar logs en múltiples destinos (`archivos`, `MongoDB`, `PostgreSQL`) mediante **plugins** y soporta enmascarado de datos sensibles (PII).
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 📦 Instalación
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
# Instalar el core
|
|
12
|
+
npm i @jmlq/logger
|
|
13
|
+
|
|
14
|
+
# Instalar plugins opcionales según el backend de persistencia
|
|
15
|
+
npm i @jmlq/logger-plugin-fs
|
|
16
|
+
npm i @jmlq/logger-plugin-mongo
|
|
17
|
+
npm i @jmlq/logger-plugin-postgres
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Si usas Mongo o Postgres en tu app cliente, instala además:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm i mongodb@^6.19.0
|
|
25
|
+
npm i pg@^8.16.3
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### DOCUMENTACION
|
|
30
|
+
|
|
31
|
+
> - [`@jmlq/logger-plugin-fs`](https://www.npmjs.com/package/@jmlq/logger-plugin-fs)
|
|
32
|
+
> - [`@jmlq/logger-plugin-mongo`](https://www.npmjs.com/package/@jmlq/logger-plugin-mongo)
|
|
33
|
+
> - [`@jmlq/logger-plugin-postgres`](https://www.npmjs.com/package/@jmlq/logger-plugin-postgres)
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 🧱 Estructura del paquete
|
|
38
|
+
|
|
39
|
+
### 📝 Resumen rápido
|
|
40
|
+
|
|
41
|
+
> - **`src/domain/`** — Reglas del negocio (sin dependencias de frameworks).
|
|
42
|
+
> > - **`value-objects/`**
|
|
43
|
+
> > > - `log-level.ts` — Define niveles (`TRACE…FATAL`) y `toLogLevel()` para convertir strings a nivel.
|
|
44
|
+
> > - **`types/`**
|
|
45
|
+
> > > - `log.types.ts` — Tipos puros del dominio: `ILog`, `IGetLogsFilter`, `PiiOptions`, etc.
|
|
46
|
+
> > - **`contracts/`**
|
|
47
|
+
> > > - `log.datasource.ts` — Puerto que debe implementar cualquier destino de logs (`save/find/flush/dispose`).
|
|
48
|
+
> > > - `logger.ts` — Contrato del logger público (`ILogger`, `ICreateLoggerOptions`).
|
|
49
|
+
> > > - `pii.ts` — Contrato del redactor de PII.
|
|
50
|
+
> > - **`services/`**
|
|
51
|
+
> > > - `pii-redactor.ts` — Enmascarado de datos sensibles (whitelist/blacklist, patrones, modo profundo).
|
|
52
|
+
|
|
53
|
+
> - **`src/application/`** — Orquestación de casos de uso (no depende de infraestructura).
|
|
54
|
+
> > - **`use-cases/`**
|
|
55
|
+
> > > - `save-log.ts` — Aplica `minLevel` + PII y delega a `datasource.save()`.
|
|
56
|
+
> > > - `get-logs.ts` — Recupera logs con filtros/paginación si el datasource lo soporta.
|
|
57
|
+
> > > - `flush-buffers.ts` — Ejecuta `flush()` en el datasource cuando exista.
|
|
58
|
+
|
|
59
|
+
> - **`src/infrastructure/`** — Adaptadores concretos (tecnología).
|
|
60
|
+
> > - **`adapters/`**
|
|
61
|
+
> > > - `composite.datasource.ts` — Fan-out: envía el log a varios datasources y no falla el todo si uno cae (avisa con `console.warn`).
|
|
62
|
+
|
|
63
|
+
> - **`src/presentation/`** — API pública y fábricas (cara del paquete).
|
|
64
|
+
> > - **`factory/`**
|
|
65
|
+
> > > - `create-logger.ts` — Crea el logger listo para usar (`trace…fatal`, `flush`, `dispose`) conectando casos de uso + PII.
|
|
66
|
+
|
|
67
|
+
> - **`src/index.ts`** — Barrel.
|
|
68
|
+
> > - Re-exporta lo público: `createLogger`, `LogLevel`, tipos/contratos y `CompositeDatasource`.
|
|
69
|
+
|
|
70
|
+
### [](./ARQUITECTURA.md)
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## 🧩 Configuración
|
|
75
|
+
|
|
76
|
+
### 🔐 Variables de Entorno (.env)
|
|
77
|
+
|
|
78
|
+
```ini
|
|
79
|
+
# --- MongoDB ---
|
|
80
|
+
MONGO_URL=mongodb://<usuario>:<password>@localhost:27017
|
|
81
|
+
# Dirección de conexión a MongoDB (usuario/contraseña opcionales)
|
|
82
|
+
MONGO_DB_NAME=my_database
|
|
83
|
+
# Nombre de la base de datos donde se guardarán los logs
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
# --- PostgreSQL ---
|
|
87
|
+
POSTGRES_URL=postgresql://<usuario>:<password>@localhost:5432/my_database
|
|
88
|
+
# URL de conexión a la base de datos principal
|
|
89
|
+
POSTGRES_DB=my_database
|
|
90
|
+
# Credenciales y nombre de la base de datos
|
|
91
|
+
|
|
92
|
+
# --- FileSystem ---
|
|
93
|
+
LOGGER_FS_PATH=./logs/app.log
|
|
94
|
+
# Ruta local donde se almacenarán los logs en formato JSONL
|
|
95
|
+
|
|
96
|
+
# --- Nivel de logging ---
|
|
97
|
+
LOGGER_LEVEL=debug
|
|
98
|
+
# Nivel mínimo de logs por entorno (dev: debug, prod: warn)
|
|
99
|
+
|
|
100
|
+
# --- PII (enmascarado de datos sensibles) ---
|
|
101
|
+
LOGGER_PII_ENABLED=true
|
|
102
|
+
# Activa/desactiva el redactor de datos sensibles
|
|
103
|
+
|
|
104
|
+
LOGGER_PII_INCLUDE_DEFAULTS=true
|
|
105
|
+
# Incluye los patrones de PII por defecto además de los definidos por el cliente
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
### 🚀 Uso del paquete
|
|
112
|
+
|
|
113
|
+
El cliente (la aplicación que usa `@jmlq/logger`) es responsable de inicializar el logger, ensamblar los **datasources** (`FS`, `Mongo`, `Postgres`) y configurar el **redactor de PII**.
|
|
114
|
+
|
|
115
|
+
#### 1) Configuración de `pii.ts`
|
|
116
|
+
|
|
117
|
+
Se definen los patrones propios de la aplicación para enmascarar datos sensibles.
|
|
118
|
+
Estos se combinan con los patrones por defecto del core (`LOGGER_PII_INCLUDE_DEFAULTS=true`).
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
// src/config/logger/pii.ts
|
|
122
|
+
|
|
123
|
+
import type { IPiiPattern } from "@jmlq/logger";
|
|
124
|
+
|
|
125
|
+
// Patrones PII propios del cliente
|
|
126
|
+
export const clientPiiPatterns: IPiiPattern[] = [
|
|
127
|
+
{
|
|
128
|
+
// Ejemplo: cédula ecuatoriana (10 dígitos con validaciones)
|
|
129
|
+
regex: /\b\d{10}\b/g,
|
|
130
|
+
replacement: "[REDACTED_CEDULA]",
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
// Ejemplo: token JWT simulado
|
|
134
|
+
regex: /\beyJ[a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+\b/g,
|
|
135
|
+
replacement: "[REDACTED_JWT]",
|
|
136
|
+
},
|
|
137
|
+
];
|
|
138
|
+
|
|
139
|
+
// Claves a redactar siempre (aunque no hagan match con regex)
|
|
140
|
+
export const redactKeys = ["password", "secret", "token"];
|
|
141
|
+
|
|
142
|
+
// Claves a preservar (no se enmascaran aunque coincidan con regex)
|
|
143
|
+
export const preserveKeys = ["city"];
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
#### 2) Inicialización de `logger/index.ts`
|
|
147
|
+
|
|
148
|
+
El logger se ensambla en un archivo central, cargando variables de entorno y adaptadores según disponibilidad:
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
// src/config/logger/index.ts
|
|
152
|
+
|
|
153
|
+
// Importa las funciones y tipos principales del core del logger
|
|
154
|
+
import { createLogger, LogLevel, CompositeDatasource } from "@jmlq/logger";
|
|
155
|
+
|
|
156
|
+
// Importa los plugins disponibles para persistencia de logs
|
|
157
|
+
import { FileSystemDatasource } from "@jmlq/logger-plugin-fs";
|
|
158
|
+
import { MongoDatasource } from "@jmlq/logger-plugin-mongo";
|
|
159
|
+
import {
|
|
160
|
+
connectPostgres,
|
|
161
|
+
ensurePostgresSchema,
|
|
162
|
+
PostgresDatasource,
|
|
163
|
+
} from "@jmlq/logger-plugin-postgres";
|
|
164
|
+
|
|
165
|
+
// Configuración cargada desde variables de entorno (env-var + dotenv)
|
|
166
|
+
import { envs } from "../plugins/envs.plugin";
|
|
167
|
+
|
|
168
|
+
// Patrones propios de PII definidos en src/config/logger/pii.ts
|
|
169
|
+
import { clientPiiPatterns, redactKeys, preserveKeys } from "./pii";
|
|
170
|
+
|
|
171
|
+
// Utilidades de Node.js para manejo de directorios y archivos
|
|
172
|
+
import { mkdirSync, existsSync } from "node:fs";
|
|
173
|
+
import { dirname } from "node:path";
|
|
174
|
+
|
|
175
|
+
// Cliente oficial de MongoDB
|
|
176
|
+
import { MongoClient } from "mongodb";
|
|
177
|
+
|
|
178
|
+
// Se mantiene una referencia global al cliente de MongoDB para cerrarlo en dispose
|
|
179
|
+
let mongoClient: MongoClient | null = null;
|
|
180
|
+
|
|
181
|
+
// Convierte un string (ej. "debug") al enum LogLevel
|
|
182
|
+
function toMinLevel(level: string): LogLevel {
|
|
183
|
+
switch (level.toLowerCase()) {
|
|
184
|
+
case "trace":
|
|
185
|
+
return LogLevel.TRACE;
|
|
186
|
+
case "debug":
|
|
187
|
+
return LogLevel.DEBUG;
|
|
188
|
+
case "info":
|
|
189
|
+
return LogLevel.INFO;
|
|
190
|
+
case "warn":
|
|
191
|
+
return LogLevel.WARN;
|
|
192
|
+
case "error":
|
|
193
|
+
return LogLevel.ERROR;
|
|
194
|
+
case "fatal":
|
|
195
|
+
return LogLevel.FATAL;
|
|
196
|
+
default:
|
|
197
|
+
return LogLevel.INFO; // Valor por defecto
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Asegura que exista el directorio de logs para el datasource de FileSystem
|
|
202
|
+
function ensureDirFor(filePath: string) {
|
|
203
|
+
const dir = dirname(filePath);
|
|
204
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Inicializa el logger ensamblando los datasources configurados en .env
|
|
208
|
+
async function initLogger() {
|
|
209
|
+
const datasources = [];
|
|
210
|
+
|
|
211
|
+
// --- FileSystem ---
|
|
212
|
+
// Si está definido LOGGER_FS_PATH, se usa un datasource local de archivos
|
|
213
|
+
if (envs.logger.LOGGER_FS_PATH) {
|
|
214
|
+
ensureDirFor(envs.logger.LOGGER_FS_PATH);
|
|
215
|
+
datasources.push(
|
|
216
|
+
new FileSystemDatasource({ filePath: envs.logger.LOGGER_FS_PATH })
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// --- MongoDB ---
|
|
221
|
+
// Si están configurados MONGO_URL y MONGO_DB_NAME, se conecta y usa la colección logs
|
|
222
|
+
if (envs.logger.MONGO_URL && envs.logger.MONGO_DB_NAME) {
|
|
223
|
+
mongoClient = new MongoClient(envs.logger.MONGO_URL);
|
|
224
|
+
await mongoClient.connect();
|
|
225
|
+
const coll = mongoClient.db(envs.logger.MONGO_DB_NAME).collection("logs");
|
|
226
|
+
datasources.push(new MongoDatasource(coll));
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// --- PostgreSQL ---
|
|
230
|
+
// Si está configurado POSTGRES_URL, se conecta, asegura la tabla y crea el datasource
|
|
231
|
+
if (envs.logger.POSTGRES_URL) {
|
|
232
|
+
await connectPostgres(envs.logger.POSTGRES_URL);
|
|
233
|
+
await ensurePostgresSchema();
|
|
234
|
+
datasources.push(new PostgresDatasource("logs"));
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Si hay más de un datasource, se compone con CompositeDatasource (fan-out)
|
|
238
|
+
const datasource =
|
|
239
|
+
datasources.length === 1
|
|
240
|
+
? datasources[0]
|
|
241
|
+
: new CompositeDatasource(datasources);
|
|
242
|
+
|
|
243
|
+
// Crea y retorna el logger con nivel mínimo y configuración de PII
|
|
244
|
+
return createLogger(datasource, {
|
|
245
|
+
minLevel: toMinLevel(envs.logger.LOGGER_LEVEL),
|
|
246
|
+
pii: {
|
|
247
|
+
enabled: envs.logger.LOGGER_PII_ENABLED,
|
|
248
|
+
includeDefaultPatterns: envs.logger.LOGGER_PII_INCLUDE_DEFAULTS,
|
|
249
|
+
patterns: clientPiiPatterns,
|
|
250
|
+
redactKeys,
|
|
251
|
+
preserveKeys,
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Exporta el logger como una Promise porque la inicialización es async
|
|
257
|
+
export const loggerReady = initLogger();
|
|
258
|
+
|
|
259
|
+
// --- Funciones de utilidad ---
|
|
260
|
+
|
|
261
|
+
// Fuerza un flush() de todos los datasources, útil para apagar servicios con logs pendientes
|
|
262
|
+
export async function flushLogs() {
|
|
263
|
+
const logger: any = await loggerReady;
|
|
264
|
+
if (typeof logger.flush === "function") await logger.flush();
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Cierra conexiones abiertas (ej. MongoClient) y libera recursos
|
|
268
|
+
export async function disposeLogs() {
|
|
269
|
+
const logger: any = await loggerReady;
|
|
270
|
+
if (typeof logger.dispose === "function") await logger.dispose();
|
|
271
|
+
if (mongoClient) await mongoClient.close();
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
#### 3) Uso desde cualquier aplicación Node.js
|
|
276
|
+
|
|
277
|
+
En cualquier parte de la app se puede usar el logger así:
|
|
278
|
+
|
|
279
|
+
```ts
|
|
280
|
+
import { loggerReady } from "./config/logger";
|
|
281
|
+
|
|
282
|
+
async function main() {
|
|
283
|
+
const logger = await loggerReady;
|
|
284
|
+
|
|
285
|
+
await logger.info("Aplicación iniciada", { pid: process.pid });
|
|
286
|
+
|
|
287
|
+
await logger.error("Error en proceso", {
|
|
288
|
+
password: "123456", // será redactado
|
|
289
|
+
city: "Quito", // se preserva
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
main();
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### 🔎 Notas importantes
|
|
297
|
+
|
|
298
|
+
> - `loggerReady` es una Promise → debe resolverse con `await`.
|
|
299
|
+
> - `flushLogs()` y `disposeLogs()` deben usarse en procesos que cierran conexiones (ej. `SIGINT`, `SIGTERM`).
|
|
300
|
+
> - Los patrones definidos en `pii.ts` se combinan con los patrones por defecto cuando `LOGGER_PII_INCLUDE_DEFAULTS=true`.
|
|
301
|
+
> - El logger puede trabajar con **un único datasource** o con **CompositeDatasource** para múltiples.
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
### 🧪 Escenarios
|
|
306
|
+
|
|
307
|
+
> - **Solo FS** → logs locales en `./logs/app.log`.
|
|
308
|
+
> - **Solo MongoDB** → logs en colección `logs`.
|
|
309
|
+
> - **Solo PostgreSQL** → logs en tabla `logs`.
|
|
310
|
+
> - **Combinado** → fan-out a varios destinos simultáneamente.
|
|
311
|
+
> - **Extensión** → implementar `ILogDatasource`.
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
## 🧯 Troubleshooting
|
|
316
|
+
|
|
317
|
+
> - **No se inicializa ningún datasource** → definir al menos una variable (`LOGGER_FS_PATH`, `MONGO_URL`, `POSTGRES_URL`).
|
|
318
|
+
> - **MongoDB Auth** → incluir `authSource=admin` en la URL si se usan usuarios root.
|
|
319
|
+
> - **Postgres** → ejecutar `ensurePostgresSchema()` para crear tabla logs si no existe.
|
|
320
|
+
> - **Alto volumen de logs** → implementar `flush()` o batching en el datasource.
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## 🧪 Tests
|
|
325
|
+
|
|
326
|
+
```ts
|
|
327
|
+
import { createLogger, LogLevel } from "@jmlq/logger";
|
|
328
|
+
import { FileSystemDatasource } from "@jmlq/logger-plugin-fs";
|
|
329
|
+
|
|
330
|
+
test("logger redacta PII en FS", async () => {
|
|
331
|
+
const fsDs = new FileSystemDatasource({ filePath: "./logs/test.log" });
|
|
332
|
+
const logger = createLogger(fsDs, {
|
|
333
|
+
minLevel: LogLevel.DEBUG,
|
|
334
|
+
pii: { enabled: true },
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
await logger.info("Inicio de sesión", { user: "demo", password: "123456" });
|
|
338
|
+
|
|
339
|
+
// Luego verificar que el archivo test.log no contiene la contraseña en claro
|
|
340
|
+
});
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
## 📄 Licencia
|
|
346
|
+
|
|
347
|
+
MIT © Mauricio Lahuasi
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FlushBuffers = void 0;
|
|
4
|
+
class FlushBuffers {
|
|
5
|
+
constructor(ds) {
|
|
6
|
+
this.ds = ds;
|
|
7
|
+
}
|
|
8
|
+
async execute() {
|
|
9
|
+
// flush es opcional; si no está implementado, no hace nada
|
|
10
|
+
await this.ds.flush?.();
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
exports.FlushBuffers = FlushBuffers;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GetLogs = void 0;
|
|
4
|
+
class GetLogs {
|
|
5
|
+
constructor(ds) {
|
|
6
|
+
this.ds = ds;
|
|
7
|
+
}
|
|
8
|
+
async execute(filter) {
|
|
9
|
+
if (!this.ds.find)
|
|
10
|
+
return []; // si el datasource no lo soporta, retorna vacío
|
|
11
|
+
// Sanitiza límites (evita valores negativos o absurdos)
|
|
12
|
+
const safe = filter
|
|
13
|
+
? {
|
|
14
|
+
...filter,
|
|
15
|
+
limit: filter.limit && filter.limit > 0
|
|
16
|
+
? Math.min(filter.limit, 5000)
|
|
17
|
+
: undefined,
|
|
18
|
+
offset: filter.offset && filter.offset >= 0 ? filter.offset : undefined,
|
|
19
|
+
}
|
|
20
|
+
: undefined;
|
|
21
|
+
return this.ds.find(safe);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.GetLogs = GetLogs;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./save-log"), exports);
|
|
18
|
+
__exportStar(require("./get-logs"), exports);
|
|
19
|
+
__exportStar(require("./flush-buffers"), exports);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ILogDatasource, IPiiRedactor } from "../../domain/contracts";
|
|
2
|
+
import { LogMessage } from "../../domain/types";
|
|
3
|
+
import { LogLevel } from "../../domain/value-objects";
|
|
4
|
+
export interface SaveLogDeps {
|
|
5
|
+
ds: ILogDatasource;
|
|
6
|
+
minLevel: LogLevel;
|
|
7
|
+
redactor: IPiiRedactor;
|
|
8
|
+
}
|
|
9
|
+
export declare class SaveLog {
|
|
10
|
+
private readonly deps;
|
|
11
|
+
constructor(deps: SaveLogDeps);
|
|
12
|
+
execute(level: LogLevel, message: LogMessage, meta?: unknown): Promise<void>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SaveLog = void 0;
|
|
4
|
+
// Normaliza el mensaje: si es función, la evalúa; si es objeto, se redacta; si es string, se redacta
|
|
5
|
+
function normalizeMessage(message, redactor) {
|
|
6
|
+
const resolved = typeof message === "function" ? message() : message; // eval laziness
|
|
7
|
+
return redactor.redact(resolved);
|
|
8
|
+
}
|
|
9
|
+
class SaveLog {
|
|
10
|
+
constructor(deps) {
|
|
11
|
+
this.deps = deps;
|
|
12
|
+
}
|
|
13
|
+
async execute(level, message, meta) {
|
|
14
|
+
// 1) Filtro por nivel (evita hacer trabajo innecesario)
|
|
15
|
+
if (level < this.deps.minLevel)
|
|
16
|
+
return; // no se loggea
|
|
17
|
+
// 2) Normalización + PII
|
|
18
|
+
const normalized = normalizeMessage(message, this.deps.redactor);
|
|
19
|
+
// 3) Construcción del evento
|
|
20
|
+
const log = {
|
|
21
|
+
level,
|
|
22
|
+
message: normalized,
|
|
23
|
+
meta: meta === undefined ? undefined : this.deps.redactor.redact(meta),
|
|
24
|
+
timestamp: Date.now(),
|
|
25
|
+
};
|
|
26
|
+
// 4) Persistencia (fan-out lo maneja el ds si es composite)
|
|
27
|
+
await this.deps.ds.save(log);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.SaveLog = SaveLog;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./log.datasource"), exports);
|
|
18
|
+
__exportStar(require("./logger"), exports);
|
|
19
|
+
__exportStar(require("./pii"), exports);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ILogDatasource } from ".";
|
|
2
|
+
import { PiiOptions } from "../types";
|
|
3
|
+
import { LogLevel } from "../value-objects";
|
|
4
|
+
export interface ICreateLoggerOptions {
|
|
5
|
+
minLevel?: LogLevel;
|
|
6
|
+
redactPII?: boolean;
|
|
7
|
+
pii?: PiiOptions;
|
|
8
|
+
}
|
|
9
|
+
export interface ILogger {
|
|
10
|
+
trace: (message: unknown, meta?: unknown) => void | Promise<void>;
|
|
11
|
+
debug: (message: unknown, meta?: unknown) => void | Promise<void>;
|
|
12
|
+
info: (message: unknown, meta?: unknown) => void | Promise<void>;
|
|
13
|
+
warn: (message: unknown, meta?: unknown) => void | Promise<void>;
|
|
14
|
+
error: (message: unknown, meta?: unknown) => void | Promise<void>;
|
|
15
|
+
fatal: (message: unknown, meta?: unknown) => void | Promise<void>;
|
|
16
|
+
flush?: () => Promise<void>;
|
|
17
|
+
dispose?: () => Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
export type { ILogDatasource as LogDatasourcePort };
|
|
@@ -1,27 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
* - Controlar redacción por nombre de clave (whitelist/blacklist).
|
|
8
|
-
*/
|
|
9
|
-
import { IPiiPattern, IPiiRedactorOptions } from "../../config";
|
|
10
|
-
export declare class PiiRedactor {
|
|
11
|
-
private readonly enabled;
|
|
12
|
-
private readonly patterns;
|
|
13
|
-
private readonly redactKeys;
|
|
14
|
-
private readonly preserveKeys;
|
|
15
|
-
constructor(opts?: IPiiRedactorOptions);
|
|
16
|
-
/** Patrones por defecto (seguros y genéricos). Ajusta según tu dominio/región si lo deseas. */
|
|
17
|
-
static defaultPatterns(): IPiiPattern[];
|
|
18
|
-
/** Punto de entrada público: redacción deep de cualquier estructura */
|
|
1
|
+
import { IPiiRedactor } from "../contracts";
|
|
2
|
+
import { PiiOptions } from "../types";
|
|
3
|
+
export declare class PiiRedactor implements IPiiRedactor {
|
|
4
|
+
private options;
|
|
5
|
+
constructor(options?: PiiOptions);
|
|
6
|
+
updateOptions(opts: PiiOptions): void;
|
|
19
7
|
redact<T = unknown>(value: T): T;
|
|
20
|
-
/** === Internos === */
|
|
21
|
-
private redactValue;
|
|
22
8
|
private applyPatterns;
|
|
23
|
-
private
|
|
24
|
-
private isPreservedKey;
|
|
25
|
-
private keyRedactionReplacement;
|
|
26
|
-
private clone;
|
|
9
|
+
private redactDeep;
|
|
27
10
|
}
|