@jmlq/logger 0.1.0-alpha.6 → 0.1.0-alpha.8
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 +232 -551
- package/dist/examples/data-source-service.example.d.ts +3 -0
- package/dist/examples/data-source-service.example.js +174 -0
- package/dist/examples/flush-buffers-use-case.example.d.ts +3 -0
- package/dist/examples/flush-buffers-use-case.example.js +60 -0
- package/dist/examples/get-logs-use-case.example.d.ts +3 -0
- package/dist/examples/get-logs-use-case.example.js +110 -0
- package/dist/examples/index.example.d.ts +8 -0
- package/dist/examples/index.example.js +116 -0
- package/dist/examples/logger-factory.example.d.ts +39 -0
- package/dist/examples/logger-factory.example.js +158 -0
- package/dist/examples/normalize-message.example.d.ts +3 -0
- package/dist/examples/normalize-message.example.js +80 -0
- package/dist/examples/pii-redactor.example.d.ts +3 -0
- package/dist/examples/pii-redactor.example.js +129 -0
- package/dist/examples/save-log-use-case.example.d.ts +3 -0
- package/dist/examples/save-log-use-case.example.js +150 -0
- package/dist/examples/to-log-level.example.d.ts +3 -0
- package/dist/examples/to-log-level.example.js +49 -0
- package/dist/examples/to-pii-regex.example.d.ts +3 -0
- package/dist/examples/to-pii-regex.example.js +75 -0
- package/dist/{presentation → src/application}/factory/create-logger.d.ts +1 -1
- package/dist/{presentation → src/application}/factory/create-logger.js +3 -3
- package/dist/src/application/factory/index.d.ts +1 -0
- package/dist/{presentation → src/application}/factory/index.js +1 -1
- package/dist/src/application/factory/logger.factory.d.ts +12 -0
- package/dist/src/application/factory/logger.factory.js +74 -0
- package/dist/src/application/index.d.ts +2 -0
- package/dist/src/application/index.js +18 -0
- package/dist/src/application/use-cases/flush-buffers.use-case.d.ts +6 -0
- package/dist/{application/use-cases/flush-buffers.js → src/application/use-cases/flush-buffers.use-case.js} +3 -3
- package/dist/src/application/use-cases/get-logs.use-case.d.ts +8 -0
- package/dist/{application/use-cases/get-logs.js → src/application/use-cases/get-logs.use-case.js} +3 -3
- package/dist/src/application/use-cases/index.d.ts +3 -0
- package/dist/{application → src/application}/use-cases/index.js +3 -3
- package/dist/src/application/use-cases/save-log.use-case.d.ts +8 -0
- package/dist/src/application/use-cases/save-log.use-case.js +26 -0
- package/dist/src/domain/index.d.ts +6 -0
- package/dist/src/domain/index.js +22 -0
- package/dist/src/domain/ports/create-logger-options.port.d.ts +7 -0
- package/dist/src/domain/ports/index.d.ts +6 -0
- package/dist/src/domain/ports/index.js +22 -0
- package/dist/src/domain/ports/log-datasource.port.d.ts +9 -0
- package/dist/src/domain/ports/logger-factory-config.port.d.ts +28 -0
- package/dist/src/domain/ports/logger-service.port.d.ts +19 -0
- package/dist/{domain/contracts/logger.d.ts → src/domain/ports/logger.port.d.ts} +0 -9
- package/dist/src/domain/ports/logger.port.js +2 -0
- package/dist/src/domain/ports/pii-redactor.port.d.ts +5 -0
- package/dist/src/domain/ports/pii-redactor.port.js +2 -0
- package/dist/src/domain/request/get-logs-filter.props.d.ts +9 -0
- package/dist/src/domain/request/get-logs-filter.props.js +2 -0
- package/dist/src/domain/request/index.d.ts +5 -0
- package/dist/src/domain/request/index.js +21 -0
- package/dist/src/domain/request/log.props.d.ts +7 -0
- package/dist/src/domain/request/log.props.js +2 -0
- package/dist/src/domain/request/pii-options.props.d.ts +8 -0
- package/dist/src/domain/request/pii-options.props.js +2 -0
- package/dist/src/domain/request/pii-replacement.props.d.ts +5 -0
- package/dist/src/domain/request/pii-replacement.props.js +2 -0
- package/dist/src/domain/request/save-log.props.d.ts +7 -0
- package/dist/src/domain/request/save-log.props.js +2 -0
- package/dist/src/domain/response/index.d.ts +1 -0
- package/dist/{domain/value-objects → src/domain/response}/index.js +1 -1
- package/dist/src/domain/response/log.response.d.ts +7 -0
- package/dist/src/domain/response/log.response.js +2 -0
- package/dist/src/domain/services/index.d.ts +4 -0
- package/dist/{domain/contracts → src/domain/services}/index.js +4 -3
- package/dist/src/domain/services/log-level.service.d.ts +2 -0
- package/dist/src/domain/services/log-level.service.js +27 -0
- package/dist/src/domain/services/message-normalizer.service.d.ts +3 -0
- package/dist/src/domain/services/message-normalizer.service.js +8 -0
- package/dist/src/domain/services/pii-pattern.service.d.ts +2 -0
- package/dist/src/domain/services/pii-pattern.service.js +13 -0
- package/dist/src/domain/services/pii-redactor.d.ts +10 -0
- package/dist/{domain → src/domain}/services/pii-redactor.js +8 -17
- package/dist/src/domain/services/pii-redactor.service.d.ts +10 -0
- package/dist/src/domain/services/pii-redactor.service.js +68 -0
- package/dist/src/domain/types/index.d.ts +1 -0
- package/dist/src/domain/types/index.js +17 -0
- package/dist/src/domain/types/log-message.type.d.ts +1 -0
- package/dist/src/domain/types/log-message.type.js +2 -0
- package/dist/src/domain/value-objects/index.d.ts +1 -0
- package/dist/{domain/services → src/domain/value-objects}/index.js +1 -1
- package/dist/{domain/value-objects/log-level.d.ts → src/domain/value-objects/log-level.vo.d.ts} +0 -1
- package/dist/src/domain/value-objects/log-level.vo.js +13 -0
- package/dist/src/index.d.ts +6 -0
- package/dist/src/index.js +22 -0
- package/dist/src/infrastructure/datasources/in-memory-log.datasource.d.ts +1 -0
- package/dist/src/infrastructure/datasources/in-memory-log.datasource.js +2 -0
- package/dist/src/infrastructure/datasources/index.d.ts +1 -0
- package/dist/{infrastructure/adapters → src/infrastructure/datasources}/index.js +1 -1
- package/dist/src/infrastructure/index.d.ts +1 -0
- package/dist/{domain/types → src/infrastructure}/index.js +1 -1
- package/dist/src/infrastructure/services/data-source-error-handler.type.d.ts +5 -0
- package/dist/src/infrastructure/services/data-source-error-handler.type.js +2 -0
- package/dist/src/infrastructure/services/datasource.service.d.ts +15 -0
- package/dist/src/infrastructure/services/datasource.service.js +63 -0
- package/dist/src/infrastructure/services/index.d.ts +2 -0
- package/dist/src/infrastructure/services/index.js +18 -0
- package/dist/tests/application/factory/logger-factory.spec.d.ts +1 -0
- package/dist/tests/application/factory/logger-factory.spec.js +161 -0
- package/dist/tests/application/use-cases/flush-buffers.use-case.spec.d.ts +1 -0
- package/dist/tests/application/use-cases/flush-buffers.use-case.spec.js +38 -0
- package/dist/tests/application/use-cases/get-logs.use-case.spec.d.ts +1 -0
- package/dist/tests/application/use-cases/get-logs.use-case.spec.js +114 -0
- package/dist/tests/application/use-cases/save-log.use-case.spec.d.ts +1 -0
- package/dist/tests/application/use-cases/save-log.use-case.spec.js +138 -0
- package/dist/tests/domain/services/log-level.service.spec.d.ts +1 -0
- package/dist/tests/domain/services/log-level.service.spec.js +68 -0
- package/dist/tests/domain/services/normalize-message.spec.d.ts +1 -0
- package/dist/tests/domain/services/normalize-message.spec.js +83 -0
- package/dist/tests/domain/services/pii-redactor.spec.d.ts +1 -0
- package/dist/tests/domain/services/pii-redactor.spec.js +170 -0
- package/dist/tests/domain/services/to-pii-regex.spec.d.ts +1 -0
- package/dist/tests/domain/services/to-pii-regex.spec.js +82 -0
- package/dist/tests/infrastructure/services/datasource.service.spec.d.ts +1 -0
- package/dist/tests/infrastructure/services/datasource.service.spec.js +128 -0
- package/dist/tests/test-utils/create-pii-redactor-mock.d.ts +5 -0
- package/dist/tests/test-utils/create-pii-redactor-mock.js +10 -0
- package/package.json +27 -19
- package/LICENSE +0 -21
- package/dist/application/use-cases/flush-buffers.d.ts +0 -6
- package/dist/application/use-cases/get-logs.d.ts +0 -7
- package/dist/application/use-cases/index.d.ts +0 -3
- package/dist/application/use-cases/save-log.d.ts +0 -13
- package/dist/application/use-cases/save-log.js +0 -30
- package/dist/domain/contracts/index.d.ts +0 -3
- package/dist/domain/contracts/log.datasource.d.ts +0 -8
- package/dist/domain/contracts/pii.d.ts +0 -5
- package/dist/domain/services/index.d.ts +0 -1
- package/dist/domain/services/pii-redactor.d.ts +0 -10
- package/dist/domain/types/index.d.ts +0 -1
- package/dist/domain/types/log.types.d.ts +0 -28
- package/dist/domain/value-objects/index.d.ts +0 -1
- package/dist/domain/value-objects/log-level.js +0 -37
- package/dist/index.d.ts +0 -5
- package/dist/index.js +0 -10
- package/dist/infrastructure/adapters/composite.datasource.d.ts +0 -11
- package/dist/infrastructure/adapters/composite.datasource.js +0 -46
- package/dist/infrastructure/adapters/index.d.ts +0 -1
- package/dist/presentation/factory/index.d.ts +0 -1
- /package/dist/{domain/contracts/log.datasource.js → src/domain/ports/create-logger-options.port.js} +0 -0
- /package/dist/{domain/contracts/logger.js → src/domain/ports/log-datasource.port.js} +0 -0
- /package/dist/{domain/contracts/pii.js → src/domain/ports/logger-factory-config.port.js} +0 -0
- /package/dist/{domain/types/log.types.js → src/domain/ports/logger-service.port.js} +0 -0
package/README.md
CHANGED
|
@@ -1,626 +1,307 @@
|
|
|
1
1
|
# @jmlq/logger
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
Permite registrar logs en múltiples destinos (`archivos`, `MongoDB`, `PostgreSQL`) mediante **plugins** y soporta enmascarado de datos sensibles (PII).
|
|
5
|
-
|
|
6
|
-
---
|
|
3
|
+
Sistema de logging modular y extensible con **Arquitectura Limpia**. Soporta múltiples destinos (archivos, MongoDB, PostgreSQL) y enmascarado automático de datos sensibles (PII).
|
|
7
4
|
|
|
8
5
|
## 📦 Instalación
|
|
9
6
|
|
|
10
7
|
```bash
|
|
11
|
-
#
|
|
12
|
-
npm
|
|
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-postgresql
|
|
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
|
|
8
|
+
# Core del logger
|
|
9
|
+
npm install @jmlq/logger
|
|
26
10
|
|
|
11
|
+
# Plugins opcionales (instala según necesites)
|
|
12
|
+
npm install @jmlq/logger-plugin-fs # Para archivos
|
|
13
|
+
npm install @jmlq/logger-plugin-mongo # Para MongoDB
|
|
14
|
+
npm install @jmlq/logger-plugin-postgresql # Para PostgreSQL
|
|
27
15
|
```
|
|
28
16
|
|
|
29
|
-
|
|
17
|
+
## 🚀 Uso Básico
|
|
30
18
|
|
|
31
|
-
|
|
32
|
-
> - [`@jmlq/logger-plugin-mongo`](https://www.npmjs.com/package/@jmlq/logger-plugin-mongo)
|
|
33
|
-
> - [`@jmlq/logger-plugin-postgresql`](https://www.npmjs.com/package/@jmlq/logger-plugin-postgresql)
|
|
19
|
+
### Configuración Simple
|
|
34
20
|
|
|
35
|
-
|
|
21
|
+
```typescript
|
|
22
|
+
import { LoggerFactory, LogLevel } from "@jmlq/logger";
|
|
36
23
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
# MONGO DB @jmlq/logger-plugin-mongo
|
|
50
|
-
MONGO_URL=mongodb://<user>:<password>@localhost:<port>
|
|
51
|
-
MONGO_DB_NAME=<db-name>
|
|
52
|
-
MONGO_COLLECTION=<collection-name>
|
|
53
|
-
LOGGER_MONGO_RETENTION_DAYS=30
|
|
54
|
-
|
|
55
|
-
# POSTGRESQL @jmlq/logger-plugin-postgresql
|
|
56
|
-
POSTGRES_URL="postgresql://mlahuasi:123456@localhost:5432/NOC"
|
|
57
|
-
POSTGRES_DB=<db-name>
|
|
58
|
-
POSTGRES_SCHEMA=public
|
|
59
|
-
POSTGRES_TABLE=<table-name>
|
|
60
|
-
LOGGER_PG_RETENTION_DAYS=30
|
|
24
|
+
// Crear un datasource en memoria (para testing/desarrollo)
|
|
25
|
+
const memoryDatasource = {
|
|
26
|
+
name: "memory",
|
|
27
|
+
async save(log) {
|
|
28
|
+
console.log("LOG:", log);
|
|
29
|
+
},
|
|
30
|
+
async find(filter) {
|
|
31
|
+
return [];
|
|
32
|
+
},
|
|
33
|
+
async flush() {},
|
|
34
|
+
async dispose() {},
|
|
35
|
+
};
|
|
61
36
|
|
|
62
|
-
|
|
63
|
-
|
|
37
|
+
// Crear el logger
|
|
38
|
+
const logger = LoggerFactory.create({
|
|
39
|
+
datasources: memoryDatasource,
|
|
40
|
+
minLevel: LogLevel.INFO,
|
|
41
|
+
redactorOptions: {
|
|
42
|
+
enabled: true,
|
|
43
|
+
deep: true,
|
|
44
|
+
includeDefaults: true,
|
|
45
|
+
},
|
|
46
|
+
});
|
|
64
47
|
|
|
48
|
+
// Usar el logger
|
|
49
|
+
await logger.info("Usuario conectado", {
|
|
50
|
+
userId: "123",
|
|
51
|
+
email: "user@example.com",
|
|
52
|
+
});
|
|
53
|
+
await logger.error("Error en el sistema", {
|
|
54
|
+
error: "Database connection failed",
|
|
55
|
+
});
|
|
65
56
|
```
|
|
66
57
|
|
|
67
|
-
|
|
58
|
+
### Con Múltiples Datasources
|
|
68
59
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
60
|
+
```typescript
|
|
61
|
+
import { LoggerFactory } from "@jmlq/logger";
|
|
62
|
+
import { FileSystemDatasource } from "@jmlq/logger-plugin-fs";
|
|
63
|
+
import { MongoDatasource } from "@jmlq/logger-plugin-mongo";
|
|
64
|
+
|
|
65
|
+
const logger = LoggerFactory.create({
|
|
66
|
+
datasources: [
|
|
67
|
+
new FileSystemDatasource({ basePath: "./logs" }),
|
|
68
|
+
new MongoDatasource({ url: "mongodb://localhost:27017", dbName: "logs" }),
|
|
69
|
+
],
|
|
70
|
+
minLevel: LogLevel.INFO,
|
|
71
|
+
});
|
|
72
72
|
|
|
73
|
+
await logger.warn("Latencia alta", { endpoint: "/api/users", duration: 850 });
|
|
73
74
|
```
|
|
74
|
-
src/
|
|
75
|
-
├─ infrastructure/
|
|
76
|
-
│ ├─ logger/
|
|
77
|
-
│ │ ├─ adapters/
|
|
78
|
-
│ │ │ ├─ fs.adapter.ts
|
|
79
|
-
│ │ │ ├─ mongo.adapter.ts
|
|
80
|
-
│ │ │ └─ postgresql.adapter.ts
|
|
81
|
-
│ │ ├─ settings/
|
|
82
|
-
│ │ │ ├─ pii.settings.ts
|
|
83
|
-
│ │ │ └─ loglevel.settings.ts
|
|
84
|
-
│ │ └─ bootstrap.ts # LoggerBootstrap (orquesta adapters + PII)
|
|
85
|
-
│ └─ plugins/
|
|
86
|
-
│ ├─ env.plugin.ts
|
|
87
|
-
│ └─ index.ts
|
|
88
|
-
├─ config/
|
|
89
|
-
│ └─ logger/
|
|
90
|
-
│ └─ index.ts # singleton global: loggerReady / flush / dispose
|
|
91
|
-
└─ presentation/
|
|
92
|
-
└─ server.ts # uso del logger en Express
|
|
93
75
|
|
|
94
|
-
|
|
76
|
+
## 🎯 Características Principales
|
|
95
77
|
|
|
96
|
-
|
|
78
|
+
### Niveles de Log
|
|
97
79
|
|
|
98
|
-
|
|
80
|
+
- `TRACE` (0) - Información muy detallada
|
|
81
|
+
- `DEBUG` (1) - Información de depuración
|
|
82
|
+
- `INFO` (2) - Información general
|
|
83
|
+
- `WARN` (3) - Advertencias
|
|
84
|
+
- `ERROR` (4) - Errores
|
|
85
|
+
- `FATAL` (5) - Errores críticos
|
|
99
86
|
|
|
100
|
-
|
|
101
|
-
//Se definine reglas de redacción PII (buscar-y-reemplazar con regex) que el logger aplica antes de persistir/emitir un log.
|
|
87
|
+
### Enmascarado PII Automático
|
|
102
88
|
|
|
103
|
-
|
|
104
|
-
pattern: string;
|
|
105
|
-
replaceWith: string;
|
|
106
|
-
flags?: string;
|
|
107
|
-
};
|
|
89
|
+
El logger detecta y enmascara automáticamente datos sensibles:
|
|
108
90
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
91
|
+
```typescript
|
|
92
|
+
await logger.info("Pago procesado", {
|
|
93
|
+
email: "user@example.com", // → user@[EMAIL]
|
|
94
|
+
card: "4111-1111-1111-1111", // → ****-****-****-****
|
|
95
|
+
password: "secret123", // → [REDACTED]
|
|
96
|
+
});
|
|
97
|
+
```
|
|
117
98
|
|
|
118
|
-
|
|
119
|
-
export const DEFAULT_PII_PATTERNS: PiiReplacement[] = [
|
|
120
|
-
{
|
|
121
|
-
pattern: String.raw`[^@\n\r ]+@[^@\n\r ]+`,
|
|
122
|
-
replaceWith: "[EMAIL]",
|
|
123
|
-
flags: "g",
|
|
124
|
-
},
|
|
125
|
-
];
|
|
126
|
-
|
|
127
|
-
// patrones “del cliente/proyecto”:
|
|
128
|
-
export const clientPiiPatterns: PiiReplacement[] = [
|
|
129
|
-
{ pattern: String.raw`\b\d{10}\b`, flags: "g", replaceWith: "[EC_DNI]" },
|
|
130
|
-
];
|
|
131
|
-
|
|
132
|
-
// lista negra de nombres de clave que se siempre se ocultan
|
|
133
|
-
export const redactKeys: string[] = ["password", "secret"];
|
|
134
|
-
// lista blanca de nombres de clave que no se deben ocultar por clave
|
|
135
|
-
export const preserveKeys: string[] = ["city"];
|
|
136
|
-
|
|
137
|
-
// Helpers
|
|
138
|
-
// 1. Filtra valores no válidos: elimina null, undefined y "" (cadena vacía).
|
|
139
|
-
// 2. Elimina duplicados usando Set.
|
|
140
|
-
// 3. Conserva el primero de cada valor repetido (porque Set guarda la primera aparición).
|
|
141
|
-
export function dedupeStrings(arr: Array<string | undefined | null>): string[] {
|
|
142
|
-
return [...new Set(arr.filter((x): x is string => !!x && x.length > 0))];
|
|
143
|
-
}
|
|
99
|
+
### Filtros y Consultas
|
|
144
100
|
|
|
145
|
-
|
|
146
|
-
//
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
return [...m.values()];
|
|
153
|
-
}
|
|
101
|
+
```typescript
|
|
102
|
+
// Obtener logs de errores de las últimas 24 horas
|
|
103
|
+
const errors = await logger.getLogs({
|
|
104
|
+
levelMin: LogLevel.ERROR,
|
|
105
|
+
since: Date.now() - 24 * 60 * 60 * 1000,
|
|
106
|
+
limit: 50,
|
|
107
|
+
});
|
|
154
108
|
|
|
155
|
-
//
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
const patterns = dedupePatterns([
|
|
162
|
-
...(includeDefaults ? DEFAULT_PII_PATTERNS : []),
|
|
163
|
-
...clientPiiPatterns,
|
|
164
|
-
...(opts?.patterns ?? []),
|
|
165
|
-
]);
|
|
166
|
-
|
|
167
|
-
const whitelistKeys = dedupeStrings([
|
|
168
|
-
...(opts?.whitelistKeys ?? []),
|
|
169
|
-
...preserveKeys,
|
|
170
|
-
]);
|
|
171
|
-
const blacklistKeys = dedupeStrings([
|
|
172
|
-
...(opts?.blacklistKeys ?? []),
|
|
173
|
-
...redactKeys,
|
|
174
|
-
]);
|
|
175
|
-
|
|
176
|
-
return {
|
|
177
|
-
enabled: !!opts?.enabled,
|
|
178
|
-
whitelistKeys,
|
|
179
|
-
blacklistKeys,
|
|
180
|
-
patterns,
|
|
181
|
-
deep: opts?.deep ?? true,
|
|
182
|
-
};
|
|
183
|
-
}
|
|
109
|
+
// Buscar logs que contengan "usuario"
|
|
110
|
+
const userLogs = await logger.getLogs({
|
|
111
|
+
query: "usuario",
|
|
112
|
+
limit: 100,
|
|
113
|
+
});
|
|
184
114
|
```
|
|
185
115
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
```ts
|
|
189
|
-
// src/infrastructure/logger/settings/loglevel.settings.ts
|
|
190
|
-
import { LogLevel } from "@jmlq/logger";
|
|
191
|
-
|
|
192
|
-
export function toMinLevel(
|
|
193
|
-
level: LogLevel | keyof typeof LogLevel | string
|
|
194
|
-
): LogLevel {
|
|
195
|
-
if (typeof level === "number") return level as LogLevel;
|
|
196
|
-
switch (String(level || "debug").toLowerCase()) {
|
|
197
|
-
case "trace":
|
|
198
|
-
return LogLevel.TRACE;
|
|
199
|
-
case "debug":
|
|
200
|
-
return LogLevel.DEBUG;
|
|
201
|
-
case "info":
|
|
202
|
-
return LogLevel.INFO;
|
|
203
|
-
case "warn":
|
|
204
|
-
return LogLevel.WARN;
|
|
205
|
-
case "error":
|
|
206
|
-
return LogLevel.ERROR;
|
|
207
|
-
case "fatal":
|
|
208
|
-
return LogLevel.FATAL;
|
|
209
|
-
default:
|
|
210
|
-
return LogLevel.DEBUG;
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
```
|
|
116
|
+
## 🔧 Configuración con Variables de Entorno
|
|
214
117
|
|
|
215
|
-
|
|
118
|
+
```env
|
|
119
|
+
# Nivel mínimo de logs
|
|
120
|
+
LOG_LEVEL=info
|
|
216
121
|
|
|
217
|
-
|
|
122
|
+
# Filesystem
|
|
123
|
+
LOGGER_FS_PATH=./logs
|
|
218
124
|
|
|
219
|
-
|
|
125
|
+
# MongoDB
|
|
126
|
+
MONGO_URL=mongodb://localhost:27017
|
|
127
|
+
MONGO_DB_NAME=logs
|
|
220
128
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
> - Si se necesita guardar logs en una base de datos relacional se crea adapter para `postgresql`.
|
|
224
|
-
> - También se pueden combinar.
|
|
225
|
-
> - Se debe implementar al menos un adapter.
|
|
129
|
+
# PostgreSQL
|
|
130
|
+
POSTGRES_URL=postgresql://user:pass@localhost:5432/logs
|
|
226
131
|
|
|
227
|
-
|
|
132
|
+
# PII Protection
|
|
133
|
+
LOGGER_PII_ENABLED=true
|
|
134
|
+
```
|
|
228
135
|
|
|
229
|
-
|
|
230
|
-
import { createFsDatasource } from "@jmlq/logger-plugin-fs";
|
|
231
|
-
import type { ILogDatasource } from "@jmlq/logger";
|
|
136
|
+
## 📁 Ejemplos Prácticos
|
|
232
137
|
|
|
233
|
-
|
|
234
|
-
basePath: string;
|
|
235
|
-
fileNamePattern: string;
|
|
236
|
-
rotationPolicy: { by: "none" | "day" | "size"; maxSizeMB?: number };
|
|
237
|
-
}
|
|
138
|
+
El proyecto incluye ejemplos completos en el directorio [`examples/`](examples/):
|
|
238
139
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
onRotate: (oldP, newP) =>
|
|
249
|
-
console.log("[fs] rotated:", oldP, "->", newP),
|
|
250
|
-
onError: (e) => console.error("[fs] error:", e),
|
|
251
|
-
});
|
|
252
|
-
console.log("[logger] Conectado a FS para logs");
|
|
253
|
-
return new FsAdapter(ds);
|
|
254
|
-
} catch (e: any) {
|
|
255
|
-
console.warn("[logger] FS deshabilitado:", e?.message ?? e);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
get datasource(): ILogDatasource {
|
|
259
|
-
return this.ds;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
140
|
+
```bash
|
|
141
|
+
# Ver todos los ejemplos disponibles
|
|
142
|
+
npm run example:help
|
|
143
|
+
|
|
144
|
+
# Ejecutar ejemplos específicos
|
|
145
|
+
npm run example:factories # LoggerFactory
|
|
146
|
+
npm run example:use-cases # SaveLog, GetLogs, FlushBuffers
|
|
147
|
+
npm run example:domain-services # PII, normalización
|
|
148
|
+
npm run example:all # Todos los ejemplos
|
|
262
149
|
```
|
|
263
150
|
|
|
264
|
-
|
|
151
|
+
### Ejemplo Express
|
|
265
152
|
|
|
266
|
-
```
|
|
267
|
-
import
|
|
268
|
-
import {
|
|
153
|
+
```typescript
|
|
154
|
+
import express from "express";
|
|
155
|
+
import { LoggerFactory, LogLevel } from "@jmlq/logger";
|
|
269
156
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
}
|
|
157
|
+
const app = express();
|
|
158
|
+
const logger = LoggerFactory.create({
|
|
159
|
+
datasources: /* tu datasource */,
|
|
160
|
+
minLevel: LogLevel.INFO
|
|
161
|
+
});
|
|
276
162
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
163
|
+
app.use(async (req, res, next) => {
|
|
164
|
+
await logger.info("Request iniciado", {
|
|
165
|
+
method: req.method,
|
|
166
|
+
url: req.url,
|
|
167
|
+
ip: req.ip
|
|
168
|
+
});
|
|
169
|
+
next();
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
app.get("/users/:id", async (req, res) => {
|
|
173
|
+
try {
|
|
174
|
+
await logger.info("Obteniendo usuario", { userId: req.params.id });
|
|
175
|
+
// ... tu lógica
|
|
176
|
+
res.json({ user: data });
|
|
177
|
+
} catch (error) {
|
|
178
|
+
await logger.error("Error al obtener usuario", {
|
|
179
|
+
userId: req.params.id,
|
|
180
|
+
error: error.message
|
|
181
|
+
});
|
|
182
|
+
res.status(500).json({ error: "Internal server error" });
|
|
297
183
|
}
|
|
298
|
-
}
|
|
184
|
+
});
|
|
299
185
|
```
|
|
300
186
|
|
|
301
|
-
|
|
187
|
+
### Ejemplo NestJS
|
|
302
188
|
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
export interface IPostgresqlProps {
|
|
308
|
-
url: string;
|
|
309
|
-
schema: string;
|
|
310
|
-
table?: string;
|
|
311
|
-
retentionDays?: number | null;
|
|
312
|
-
}
|
|
189
|
+
```typescript
|
|
190
|
+
@Injectable()
|
|
191
|
+
export class AppService {
|
|
192
|
+
constructor(@Inject("LOGGER") private readonly logger: ILoggerService) {}
|
|
313
193
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
static async create(
|
|
317
|
-
opts: IPostgresqlProps
|
|
318
|
-
): Promise<PostgresqlAdapter | undefined> {
|
|
194
|
+
async createUser(userData: any) {
|
|
195
|
+
await this.logger.info("Creando usuario", { userData });
|
|
319
196
|
try {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
197
|
+
// ... lógica
|
|
198
|
+
await this.logger.info("Usuario creado", { userId: result.id });
|
|
199
|
+
return result;
|
|
200
|
+
} catch (error) {
|
|
201
|
+
await this.logger.error("Error creando usuario", {
|
|
202
|
+
error: error.message,
|
|
326
203
|
});
|
|
327
|
-
|
|
328
|
-
return new PostgresqlAdapter(ps);
|
|
329
|
-
} catch (e: any) {
|
|
330
|
-
console.warn("[logger] Postgres deshabilitado:", e?.message ?? e);
|
|
204
|
+
throw error;
|
|
331
205
|
}
|
|
332
206
|
}
|
|
333
|
-
get datasource(): ILogDatasource {
|
|
334
|
-
return this.ds;
|
|
335
|
-
}
|
|
336
207
|
}
|
|
337
208
|
```
|
|
338
209
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
```ts
|
|
342
|
-
// src/infrastructure/logger/bootstrap.ts
|
|
343
|
-
import {
|
|
344
|
-
createLogger,
|
|
345
|
-
CompositeDatasource,
|
|
346
|
-
type ILogDatasource,
|
|
347
|
-
} from "@jmlq/logger";
|
|
348
|
-
import { buildPiiConfig } from "./settings/pii.settings";
|
|
349
|
-
import { toMinLevel } from "./settings/loglevel.settings";
|
|
350
|
-
// NOTA: Opcionales depende de las necesidades del cliente
|
|
351
|
-
import { FsAdapter, type IFsProps } from "./adapters/fs.adapter";
|
|
352
|
-
import { MongoAdapter, type IMongoProps } from "./adapters/mongo.adapter";
|
|
353
|
-
import {
|
|
354
|
-
PostgresqlAdapter,
|
|
355
|
-
type IPostgresqlProps,
|
|
356
|
-
} from "./adapters/postgresql.adapter";
|
|
357
|
-
|
|
358
|
-
export interface LoggerBootstrapOptions {
|
|
359
|
-
minLevel: string | number;
|
|
360
|
-
pii?: {
|
|
361
|
-
enabled?: boolean;
|
|
362
|
-
whitelistKeys?: string[];
|
|
363
|
-
blacklistKeys?: string[];
|
|
364
|
-
patterns?: any[];
|
|
365
|
-
deep?: boolean;
|
|
366
|
-
includeDefaults?: boolean;
|
|
367
|
-
};
|
|
368
|
-
adapters?: {
|
|
369
|
-
// NOTA: Opcionales depende de las necesidades del cliente
|
|
370
|
-
fs?: IFsProps;
|
|
371
|
-
mongo?: IMongoProps;
|
|
372
|
-
postgres?: IPostgresqlProps;
|
|
373
|
-
};
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
export class LoggerBootstrap {
|
|
377
|
-
private constructor(
|
|
378
|
-
private readonly _logger: ReturnType<typeof createLogger>,
|
|
379
|
-
private readonly _ds: ILogDatasource
|
|
380
|
-
) {}
|
|
210
|
+
## 🏗️ Arquitectura
|
|
381
211
|
|
|
382
|
-
|
|
383
|
-
const dsList: ILogDatasource[] = [];
|
|
212
|
+
El paquete sigue **Arquitectura Limpia**:
|
|
384
213
|
|
|
385
|
-
// NOTA: Opcionales depende de las necesidades del cliente
|
|
386
|
-
if (opts.adapters?.fs) {
|
|
387
|
-
const fs = FsAdapter.create(opts.adapters.fs);
|
|
388
|
-
if (fs) dsList.push(fs.datasource);
|
|
389
|
-
}
|
|
390
|
-
if (opts.adapters?.mongo) {
|
|
391
|
-
const mg = await MongoAdapter.create(opts.adapters.mongo);
|
|
392
|
-
if (mg) dsList.push(mg.datasource);
|
|
393
|
-
}
|
|
394
|
-
if (opts.adapters?.postgres) {
|
|
395
|
-
const pg = await PostgresqlAdapter.create(opts.adapters.postgres);
|
|
396
|
-
if (pg) dsList.push(pg.datasource);
|
|
397
|
-
}
|
|
398
|
-
//----
|
|
399
|
-
|
|
400
|
-
if (dsList.length === 0)
|
|
401
|
-
throw new Error("[logger] No hay datasources válidos.");
|
|
402
|
-
const datasource =
|
|
403
|
-
dsList.length === 1 ? dsList[0] : new CompositeDatasource(dsList);
|
|
404
|
-
|
|
405
|
-
const pii = buildPiiConfig({
|
|
406
|
-
enabled: opts.pii?.enabled ?? false,
|
|
407
|
-
includeDefaults: opts.pii?.includeDefaults ?? true,
|
|
408
|
-
whitelistKeys: opts.pii?.whitelistKeys,
|
|
409
|
-
blacklistKeys: opts.pii?.blacklistKeys,
|
|
410
|
-
patterns: opts.pii?.patterns,
|
|
411
|
-
deep: opts.pii?.deep ?? true,
|
|
412
|
-
});
|
|
413
|
-
|
|
414
|
-
const logger = createLogger(datasource, {
|
|
415
|
-
minLevel: toMinLevel(opts.minLevel),
|
|
416
|
-
pii,
|
|
417
|
-
});
|
|
418
|
-
return new LoggerBootstrap(logger, datasource);
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
get logger() {
|
|
422
|
-
return this._logger;
|
|
423
|
-
}
|
|
424
|
-
async flush() {
|
|
425
|
-
const any = this._logger as any;
|
|
426
|
-
if (typeof any.flush === "function") await any.flush();
|
|
427
|
-
}
|
|
428
|
-
async dispose() {
|
|
429
|
-
const any = this._logger as any;
|
|
430
|
-
if (typeof any.dispose === "function") await any.dispose();
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
214
|
```
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
async function init() {
|
|
447
|
-
return LoggerBootstrap.create({
|
|
448
|
-
minLevel: envs.logger.LOGGER_LEVEL ?? "debug",
|
|
449
|
-
pii: {
|
|
450
|
-
enabled: envs.logger.LOGGER_PII_ENABLED,
|
|
451
|
-
includeDefaults: envs.logger.LOGGER_PII_INCLUDE_DEFAULTS,
|
|
452
|
-
deep: true,
|
|
453
|
-
},
|
|
454
|
-
adapters: {
|
|
455
|
-
// NOTA: Opcionales depende de las necesidades del cliente
|
|
456
|
-
fs: envs.logger.LOGGER_FS_PATH
|
|
457
|
-
? {
|
|
458
|
-
basePath: envs.logger.LOGGER_FS_PATH,
|
|
459
|
-
fileNamePattern: "app-{yyyy}{MM}{dd}.log",
|
|
460
|
-
rotationPolicy: { by: "day" },
|
|
461
|
-
}
|
|
462
|
-
: undefined,
|
|
463
|
-
mongo: envs.logger.MONGO_URL
|
|
464
|
-
? {
|
|
465
|
-
url: envs.logger.MONGO_URL!,
|
|
466
|
-
dbName: envs.logger.MONGO_DB_NAME!,
|
|
467
|
-
collectionName: envs.logger.MONGO_COLLECTION ?? "logs",
|
|
468
|
-
retentionDays: Number(envs.logger.LOGGER_MONGO_RETENTION_DAYS) || 0,
|
|
469
|
-
}
|
|
470
|
-
: undefined,
|
|
471
|
-
postgres: envs.logger.POSTGRES_URL
|
|
472
|
-
? {
|
|
473
|
-
url: envs.logger.POSTGRES_URL!,
|
|
474
|
-
schema: envs.logger.POSTGRES_SCHEMA ?? "public",
|
|
475
|
-
table: envs.logger.POSTGRES_TABLE ?? "logs",
|
|
476
|
-
retentionDays: Number(envs.logger.LOGGER_PG_RETENTION_DAYS) || 0,
|
|
477
|
-
}
|
|
478
|
-
: undefined,
|
|
479
|
-
// ---
|
|
480
|
-
},
|
|
481
|
-
});
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
// 1. Es una promesa singleton de LoggerBootstrap
|
|
485
|
-
// 2. usa el operador nullish-coalescing (??) para: Reusar globalThis.__LOGGER_BOOT__ si ya existe. En caso contrario crea y memoriza (= init()) la promesa si no existe aún.
|
|
486
|
-
// 3. Garantiza una sola inicialización global del sistema de logging (adapters, datasources, PII, etc.) aunque el módulo se importe múltiples veces
|
|
487
|
-
export const bootReady: Promise<LoggerBootstrap> =
|
|
488
|
-
globalThis.__LOGGER_BOOT__ ?? (globalThis.__LOGGER_BOOT__ = init());
|
|
489
|
-
|
|
490
|
-
// 1. Es una promesa que resuelve directamente al logger
|
|
491
|
-
// 2. Hace un map de la promesa anterior: bootReady.then(b => b.logger).
|
|
492
|
-
export const loggerReady = bootReady.then((b) => b.logger);
|
|
493
|
-
|
|
494
|
-
// 1. Espera a bootReady y llama boot.flush(), que a su vez pide al logger/datasources que vacíen buffers pendientes (útil antes de apagar el proceso o en tests).
|
|
495
|
-
export async function flushLogs() {
|
|
496
|
-
const boot = await bootReady;
|
|
497
|
-
await boot.flush();
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
// 1. Espera a bootReady y llama boot.dispose(), que cierra recursos (conexiones a Mongo/Postgres, file handles, etc.)
|
|
501
|
-
export async function disposeLogs() {
|
|
502
|
-
const boot = await bootReady;
|
|
503
|
-
await boot.dispose();
|
|
504
|
-
}
|
|
215
|
+
src/
|
|
216
|
+
├─ domain/ # Reglas de negocio puras
|
|
217
|
+
│ ├─ entities/
|
|
218
|
+
│ ├─ value-objects/ # LogLevel
|
|
219
|
+
│ ├─ services/ # PiiRedactor, MessageNormalizer
|
|
220
|
+
│ ├─ ports/ # Interfaces/contratos
|
|
221
|
+
│ └─ types/
|
|
222
|
+
├─ application/ # Casos de uso
|
|
223
|
+
│ ├─ use-cases/ # SaveLog, GetLogs, FlushBuffers
|
|
224
|
+
│ └─ factory/ # LoggerFactory
|
|
225
|
+
└─ infrastructure/ # Implementaciones concretas
|
|
226
|
+
└─ services/ # DataSourceService (composite)
|
|
505
227
|
```
|
|
506
228
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
229
|
+
### Casos de Uso Principales
|
|
230
|
+
|
|
231
|
+
- **[`SaveLogUseCase`](src/application/use-cases/save-log.use-case.ts)** - Guarda logs aplicando filtros de nivel y PII
|
|
232
|
+
- **[`GetLogsUseCase`](src/application/use-cases/get-logs.use-case.ts)** - Recupera logs con filtros
|
|
233
|
+
- **[`FlushBuffersUseCase`](src/application/use-cases/flush-buffers.use-case.ts)** - Vacía buffers de datasources
|
|
234
|
+
|
|
235
|
+
### Servicios de Dominio
|
|
236
|
+
|
|
237
|
+
- **[`PiiRedactor`](src/domain/services/pii-redactor.service.ts)** - Enmascara datos sensibles
|
|
238
|
+
- **[`MessageNormalizer`](src/domain/services/message-normalizer.service.ts)** - Normaliza mensajes de log
|
|
239
|
+
- **[`LogLevelService`](src/domain/services/log-level.service.ts)** - Maneja niveles de log
|
|
240
|
+
|
|
241
|
+
## 🔌 Plugins Disponibles
|
|
242
|
+
|
|
243
|
+
| Plugin | Descripción | NPM |
|
|
244
|
+
| -------------------------------- | -------------------------- | --------------------------------------------------------------- |
|
|
245
|
+
| `@jmlq/logger-plugin-fs` | Persistencia en archivos | [npm](https://npmjs.com/package/@jmlq/logger-plugin-fs) |
|
|
246
|
+
| `@jmlq/logger-plugin-mongo` | Persistencia en MongoDB | [npm](https://npmjs.com/package/@jmlq/logger-plugin-mongo) |
|
|
247
|
+
| `@jmlq/logger-plugin-postgresql` | Persistencia en PostgreSQL | [npm](https://npmjs.com/package/@jmlq/logger-plugin-postgresql) |
|
|
248
|
+
|
|
249
|
+
## 🧪 Testing
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
import { LoggerFactory, LogLevel } from "@jmlq/logger";
|
|
253
|
+
|
|
254
|
+
describe("Logger Tests", () => {
|
|
255
|
+
it("debe enmascarar PII correctamente", async () => {
|
|
256
|
+
const logs: any[] = [];
|
|
257
|
+
const mockDatasource = {
|
|
258
|
+
name: "mock",
|
|
259
|
+
async save(log: any) {
|
|
260
|
+
logs.push(log);
|
|
261
|
+
},
|
|
262
|
+
async find() {
|
|
263
|
+
return [];
|
|
264
|
+
},
|
|
265
|
+
async flush() {},
|
|
266
|
+
async dispose() {},
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
const logger = LoggerFactory.create({
|
|
270
|
+
datasources: mockDatasource,
|
|
271
|
+
redactorOptions: { enabled: true },
|
|
272
|
+
});
|
|
549
273
|
|
|
550
|
-
|
|
551
|
-
app.use(
|
|
552
|
-
async (err: any, req: Request, res: Response, _next: NextFunction) => {
|
|
553
|
-
const status = err?.statusCode ?? 500;
|
|
554
|
-
// @ts-expect-error
|
|
555
|
-
const logger = req.logger ?? (await loggerReady);
|
|
556
|
-
await logger.error("http_error", {
|
|
557
|
-
message: err?.message,
|
|
558
|
-
stack: err?.stack,
|
|
559
|
-
status,
|
|
560
|
-
});
|
|
561
|
-
res
|
|
562
|
-
.status(status)
|
|
563
|
-
.json({ error: err?.message ?? "Internal Server Error" });
|
|
564
|
-
}
|
|
565
|
-
);
|
|
274
|
+
await logger.info("Usuario: user@example.com");
|
|
566
275
|
|
|
567
|
-
|
|
568
|
-
|
|
276
|
+
expect(logs[0].message).not.toContain("user@example.com");
|
|
277
|
+
expect(logs[0].message).toContain("[EMAIL]");
|
|
278
|
+
});
|
|
279
|
+
});
|
|
569
280
|
```
|
|
570
281
|
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
> - `loggerReady` es una Promise → debe resolverse con `await`.
|
|
574
|
-
> - `flushLogs()` y `disposeLogs()` deben usarse en procesos que cierran conexiones (ej. `SIGINT`, `SIGTERM`).
|
|
575
|
-
> - Los patrones definidos en `pii.ts` se combinan con los patrones por defecto cuando `LOGGER_PII_INCLUDE_DEFAULTS=true`.
|
|
576
|
-
> - El logger puede trabajar con **un único datasource** o con **CompositeDatasource** para múltiples.
|
|
577
|
-
|
|
578
|
-
---
|
|
579
|
-
|
|
580
|
-
### 🧪 Escenarios
|
|
282
|
+
## 🚨 Troubleshooting
|
|
581
283
|
|
|
582
|
-
|
|
583
|
-
> - **Solo MongoDB** → logs en colección `logs`.
|
|
584
|
-
> - **Solo PostgreSQL** → logs en tabla `logs`.
|
|
585
|
-
> - **Combinado** → fan-out a varios destinos simultáneamente.
|
|
586
|
-
> - **Extensión** → implementar `ILogDatasource`.
|
|
284
|
+
**Error: No datasources válidos**
|
|
587
285
|
|
|
588
|
-
|
|
286
|
+
- Asegúrate de configurar al menos un datasource
|
|
287
|
+
- Verifica las variables de entorno
|
|
589
288
|
|
|
590
|
-
|
|
289
|
+
**MongoDB no conecta**
|
|
591
290
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
> - **Postgres** → ejecutar `ensurePostgresSchema()` para crear tabla logs si no existe.
|
|
595
|
-
> - **Alto volumen de logs** → implementar `flush()` o batching en el datasource.
|
|
291
|
+
- Verifica la URL de conexión
|
|
292
|
+
- Incluye `authSource=admin` si usas usuarios root
|
|
596
293
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
## 🧪 Tests
|
|
600
|
-
|
|
601
|
-
```ts
|
|
602
|
-
import { createLogger, LogLevel } from "@jmlq/logger";
|
|
603
|
-
import { FileSystemDatasource } from "@jmlq/logger-plugin-fs";
|
|
604
|
-
|
|
605
|
-
test("logger redacta PII en FS", async () => {
|
|
606
|
-
const fsDs = new FileSystemDatasource({ filePath: "./logs/test.log" });
|
|
607
|
-
const logger = createLogger(fsDs, {
|
|
608
|
-
minLevel: LogLevel.DEBUG,
|
|
609
|
-
pii: { enabled: true },
|
|
610
|
-
});
|
|
611
|
-
|
|
612
|
-
await logger.info("Inicio de sesión", { user: "demo", password: "123456" });
|
|
613
|
-
|
|
614
|
-
// Luego verificar que el archivo test.log no contiene la contraseña en claro
|
|
615
|
-
});
|
|
616
|
-
```
|
|
294
|
+
**Alto uso de memoria**
|
|
617
295
|
|
|
618
|
-
|
|
296
|
+
- Implementa límites en las consultas
|
|
297
|
+
- Usa `flush()` periódicamente para vaciar buffers
|
|
619
298
|
|
|
620
|
-
|
|
299
|
+
## 📄 Más Información
|
|
621
300
|
|
|
622
|
-
|
|
301
|
+
- **[Arquitectura Detallada](./ARQUITECTURA.md)** - Documentación técnica completa
|
|
302
|
+
- **[Guía de Instalación](./install.md)** - Configuración paso a paso
|
|
303
|
+
- **[Ejemplos](./examples/)** - Códigos de ejemplo funcionales
|
|
623
304
|
|
|
624
|
-
##
|
|
305
|
+
## 📝 Licencia
|
|
625
306
|
|
|
626
307
|
MIT © Mauricio Lahuasi
|