@jmlq/logger 0.1.0-alpha.3 → 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 +37 -37
- 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
CHANGED
|
@@ -18,6 +18,14 @@ npm i @jmlq/logger-plugin-postgres
|
|
|
18
18
|
|
|
19
19
|
```
|
|
20
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
|
+
|
|
21
29
|
### DOCUMENTACION
|
|
22
30
|
|
|
23
31
|
> - [`@jmlq/logger-plugin-fs`](https://www.npmjs.com/package/@jmlq/logger-plugin-fs)
|
|
@@ -28,33 +36,38 @@ npm i @jmlq/logger-plugin-postgres
|
|
|
28
36
|
|
|
29
37
|
## 🧱 Estructura del paquete
|
|
30
38
|
|
|
31
|
-
|
|
32
|
-
src/
|
|
33
|
-
composite/
|
|
34
|
-
index.ts # Implementa CompositeDatasource para fan-out de logs a múltiples destinos
|
|
35
|
-
config/
|
|
36
|
-
interfaces/
|
|
37
|
-
index.ts # Define contratos/puertos (ILogDatasource, ILogRepository)
|
|
38
|
-
types/
|
|
39
|
-
index.ts # Tipos y utilidades comunes (filtros de logs, opciones de logger, etc.)
|
|
40
|
-
domain/
|
|
41
|
-
services/
|
|
42
|
-
index.ts # Servicio PiiRedactor: enmascara datos sensibles
|
|
43
|
-
presentation/
|
|
44
|
-
factory/
|
|
45
|
-
index.ts # Factory createLogger: construye la API final del logger
|
|
46
|
-
index.ts # Punto de entrada que re-exporta contratos, utilidades y createLogger
|
|
39
|
+
### 📝 Resumen rápido
|
|
47
40
|
|
|
48
|
-
|
|
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`).
|
|
49
62
|
|
|
50
|
-
|
|
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.
|
|
51
66
|
|
|
52
|
-
> -
|
|
53
|
-
> -
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
> - **presentation/factory** → `createLogger`, expone la API (`trace`, `debug`, `info`, `warn`, `error`, `fatal`).
|
|
57
|
-
> - **index.ts** → Punto de entrada limpio para el consumidor.
|
|
67
|
+
> - **`src/index.ts`** — Barrel.
|
|
68
|
+
> > - Re-exporta lo público: `createLogger`, `LogLevel`, tipos/contratos y `CompositeDatasource`.
|
|
69
|
+
|
|
70
|
+
### [](./ARQUITECTURA.md)
|
|
58
71
|
|
|
59
72
|
---
|
|
60
73
|
|
|
@@ -280,19 +293,6 @@ async function main() {
|
|
|
280
293
|
main();
|
|
281
294
|
```
|
|
282
295
|
|
|
283
|
-
### 📦 Dependencias adicionales en el cliente
|
|
284
|
-
|
|
285
|
-
```bash
|
|
286
|
-
# Si se va a usar MongoDB
|
|
287
|
-
npm i mongodb@^6.19.0
|
|
288
|
-
|
|
289
|
-
# Si se va a usar PostgreSQL
|
|
290
|
-
npm i pg@^8.16.3
|
|
291
|
-
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
`mongodb` y `pg` son **peerDependencies**: `@jmlq/logger` no las instala por sí mismo para mantener el core desacoplado.
|
|
295
|
-
|
|
296
296
|
### 🔎 Notas importantes
|
|
297
297
|
|
|
298
298
|
> - `loggerReady` es una Promise → debe resolverse con `await`.
|
|
@@ -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
|
}
|
|
@@ -1,139 +1,77 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Domain Service: PiiRedactor
|
|
4
|
-
*
|
|
5
|
-
* Propósito:
|
|
6
|
-
* - Redactar/anonimizar PII en strings, objetos y arrays (deep).
|
|
7
|
-
* - Permitir que el cliente pase patrones adicionales/propios.
|
|
8
|
-
* - Controlar redacción por nombre de clave (whitelist/blacklist).
|
|
9
|
-
*/
|
|
10
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
3
|
exports.PiiRedactor = void 0;
|
|
4
|
+
// Utilidad local: crea RegExp segura desde {pattern, flags}
|
|
5
|
+
function toRegex(p) {
|
|
6
|
+
// new RegExp lanza si el patrón es inválido → try/catch para robustez
|
|
7
|
+
try {
|
|
8
|
+
return new RegExp(p.pattern, p.flags ?? "g");
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return /$a/; /* patrón imposible */
|
|
12
|
+
}
|
|
13
|
+
}
|
|
12
14
|
class PiiRedactor {
|
|
13
|
-
constructor(
|
|
14
|
-
|
|
15
|
-
this.enabled = enabled;
|
|
16
|
-
this.patterns = includeDefaultPatterns
|
|
17
|
-
? [...PiiRedactor.defaultPatterns(), ...patterns]
|
|
18
|
-
: [...patterns];
|
|
19
|
-
this.redactKeys = redactKeys;
|
|
20
|
-
this.preserveKeys = preserveKeys;
|
|
15
|
+
constructor(options = {}) {
|
|
16
|
+
this.options = options;
|
|
21
17
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
// Tarjetas (13–19 dígitos con espacios o guiones) — patrón simple
|
|
26
|
-
{
|
|
27
|
-
name: "credit_card",
|
|
28
|
-
regex: /\b(?:\d[ -]*?){13,19}\b/g,
|
|
29
|
-
replacement: "[REDACTED_CARD]",
|
|
30
|
-
},
|
|
31
|
-
// Emails
|
|
32
|
-
{
|
|
33
|
-
name: "email",
|
|
34
|
-
regex: /\b[\w._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g,
|
|
35
|
-
replacement: "[REDACTED_EMAIL]",
|
|
36
|
-
},
|
|
37
|
-
// Teléfonos: 10+ dígitos (ajusta a tu región si hace falta)
|
|
38
|
-
{
|
|
39
|
-
name: "phone",
|
|
40
|
-
regex: /\b\d{10,}\b/g,
|
|
41
|
-
replacement: "[REDACTED_PHONE]",
|
|
42
|
-
},
|
|
43
|
-
// Documento genérico: ABC-123456… o similar
|
|
44
|
-
{
|
|
45
|
-
name: "document_id",
|
|
46
|
-
regex: /\b[A-Z]{1,3}-?\d{6,10}\b/g,
|
|
47
|
-
replacement: "[REDACTED_ID]",
|
|
48
|
-
},
|
|
49
|
-
// IPv4
|
|
50
|
-
{
|
|
51
|
-
name: "ipv4",
|
|
52
|
-
regex: /\b(?:(?:25[0-5]|2[0-4]\d|1?\d?\d)(?:\.|$)){4}\b/g,
|
|
53
|
-
replacement: "[REDACTED_IP]",
|
|
54
|
-
},
|
|
55
|
-
];
|
|
18
|
+
updateOptions(opts) {
|
|
19
|
+
// merge superficial de opciones; no muta referencias externas
|
|
20
|
+
this.options = { ...this.options, ...opts };
|
|
56
21
|
}
|
|
57
|
-
/** Punto de entrada público: redacción deep de cualquier estructura */
|
|
58
22
|
redact(value) {
|
|
59
|
-
if (!this.enabled)
|
|
23
|
+
if (!this.options.enabled)
|
|
24
|
+
return value; // early exit si PII apagado
|
|
25
|
+
if (value == null)
|
|
60
26
|
return value;
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
/** === Internos === */
|
|
64
|
-
redactValue(value, keyPath) {
|
|
65
|
-
// Strings → aplicar patrones
|
|
27
|
+
// Si es string → aplicar patrones
|
|
66
28
|
if (typeof value === "string") {
|
|
67
|
-
return this.applyPatterns(value
|
|
68
|
-
}
|
|
69
|
-
// Números / boolean / null / undefined → retornar tal cual
|
|
70
|
-
if (typeof value === "number" ||
|
|
71
|
-
typeof value === "boolean" ||
|
|
72
|
-
value === null ||
|
|
73
|
-
typeof value === "undefined") {
|
|
74
|
-
return value;
|
|
75
|
-
}
|
|
76
|
-
// Arrays → deep
|
|
77
|
-
if (Array.isArray(value)) {
|
|
78
|
-
return value.map((v, i) => this.redactValue(v, [...keyPath, String(i)]));
|
|
29
|
+
return this.applyPatterns(value);
|
|
79
30
|
}
|
|
80
|
-
//
|
|
31
|
+
// Si es objeto/array → redacción profunda si corresponde
|
|
81
32
|
if (typeof value === "object") {
|
|
82
|
-
|
|
83
|
-
const out = {};
|
|
84
|
-
for (const [k, v] of Object.entries(src)) {
|
|
85
|
-
const path = [...keyPath, k];
|
|
86
|
-
if (this.isPreservedKey(k)) {
|
|
87
|
-
out[k] = this.clone(v); // explícitamente preservado
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
if (this.isRedactedKey(k)) {
|
|
91
|
-
out[k] = this.keyRedactionReplacement(k, path);
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
|
-
out[k] = this.redactValue(v, path);
|
|
95
|
-
}
|
|
96
|
-
return out;
|
|
33
|
+
return this.redactDeep(value);
|
|
97
34
|
}
|
|
98
|
-
//
|
|
35
|
+
// Otros tipos (number, boolean, function) no se redactan
|
|
99
36
|
return value;
|
|
100
37
|
}
|
|
101
|
-
applyPatterns(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
else {
|
|
110
|
-
result = result.replace(p.regex, replacement);
|
|
111
|
-
}
|
|
38
|
+
applyPatterns(input) {
|
|
39
|
+
const patterns = this.options.patterns ?? [];
|
|
40
|
+
let out = input;
|
|
41
|
+
for (const p of patterns) {
|
|
42
|
+
const rx = toRegex(p);
|
|
43
|
+
// replace con patrón RegExp
|
|
44
|
+
out = out.replace(rx, p.replaceWith);
|
|
112
45
|
}
|
|
113
|
-
return
|
|
114
|
-
}
|
|
115
|
-
isRedactedKey(key) {
|
|
116
|
-
return this.redactKeys.some((r) => typeof r === "string" ? r === key : r.test(key));
|
|
46
|
+
return out;
|
|
117
47
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
48
|
+
redactDeep(obj) {
|
|
49
|
+
// Evita mutar el objeto original → copia superficial
|
|
50
|
+
const clone = Array.isArray(obj)
|
|
51
|
+
? [...obj]
|
|
52
|
+
: { ...obj };
|
|
53
|
+
const { whitelistKeys = [], blacklistKeys = [], deep = true, } = this.options;
|
|
54
|
+
for (const key of Object.keys(clone)) {
|
|
55
|
+
const val = clone[key];
|
|
56
|
+
// Si está en whitelist, se respeta siempre
|
|
57
|
+
if (whitelistKeys.includes(key))
|
|
58
|
+
continue;
|
|
59
|
+
// Si está en blacklist y es string → reemplazar completamente
|
|
60
|
+
if (blacklistKeys.includes(key)) {
|
|
61
|
+
clone[key] = "[REDACTED]"; // fuerza redacción total de claves sensibles
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
// Para strings comunes → aplicar patrones
|
|
65
|
+
if (typeof val === "string") {
|
|
66
|
+
clone[key] = this.applyPatterns(val);
|
|
67
|
+
continue;
|
|
128
68
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
return [...v];
|
|
133
|
-
return { ...v };
|
|
69
|
+
// Para objetos/arrays y deep===true → recursión
|
|
70
|
+
if (deep && val && typeof val === "object") {
|
|
71
|
+
clone[key] = this.redactDeep(val);
|
|
134
72
|
}
|
|
135
73
|
}
|
|
136
|
-
return
|
|
74
|
+
return clone;
|
|
137
75
|
}
|
|
138
76
|
}
|
|
139
77
|
exports.PiiRedactor = PiiRedactor;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./log.types";
|
|
@@ -14,4 +14,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./
|
|
17
|
+
__exportStar(require("./log.types"), exports);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { LogLevel } from "../value-objects";
|
|
2
|
+
export type LogMessage = string | Record<string, unknown> | (() => string | Record<string, unknown>);
|
|
3
|
+
export interface ILog {
|
|
4
|
+
level: LogLevel;
|
|
5
|
+
message: string | Record<string, unknown>;
|
|
6
|
+
meta?: unknown;
|
|
7
|
+
timestamp: number;
|
|
8
|
+
}
|
|
9
|
+
export interface IGetLogsFilter {
|
|
10
|
+
levelMin?: LogLevel;
|
|
11
|
+
since?: number;
|
|
12
|
+
until?: number;
|
|
13
|
+
limit?: number;
|
|
14
|
+
offset?: number;
|
|
15
|
+
query?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface PiiReplacement {
|
|
18
|
+
pattern: string;
|
|
19
|
+
flags?: string;
|
|
20
|
+
replaceWith: string;
|
|
21
|
+
}
|
|
22
|
+
export interface PiiOptions {
|
|
23
|
+
enabled?: boolean;
|
|
24
|
+
whitelistKeys?: string[];
|
|
25
|
+
blacklistKeys?: string[];
|
|
26
|
+
patterns?: PiiReplacement[];
|
|
27
|
+
deep?: boolean;
|
|
28
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./log-level";
|
|
@@ -14,4 +14,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./
|
|
17
|
+
__exportStar(require("./log-level"), exports);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LogLevel = void 0;
|
|
4
|
+
exports.toLogLevel = toLogLevel;
|
|
5
|
+
var LogLevel;
|
|
6
|
+
(function (LogLevel) {
|
|
7
|
+
// Orden creciente permite comparar por severidad (trace < debug < ...)
|
|
8
|
+
LogLevel[LogLevel["TRACE"] = 10] = "TRACE";
|
|
9
|
+
LogLevel[LogLevel["DEBUG"] = 20] = "DEBUG";
|
|
10
|
+
LogLevel[LogLevel["INFO"] = 30] = "INFO";
|
|
11
|
+
LogLevel[LogLevel["WARN"] = 40] = "WARN";
|
|
12
|
+
LogLevel[LogLevel["ERROR"] = 50] = "ERROR";
|
|
13
|
+
LogLevel[LogLevel["FATAL"] = 60] = "FATAL";
|
|
14
|
+
})(LogLevel || (exports.LogLevel = LogLevel = {}));
|
|
15
|
+
// Convierte string a LogLevel con fallback seguro
|
|
16
|
+
function toLogLevel(level, def = LogLevel.INFO) {
|
|
17
|
+
if (typeof level === "number")
|
|
18
|
+
return (Object.values(LogLevel).includes(level) ? level : def);
|
|
19
|
+
if (!level)
|
|
20
|
+
return def;
|
|
21
|
+
switch (String(level).toLowerCase()) {
|
|
22
|
+
case "trace":
|
|
23
|
+
return LogLevel.TRACE;
|
|
24
|
+
case "debug":
|
|
25
|
+
return LogLevel.DEBUG;
|
|
26
|
+
case "info":
|
|
27
|
+
return LogLevel.INFO;
|
|
28
|
+
case "warn":
|
|
29
|
+
return LogLevel.WARN;
|
|
30
|
+
case "error":
|
|
31
|
+
return LogLevel.ERROR;
|
|
32
|
+
case "fatal":
|
|
33
|
+
return LogLevel.FATAL;
|
|
34
|
+
default:
|
|
35
|
+
return def; // evita lanzar excepción por valores inesperados
|
|
36
|
+
}
|
|
37
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
export
|
|
4
|
-
export
|
|
1
|
+
export { LogLevel, toLogLevel } from "./domain/value-objects";
|
|
2
|
+
export type { LogMessage, ILog, IGetLogsFilter, PiiOptions, PiiReplacement, } from "./domain/types";
|
|
3
|
+
export type { LogDatasourcePort, ILogger, ICreateLoggerOptions, ILogDatasource, } from "./domain/contracts";
|
|
4
|
+
export { createLogger } from "./presentation/factory";
|
|
5
|
+
export { CompositeDatasource } from "./infrastructure/adapters";
|
package/dist/index.js
CHANGED
|
@@ -1,20 +1,10 @@
|
|
|
1
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
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
3
|
+
exports.CompositeDatasource = exports.createLogger = exports.toLogLevel = exports.LogLevel = void 0;
|
|
4
|
+
var value_objects_1 = require("./domain/value-objects");
|
|
5
|
+
Object.defineProperty(exports, "LogLevel", { enumerable: true, get: function () { return value_objects_1.LogLevel; } });
|
|
6
|
+
Object.defineProperty(exports, "toLogLevel", { enumerable: true, get: function () { return value_objects_1.toLogLevel; } });
|
|
7
|
+
var factory_1 = require("./presentation/factory");
|
|
8
|
+
Object.defineProperty(exports, "createLogger", { enumerable: true, get: function () { return factory_1.createLogger; } });
|
|
9
|
+
var adapters_1 = require("./infrastructure/adapters");
|
|
10
|
+
Object.defineProperty(exports, "CompositeDatasource", { enumerable: true, get: function () { return adapters_1.CompositeDatasource; } });
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ILogDatasource } from "../../domain/contracts";
|
|
2
|
+
import { IGetLogsFilter, ILog } from "../../domain/types";
|
|
3
|
+
export declare class CompositeDatasource implements ILogDatasource {
|
|
4
|
+
private readonly targets;
|
|
5
|
+
readonly name = "composite";
|
|
6
|
+
constructor(targets: ILogDatasource[]);
|
|
7
|
+
save(log: ILog): Promise<void>;
|
|
8
|
+
find(filter?: IGetLogsFilter): Promise<ILog[]>;
|
|
9
|
+
flush(): Promise<void>;
|
|
10
|
+
dispose(): Promise<void>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CompositeDatasource = void 0;
|
|
4
|
+
// CompositeDatasource: fan-out hacia múltiples datasources
|
|
5
|
+
class CompositeDatasource {
|
|
6
|
+
constructor(targets) {
|
|
7
|
+
this.targets = targets;
|
|
8
|
+
this.name = "composite";
|
|
9
|
+
// Filtro targets válidos (defensivo). Evita null/undefined
|
|
10
|
+
this.targets = (targets ?? []).filter(Boolean);
|
|
11
|
+
}
|
|
12
|
+
async save(log) {
|
|
13
|
+
// Ejecuta todos en paralelo y NO falla el todo si uno falla
|
|
14
|
+
const results = await Promise.allSettled(this.targets.map((ds) => ds.save(log)));
|
|
15
|
+
// Log de errores silencioso (el logger real no debe auto-dependerse)
|
|
16
|
+
for (let i = 0; i < results.length; i++) {
|
|
17
|
+
const r = results[i];
|
|
18
|
+
if (r.status === "rejected") {
|
|
19
|
+
// Preferible: exponer un hook externo; aquí solo console.warn por simplicidad
|
|
20
|
+
// eslint-disable-next-line no-console
|
|
21
|
+
console.warn(`[CompositeDatasource] save error in ds#${i} (${this.targets[i]?.name ?? "unknown"})`, r.reason);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
async find(filter) {
|
|
26
|
+
// Si varios ds soportan find, se concatena y ordena por timestamp desc
|
|
27
|
+
const results = await Promise.allSettled(this.targets.map((ds) => ds.find?.(filter)));
|
|
28
|
+
const logs = [];
|
|
29
|
+
for (const r of results) {
|
|
30
|
+
if (r.status === "fulfilled" && Array.isArray(r.value))
|
|
31
|
+
logs.push(...r.value);
|
|
32
|
+
}
|
|
33
|
+
// Orden descendente por timestamp (más recientes primero)
|
|
34
|
+
logs.sort((a, b) => b.timestamp - a.timestamp);
|
|
35
|
+
return logs;
|
|
36
|
+
}
|
|
37
|
+
async flush() {
|
|
38
|
+
// Ejecuta flush donde esté disponible
|
|
39
|
+
await Promise.allSettled(this.targets.map((ds) => ds.flush?.()));
|
|
40
|
+
}
|
|
41
|
+
async dispose() {
|
|
42
|
+
// Libera recursos en todos los ds
|
|
43
|
+
await Promise.allSettled(this.targets.map((ds) => ds.dispose?.()));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
exports.CompositeDatasource = CompositeDatasource;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./composite.datasource";
|
|
@@ -14,5 +14,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./
|
|
18
|
-
__exportStar(require("./types"), exports);
|
|
17
|
+
__exportStar(require("./composite.datasource"), exports);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createLogger = createLogger;
|
|
4
|
+
const use_cases_1 = require("../../application/use-cases");
|
|
5
|
+
const services_1 = require("../../domain/services");
|
|
6
|
+
const value_objects_1 = require("../../domain/value-objects");
|
|
7
|
+
// Factoría de logger. Encapsula composición de casos de uso y servicios
|
|
8
|
+
function createLogger(ds, opts = {}) {
|
|
9
|
+
// Resolve minLevel con un default razonable (INFO)
|
|
10
|
+
const minLevel = opts.minLevel ?? value_objects_1.LogLevel.INFO;
|
|
11
|
+
// Mantén compatibilidad con redactPII
|
|
12
|
+
const piiEnabled = opts.pii?.enabled ?? opts.redactPII ?? false;
|
|
13
|
+
const redactor = new services_1.PiiRedactor({ enabled: piiEnabled, ...opts.pii });
|
|
14
|
+
const save = new use_cases_1.SaveLog({ ds, minLevel, redactor });
|
|
15
|
+
const flushUC = new use_cases_1.FlushBuffers(ds);
|
|
16
|
+
// Helper emisor; si en el futuro se necesita encolar, aquí es el lugar
|
|
17
|
+
const emit = (lvl, message, meta) => save.execute(lvl, message, meta);
|
|
18
|
+
return {
|
|
19
|
+
trace: (m, meta) => emit(value_objects_1.LogLevel.TRACE, m, meta),
|
|
20
|
+
debug: (m, meta) => emit(value_objects_1.LogLevel.DEBUG, m, meta),
|
|
21
|
+
info: (m, meta) => emit(value_objects_1.LogLevel.INFO, m, meta),
|
|
22
|
+
warn: (m, meta) => emit(value_objects_1.LogLevel.WARN, m, meta),
|
|
23
|
+
error: (m, meta) => emit(value_objects_1.LogLevel.ERROR, m, meta),
|
|
24
|
+
fatal: (m, meta) => emit(value_objects_1.LogLevel.FATAL, m, meta),
|
|
25
|
+
// Gestión de ciclo de vida → delega en caso de uso / puerto
|
|
26
|
+
flush: () => flushUC.execute(),
|
|
27
|
+
dispose: () => ds.dispose?.() ?? Promise.resolve(),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export declare function createLogger(ds: ILogDatasource, opts?: ICreateLoggerOptions): ILogger;
|
|
1
|
+
export * from "./create-logger";
|
|
@@ -1,74 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
Object.
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
*/
|
|
8
|
-
function normalizeLogMessage(value, redactEnabled, redactor) {
|
|
9
|
-
const v = redactEnabled ? redactor.redact(value) : value;
|
|
10
|
-
if (typeof v === "string")
|
|
11
|
-
return v;
|
|
12
|
-
// Si soportas Error en LogMessage, lo serializamos “bonito”
|
|
13
|
-
if (v instanceof Error) {
|
|
14
|
-
const err = v;
|
|
15
|
-
const obj = {
|
|
16
|
-
error: err.name || "Error",
|
|
17
|
-
message: err.message,
|
|
18
|
-
stack: err.stack,
|
|
19
|
-
code: err.code,
|
|
20
|
-
cause: err.cause,
|
|
21
|
-
};
|
|
22
|
-
try {
|
|
23
|
-
return JSON.stringify(obj);
|
|
24
|
-
}
|
|
25
|
-
catch {
|
|
26
|
-
return `${obj.error}: ${obj.message}`;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
// Objeto → JSON
|
|
30
|
-
try {
|
|
31
|
-
return JSON.stringify(v);
|
|
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]; } };
|
|
32
7
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const redactor = new __1.PiiRedactor({
|
|
44
|
-
enabled: redactEnabled,
|
|
45
|
-
includeDefaultPatterns: opts.pii?.includeDefaultPatterns ?? true,
|
|
46
|
-
patterns: opts.pii?.patterns,
|
|
47
|
-
redactKeys: opts.pii?.redactKeys,
|
|
48
|
-
preserveKeys: opts.pii?.preserveKeys,
|
|
49
|
-
});
|
|
50
|
-
const emit = async (level, message, meta) => {
|
|
51
|
-
if (level < minLevel)
|
|
52
|
-
return;
|
|
53
|
-
const msgStr = normalizeLogMessage(message, redactEnabled, redactor);
|
|
54
|
-
const metaVal = redactEnabled ? redactor.redact(meta) : meta;
|
|
55
|
-
const payload = {
|
|
56
|
-
level,
|
|
57
|
-
timestamp: Date.now(),
|
|
58
|
-
message: msgStr, // <- garantizamos string
|
|
59
|
-
meta: metaVal,
|
|
60
|
-
};
|
|
61
|
-
await ds.save(payload);
|
|
62
|
-
};
|
|
63
|
-
const logger = {
|
|
64
|
-
trace: (m, meta) => emit(__1.LogLevel.TRACE, m, meta),
|
|
65
|
-
debug: (m, meta) => emit(__1.LogLevel.DEBUG, m, meta),
|
|
66
|
-
info: (m, meta) => emit(__1.LogLevel.INFO, m, meta),
|
|
67
|
-
warn: (m, meta) => emit(__1.LogLevel.WARN, m, meta),
|
|
68
|
-
error: (m, meta) => emit(__1.LogLevel.ERROR, m, meta),
|
|
69
|
-
fatal: (m, meta) => emit(__1.LogLevel.FATAL, m, meta),
|
|
70
|
-
flush: () => ds.flush?.() ?? Promise.resolve(),
|
|
71
|
-
dispose: () => ds.dispose?.() ?? Promise.resolve(),
|
|
72
|
-
};
|
|
73
|
-
return logger;
|
|
74
|
-
}
|
|
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("./create-logger"), exports);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jmlq/logger",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.4",
|
|
4
4
|
"author": "MLahuasi",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -9,11 +9,23 @@
|
|
|
9
9
|
],
|
|
10
10
|
"scripts": {
|
|
11
11
|
"build": "tsc -p tsconfig.json",
|
|
12
|
-
"prepublishOnly": "npm run build"
|
|
12
|
+
"prepublishOnly": "npm run build",
|
|
13
|
+
"test": "jest --passWithNoTests",
|
|
14
|
+
"test:watch": "jest --watch",
|
|
15
|
+
"test:cov": "jest --coverage"
|
|
13
16
|
},
|
|
14
17
|
"devDependencies": {
|
|
18
|
+
"@swc/core": "^1.13.5",
|
|
19
|
+
"@swc/jest": "^0.2.39",
|
|
20
|
+
"@types/jest": "^30.0.0",
|
|
15
21
|
"@types/node": "^24.3.0",
|
|
22
|
+
"jest": "^30.1.3",
|
|
16
23
|
"tsx": "^4.20.5",
|
|
17
24
|
"typescript": "^5.9.2"
|
|
25
|
+
},
|
|
26
|
+
"overrides": {
|
|
27
|
+
"test-exclude": "^7.0.1",
|
|
28
|
+
"glob": "^10.4.5",
|
|
29
|
+
"minimatch": "^9.0.5"
|
|
18
30
|
}
|
|
19
31
|
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { IGetLogsFilter, ILogDatasource, ILog } from "../config";
|
|
2
|
-
export declare class CompositeDatasource implements ILogDatasource {
|
|
3
|
-
private readonly datasources;
|
|
4
|
-
constructor(datasources: ILogDatasource[]);
|
|
5
|
-
save(log: ILog): Promise<void>;
|
|
6
|
-
find(filter?: IGetLogsFilter): Promise<ILog[]>;
|
|
7
|
-
flush(): Promise<void>;
|
|
8
|
-
dispose(): Promise<void>;
|
|
9
|
-
}
|
package/dist/Composite/index.js
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CompositeDatasource = void 0;
|
|
4
|
-
// ---- Composite fan-out ----
|
|
5
|
-
class CompositeDatasource {
|
|
6
|
-
constructor(datasources) {
|
|
7
|
-
this.datasources = datasources;
|
|
8
|
-
}
|
|
9
|
-
async save(log) {
|
|
10
|
-
const results = await Promise.allSettled(this.datasources.map((ds) => ds.save(log)));
|
|
11
|
-
for (const r of results) {
|
|
12
|
-
if (r.status === "rejected") {
|
|
13
|
-
// Evita romper el flujo si un destino falla.
|
|
14
|
-
// Aquí podrías enrutar a "dead-letter", métricas, etc.
|
|
15
|
-
// eslint-disable-next-line no-console
|
|
16
|
-
console.warn("[CompositeDatasource] save failed:", r.reason);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
async find(filter = {}) {
|
|
21
|
-
for (const ds of this.datasources) {
|
|
22
|
-
if (typeof ds.find === "function") {
|
|
23
|
-
try {
|
|
24
|
-
return await ds.find(filter);
|
|
25
|
-
}
|
|
26
|
-
catch (e) {
|
|
27
|
-
// eslint-disable-next-line no-console
|
|
28
|
-
console.warn("[CompositeDatasource] find failed on a datasource:", e);
|
|
29
|
-
// probar siguiente datasource
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
return [];
|
|
34
|
-
}
|
|
35
|
-
async flush() {
|
|
36
|
-
const results = await Promise.allSettled(this.datasources.map((ds) => ds.flush?.() ?? Promise.resolve()));
|
|
37
|
-
for (const r of results) {
|
|
38
|
-
if (r.status === "rejected") {
|
|
39
|
-
// eslint-disable-next-line no-console
|
|
40
|
-
console.warn("[CompositeDatasource] flush failed:", r.reason);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
async dispose() {
|
|
45
|
-
const results = await Promise.allSettled(this.datasources.map((ds) => ds.dispose?.() ?? Promise.resolve()));
|
|
46
|
-
for (const r of results) {
|
|
47
|
-
if (r.status === "rejected") {
|
|
48
|
-
// eslint-disable-next-line no-console
|
|
49
|
-
console.warn("[CompositeDatasource] dispose failed:", r.reason);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
exports.CompositeDatasource = CompositeDatasource;
|
package/dist/Factory/index.d.ts
DELETED
package/dist/Factory/index.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createLogger = createLogger;
|
|
4
|
-
const __1 = require("..");
|
|
5
|
-
function createLogger(ds, opts) {
|
|
6
|
-
const write = async (level, message, meta) => {
|
|
7
|
-
if (level < opts.minLevel)
|
|
8
|
-
return;
|
|
9
|
-
// (Opcional) redactor PII aquí si opts.redactPII === true
|
|
10
|
-
await ds.save({ level, message, meta, timestamp: Date.now() });
|
|
11
|
-
};
|
|
12
|
-
const logger = {
|
|
13
|
-
trace: (m, meta) => write(__1.LogLevel.TRACE, m, meta),
|
|
14
|
-
debug: (m, meta) => write(__1.LogLevel.DEBUG, m, meta),
|
|
15
|
-
info: (m, meta) => write(__1.LogLevel.INFO, m, meta),
|
|
16
|
-
warn: (m, meta) => write(__1.LogLevel.WARN, m, meta),
|
|
17
|
-
error: (m, meta) => write(__1.LogLevel.ERROR, m, meta),
|
|
18
|
-
fatal: (m, meta) => write(__1.LogLevel.FATAL, m, meta),
|
|
19
|
-
flush: () => ds.flush?.() ?? Promise.resolve(),
|
|
20
|
-
dispose: () => ds.dispose?.() ?? Promise.resolve(),
|
|
21
|
-
};
|
|
22
|
-
return logger;
|
|
23
|
-
}
|
package/dist/config/index.d.ts
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { LogLevel, LogMessage, PiiReplacement } from "..";
|
|
2
|
-
export interface ILog {
|
|
3
|
-
level: LogLevel;
|
|
4
|
-
message: string;
|
|
5
|
-
meta?: unknown;
|
|
6
|
-
timestamp: number;
|
|
7
|
-
}
|
|
8
|
-
export interface IGetLogsFilter {
|
|
9
|
-
levelMin?: LogLevel;
|
|
10
|
-
since?: number;
|
|
11
|
-
until?: number;
|
|
12
|
-
}
|
|
13
|
-
export interface ILogDatasource {
|
|
14
|
-
save(log: ILog): Promise<void>;
|
|
15
|
-
find?(filter?: IGetLogsFilter): Promise<ILog[]>;
|
|
16
|
-
flush?(): Promise<void>;
|
|
17
|
-
dispose?(): Promise<void>;
|
|
18
|
-
}
|
|
19
|
-
export interface ILogger {
|
|
20
|
-
trace: (msg: LogMessage, meta?: unknown) => Promise<void>;
|
|
21
|
-
debug: (msg: LogMessage, meta?: unknown) => Promise<void>;
|
|
22
|
-
info: (msg: LogMessage, meta?: unknown) => Promise<void>;
|
|
23
|
-
warn: (msg: LogMessage, meta?: unknown) => Promise<void>;
|
|
24
|
-
error: (msg: LogMessage, meta?: unknown) => Promise<void>;
|
|
25
|
-
fatal: (msg: LogMessage, meta?: unknown) => Promise<void>;
|
|
26
|
-
flush?: () => Promise<void>;
|
|
27
|
-
dispose?: () => Promise<void>;
|
|
28
|
-
}
|
|
29
|
-
export interface IPiiPattern {
|
|
30
|
-
/** (Opcional) Nombre del patrón para debug/telemetría */
|
|
31
|
-
name?: string;
|
|
32
|
-
/** Expresión regular a aplicar sobre strings */
|
|
33
|
-
regex: RegExp;
|
|
34
|
-
/** Reemplazo, por defecto "[REDACTED]" si no se provee */
|
|
35
|
-
replacement?: PiiReplacement;
|
|
36
|
-
}
|
|
37
|
-
export interface IPiiRedactorOptions {
|
|
38
|
-
/** Habilita/deshabilita redacción (permite control fino además del flag redactPII) */
|
|
39
|
-
enabled?: boolean;
|
|
40
|
-
/**
|
|
41
|
-
* Incluye patrones default (true) y fusiona con los custom. Si false, usa solo los personalizados
|
|
42
|
-
* por el cliente.
|
|
43
|
-
*/
|
|
44
|
-
includeDefaultPatterns?: boolean;
|
|
45
|
-
/** Patrones adicionales o propios del cliente */
|
|
46
|
-
patterns?: IPiiPattern[];
|
|
47
|
-
/**
|
|
48
|
-
* Redactar por nombre de clave (deep). Cualquier key que haga match será redacatada
|
|
49
|
-
* con "[REDACTED]". Acepta exacto o RegExp.
|
|
50
|
-
*/
|
|
51
|
-
redactKeys?: Array<string | RegExp>;
|
|
52
|
-
/**
|
|
53
|
-
* Claves que NO se deben redactar aunque coincidan con patrones o redactKeys.
|
|
54
|
-
* Prioridad: preserveKeys > redactKeys > patterns.
|
|
55
|
-
*/
|
|
56
|
-
preserveKeys?: Array<string | RegExp>;
|
|
57
|
-
}
|
|
58
|
-
export interface ICreateLoggerOptions {
|
|
59
|
-
minLevel?: LogLevel;
|
|
60
|
-
/** compatibilidad con lo que ya tienes */
|
|
61
|
-
redactPII?: boolean;
|
|
62
|
-
/** NUEVO: bloque de configuración PII */
|
|
63
|
-
pii?: IPiiRedactorOptions & {
|
|
64
|
-
/** atajo para permitir que el cliente mande solo patterns sin escribir PiiRedactorOptions */
|
|
65
|
-
patterns?: IPiiPattern[];
|
|
66
|
-
};
|
|
67
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export declare enum LogLevel {
|
|
2
|
-
TRACE = 0,
|
|
3
|
-
DEBUG = 1,
|
|
4
|
-
INFO = 2,
|
|
5
|
-
WARN = 3,
|
|
6
|
-
ERROR = 4,
|
|
7
|
-
FATAL = 5
|
|
8
|
-
}
|
|
9
|
-
export type PiiReplacement = string | ((match: string, keyPath: string[]) => string);
|
|
10
|
-
export type LogMessage = string | Record<string, unknown> | Error;
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.LogLevel = void 0;
|
|
4
|
-
// ---- Types ----
|
|
5
|
-
var LogLevel;
|
|
6
|
-
(function (LogLevel) {
|
|
7
|
-
LogLevel[LogLevel["TRACE"] = 0] = "TRACE";
|
|
8
|
-
LogLevel[LogLevel["DEBUG"] = 1] = "DEBUG";
|
|
9
|
-
LogLevel[LogLevel["INFO"] = 2] = "INFO";
|
|
10
|
-
LogLevel[LogLevel["WARN"] = 3] = "WARN";
|
|
11
|
-
LogLevel[LogLevel["ERROR"] = 4] = "ERROR";
|
|
12
|
-
LogLevel[LogLevel["FATAL"] = 5] = "FATAL";
|
|
13
|
-
})(LogLevel || (exports.LogLevel = LogLevel = {}));
|
package/dist/domain/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./services";
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
export declare enum LogLevel {
|
|
2
|
-
TRACE = 0,
|
|
3
|
-
DEBUG = 1,
|
|
4
|
-
INFO = 2,
|
|
5
|
-
WARN = 3,
|
|
6
|
-
ERROR = 4,
|
|
7
|
-
FATAL = 5
|
|
8
|
-
}
|
|
9
|
-
export interface Log {
|
|
10
|
-
level: LogLevel;
|
|
11
|
-
message: string;
|
|
12
|
-
meta?: unknown;
|
|
13
|
-
timestamp: number;
|
|
14
|
-
}
|
|
15
|
-
export interface GetLogsFilter {
|
|
16
|
-
levelMin?: LogLevel;
|
|
17
|
-
since?: number;
|
|
18
|
-
until?: number;
|
|
19
|
-
}
|
|
20
|
-
export interface ILogDatasource {
|
|
21
|
-
save(log: Log): Promise<void>;
|
|
22
|
-
find?(filter?: GetLogsFilter): Promise<Log[]>;
|
|
23
|
-
flush?(): Promise<void>;
|
|
24
|
-
dispose?(): Promise<void>;
|
|
25
|
-
}
|
|
26
|
-
export interface Logger {
|
|
27
|
-
trace: (msg: string, meta?: unknown) => Promise<void>;
|
|
28
|
-
debug: (msg: string, meta?: unknown) => Promise<void>;
|
|
29
|
-
info: (msg: string, meta?: unknown) => Promise<void>;
|
|
30
|
-
warn: (msg: string, meta?: unknown) => Promise<void>;
|
|
31
|
-
error: (msg: string, meta?: unknown) => Promise<void>;
|
|
32
|
-
fatal: (msg: string, meta?: unknown) => Promise<void>;
|
|
33
|
-
flush?: () => Promise<void>;
|
|
34
|
-
dispose?: () => Promise<void>;
|
|
35
|
-
}
|
package/dist/interfaces/index.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.LogLevel = void 0;
|
|
4
|
-
// ---- Types & contratos ----
|
|
5
|
-
var LogLevel;
|
|
6
|
-
(function (LogLevel) {
|
|
7
|
-
LogLevel[LogLevel["TRACE"] = 0] = "TRACE";
|
|
8
|
-
LogLevel[LogLevel["DEBUG"] = 1] = "DEBUG";
|
|
9
|
-
LogLevel[LogLevel["INFO"] = 2] = "INFO";
|
|
10
|
-
LogLevel[LogLevel["WARN"] = 3] = "WARN";
|
|
11
|
-
LogLevel[LogLevel["ERROR"] = 4] = "ERROR";
|
|
12
|
-
LogLevel[LogLevel["FATAL"] = 5] = "FATAL";
|
|
13
|
-
})(LogLevel || (exports.LogLevel = LogLevel = {}));
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./factory";
|