@jmlq/logger 0.1.0-alpha.2 → 0.1.0-alpha.20
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 +247 -0
- package/architecture.md +171 -0
- package/dist/application/factory/index.d.ts +1 -0
- package/dist/{config → application/factory}/index.js +1 -2
- package/dist/application/factory/logger.factory.d.ts +10 -0
- package/dist/application/factory/logger.factory.js +71 -0
- package/dist/application/index.d.ts +2 -0
- package/dist/{presentation → application}/index.js +1 -0
- package/dist/application/use-cases/flush-buffers.use-case.d.ts +6 -0
- package/dist/application/use-cases/flush-buffers.use-case.js +13 -0
- package/dist/application/use-cases/get-logs.use-case.d.ts +8 -0
- package/dist/application/use-cases/get-logs.use-case.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/index.d.ts +1 -0
- package/dist/application/use-cases/save-log/index.js +17 -0
- package/dist/application/use-cases/save-log/save-log.props.d.ts +7 -0
- package/dist/{config/interfaces/index.js → application/use-cases/save-log/save-log.props.js} +0 -1
- package/dist/application/use-cases/save-log.use-case.d.ts +8 -0
- package/dist/application/use-cases/save-log.use-case.js +27 -0
- package/dist/domain/index.d.ts +7 -0
- package/dist/domain/index.js +7 -0
- package/dist/domain/model/index.d.ts +3 -0
- package/dist/domain/model/index.js +19 -0
- package/dist/domain/model/log-entry.model.d.ts +8 -0
- package/dist/domain/model/log-entry.model.js +2 -0
- package/dist/domain/model/pii-options.model.d.ts +8 -0
- package/dist/domain/model/pii-options.model.js +2 -0
- package/dist/domain/model/pii-replacement-rule.d.ts +5 -0
- package/dist/domain/model/pii-replacement-rule.js +2 -0
- package/dist/domain/ports/create-logger-options.port.d.ts +7 -0
- package/dist/domain/ports/create-logger-options.port.js +2 -0
- package/dist/domain/ports/index.d.ts +5 -0
- package/dist/domain/ports/index.js +22 -0
- package/dist/domain/ports/log-datasource.port.d.ts +10 -0
- package/dist/domain/ports/log-datasource.port.js +2 -0
- package/dist/domain/ports/logger-factory-config.port.d.ts +28 -0
- package/dist/domain/ports/logger-factory-config.port.js +2 -0
- package/dist/domain/ports/logger.port.d.ts +15 -0
- package/dist/domain/ports/logger.port.js +2 -0
- package/dist/domain/ports/pii-redactor.port.d.ts +5 -0
- package/dist/domain/ports/pii-redactor.port.js +2 -0
- package/dist/domain/request/index.d.ts +1 -0
- package/dist/domain/request/index.js +17 -0
- package/dist/domain/request/log-filter.request.d.ts +9 -0
- package/dist/domain/request/log-filter.request.js +2 -0
- package/dist/domain/response/index.d.ts +1 -0
- package/dist/domain/response/index.js +17 -0
- package/dist/domain/response/log.response.d.ts +8 -0
- package/dist/domain/response/log.response.js +2 -0
- package/dist/domain/services/index.d.ts +1 -1
- package/dist/domain/services/index.js +1 -1
- package/dist/domain/services/pii-redactor.service.d.ts +10 -0
- package/dist/domain/services/pii-redactor.service.js +68 -0
- package/dist/domain/types/index.d.ts +1 -0
- package/dist/domain/types/index.js +17 -0
- package/dist/domain/types/log-message.type.d.ts +1 -0
- package/dist/domain/types/log-message.type.js +2 -0
- package/dist/domain/utils/index.d.ts +3 -0
- package/dist/domain/utils/index.js +19 -0
- package/dist/domain/utils/normalize-message.util.d.ts +3 -0
- package/dist/domain/utils/normalize-message.util.js +8 -0
- package/dist/domain/utils/parse-log-level.util.d.ts +2 -0
- package/dist/domain/utils/parse-log-level.util.js +27 -0
- package/dist/domain/utils/pii-regex.util.d.ts +2 -0
- package/dist/domain/utils/pii-regex.util.js +13 -0
- package/dist/domain/value-objects/index.d.ts +1 -0
- package/dist/domain/value-objects/index.js +17 -0
- package/dist/domain/value-objects/log-level.vo.d.ts +8 -0
- package/dist/domain/value-objects/log-level.vo.js +13 -0
- package/dist/index.d.ts +8 -4
- package/dist/index.js +32 -7
- package/dist/infrastructure/index.d.ts +1 -0
- package/dist/infrastructure/index.js +17 -0
- package/dist/infrastructure/services/datasource.service.d.ts +18 -0
- package/dist/infrastructure/services/datasource.service.js +102 -0
- package/dist/infrastructure/services/index.d.ts +1 -0
- package/dist/infrastructure/services/index.js +17 -0
- package/dist/infrastructure/types/index.d.ts +1 -0
- package/dist/infrastructure/types/index.js +17 -0
- package/dist/infrastructure/types/on-data-source-error.type.d.ts +5 -0
- package/dist/infrastructure/types/on-data-source-error.type.js +2 -0
- package/install.md +632 -0
- package/package.json +37 -11
- 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/services/pii-redactor.d.ts +0 -27
- package/dist/domain/services/pii-redactor.js +0 -139
- package/dist/interfaces/index.d.ts +0 -35
- package/dist/interfaces/index.js +0 -13
- package/dist/presentation/factory/index.d.ts +0 -2
- package/dist/presentation/factory/index.js +0 -74
- package/dist/presentation/index.d.ts +0 -1
package/README.md
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# @jmlq/logger
|
|
2
|
+
|
|
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).
|
|
4
|
+
|
|
5
|
+
## 📦 Instalación
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Core del logger
|
|
9
|
+
npm install @jmlq/logger
|
|
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
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## 🚀 Uso Básico
|
|
18
|
+
|
|
19
|
+
Con esta configuración se busca:
|
|
20
|
+
|
|
21
|
+
- Tener un **logger centralizado** basado en `@jmlq/logger`.
|
|
22
|
+
- Enviar logs a uno o varios de estos destinos (**opcionales** y combinables):
|
|
23
|
+
- Sistema de archivos (`@jmlq/logger-plugin-fs`)
|
|
24
|
+
- MongoDB (`@jmlq/logger-plugin-mongo`)
|
|
25
|
+
- PostgreSQL (`@jmlq/logger-plugin-postgresql`)
|
|
26
|
+
- Integrar el logger con:
|
|
27
|
+
- Express (por request) mediante un middleware (`req.logger` + `req.requestId`).
|
|
28
|
+
- El ciclo de vida de la app: arranque, apagado limpio y fallos no controlados.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 🎯 Características Principales
|
|
33
|
+
|
|
34
|
+
### Niveles de Log
|
|
35
|
+
|
|
36
|
+
- `TRACE` (10) - Información muy detallada
|
|
37
|
+
- `DEBUG` (20) - Información de depuración
|
|
38
|
+
- `INFO` (30) - Información general
|
|
39
|
+
- `WARN` (40) - Advertencias
|
|
40
|
+
- `ERROR` (50) - Errores
|
|
41
|
+
- `FATAL` (60) - Errores críticos
|
|
42
|
+
|
|
43
|
+
### Enmascarado PII Automático
|
|
44
|
+
|
|
45
|
+
El logger detecta y enmascara automáticamente datos sensibles utilizando patrones personalizados:
|
|
46
|
+
|
|
47
|
+
```js
|
|
48
|
+
|
|
49
|
+
patterns: [
|
|
50
|
+
// Tarjetas de crédito
|
|
51
|
+
{
|
|
52
|
+
pattern: "\\b\\d{4}[-\\s]?\\d{4}[-\\s]?\\d{4}[-\\s]?\\d{4}\\b",
|
|
53
|
+
replaceWith: "****-****-****-****",
|
|
54
|
+
},
|
|
55
|
+
// Números de cuenta bancaria
|
|
56
|
+
{
|
|
57
|
+
pattern: "\\b\\d{10,20}\\b",
|
|
58
|
+
replaceWith: "****ACCOUNT****",
|
|
59
|
+
},
|
|
60
|
+
// Tokens JWT
|
|
61
|
+
{
|
|
62
|
+
pattern: "eyJ[A-Za-z0-9-_=]+\\.[A-Za-z0-9-_=]+\\.?[A-Za-z0-9-_.+/=]*",
|
|
63
|
+
replaceWith: "****JWT****",
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Por ejemplo se tiene la siguiente información:
|
|
70
|
+
|
|
71
|
+
```js
|
|
72
|
+
[
|
|
73
|
+
{
|
|
74
|
+
id: "1",
|
|
75
|
+
name: "John Doe",
|
|
76
|
+
email: "john.doe@example.com",
|
|
77
|
+
card: "1234-5678-9012-3456",
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: "2",
|
|
81
|
+
name: "Jane Doe",
|
|
82
|
+
email: "jane.doe@example.com",
|
|
83
|
+
card: "6258-3842-9524-3251",
|
|
84
|
+
},
|
|
85
|
+
];
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Los logs registrarán lo siguiente:
|
|
89
|
+
|
|
90
|
+
> - **File .log** (`@jmlq/logger-plugin-fs`)
|
|
91
|
+
|
|
92
|
+
```js
|
|
93
|
+
{
|
|
94
|
+
"level": 30,
|
|
95
|
+
"message": "Users listed successfully",
|
|
96
|
+
"meta": {
|
|
97
|
+
"count": 2,
|
|
98
|
+
"result": [
|
|
99
|
+
{
|
|
100
|
+
"id": "1",
|
|
101
|
+
"name": "John Doe",
|
|
102
|
+
"email": "***@***",
|
|
103
|
+
"card": "****-****-****-****"
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"id": "2",
|
|
107
|
+
"name": "Jane Doe",
|
|
108
|
+
"email": "***@***",
|
|
109
|
+
"card": "****-****-****-****"
|
|
110
|
+
}
|
|
111
|
+
]
|
|
112
|
+
},
|
|
113
|
+
"timestamp": 1765400661565,
|
|
114
|
+
"_id": "6939e0556925736129e1008d"
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
> - **MongoDB** (`@jmlq/logger-plugin-mongo`)
|
|
119
|
+
|
|
120
|
+

|
|
121
|
+
|
|
122
|
+
> - **MongoDB** (`@jmlq/logger-plugin-mongo`)
|
|
123
|
+
|
|
124
|
+

|
|
125
|
+
|
|
126
|
+
### Filtros y Consultas
|
|
127
|
+
|
|
128
|
+
#### 1. Obtener todos los logs de nivel WARN o superior (en este caso retorna WARN, ERROR y FATAL)
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
const errors = await _req.logger?.getLogs({
|
|
132
|
+
levelMin: parseLogLevel("WARN"),
|
|
133
|
+
});
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Resultado:
|
|
137
|
+
|
|
138
|
+
```ini
|
|
139
|
+
[2025-12-10T21:19:58.804Z] [50] app.error {"name":"AppError","code":"INTERNAL","message":"Unhandled exception","retryable":false,"meta":null,"cause":{"cause":{}},"stack":"AppError: Unhandled exception\n at AppError.internal (D:\\Fuentes\\Core\\ml-dev-rest-api\\dist\\shared\\errors\\app-error.js:61:16)\n at D:\\Fuentes\\Core\\ml-dev-rest-api\\dist\\presentation\\middlewares\\http\\error.middleware.js:36:40\n at Layer.handleError (D:\\Fuentes\\Core\\ml-dev-rest-api\\node_modules\\router\\lib\\layer.js:116:17)\n
|
|
140
|
+
at trimPrefix (D:\\Fuentes\\Core\\ml-dev-rest-api\\node_modules\\router\\index.js:340:13)\n at D:\\Fuentes\\Core\\ml-dev-rest-api\\node_modules\\router\\index.js:297:9\n at processParams (D:\\Fuentes\\Core\\ml-dev-rest-api\\node_modules\\router\\index.js:582:12)\n at Immediate.next (D:\\Fuentes\\Core\\ml-dev-rest-api\\node_modules\\router\\index.js:291:5)\n at Immediate._onImmediate (D:\\Fuentes\\Core\\ml-dev-rest-api\\node_modules\\router\\index.js:688:15)\n at process.processImmediate (node:internal/timers:485:21)"}
|
|
141
|
+
[2025-12-10T21:19:58.804Z] [50] app.error {"code":"INTERNAL","name":"AppError","cause":{"cause":{}},"stack":"AppError: Unhandled exception\n at AppError.internal (D:\\Fuentes\\Core\\ml-dev-rest-api\\dist\\shared\\errors\\app-error.js:61:16)\n at D:\\Fuentes\\Core\\ml-dev-rest-api\\dist\\presentation\\middlewares\\http\\error.middleware.js:36:40\n at Layer.handleError (D:\\Fuentes\\Core\\ml-dev-rest-api\\node_modules\\router\\lib\\layer.js:116:17)\n at trimPrefix (D:\\Fuentes\\Core\\ml-dev-rest-api\\node_modules\\router\\index.js:340:13)\n at D:\\Fuentes\\Core\\ml-dev-rest-api\\node_modules\\router\\index.js:297:9\n at processParams (D:\\Fuentes\\Core\\ml-dev-rest-api\\node_modules\\router\\index.js:582:12)\n at Immediate.next (D:\\Fuentes\\Core\\ml-dev-rest-api\\node_modules\\router\\index.js:291:5)\n at Immediate._onImmediate (D:\\Fuentes\\Core\\ml-dev-rest-api\\node_modules\\router\\index.js:688:15)\n at process.processImmediate (node:internal/timers:485:21)","message":"Unhandled exception","retryable":false}
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**NOTA**: `parseLogLevel` es un helper para convertir un `string` a `LogLevel`.
|
|
146
|
+
|
|
147
|
+
#### 2. Filtrar por rango de fechas
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
const yesterday = Date.now() - 24 * 60 * 60 * 1000;
|
|
151
|
+
const recentLogs = await _req.logger?.getLogs({
|
|
152
|
+
since: yesterday,
|
|
153
|
+
until: Date.now(),
|
|
154
|
+
});
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Resultado:
|
|
158
|
+
|
|
159
|
+
```ini
|
|
160
|
+
[2025-12-10T22:21:58.500Z] [30] app.shutdown.begin {"reason":"SIGINT"}
|
|
161
|
+
[2025-12-10T21:12:59.216Z] [50] app.error {"name":"AppError","code":"INTERNAL","message":"Unhandled exception","retryable":false,"meta":null,"cause":{"cause":{}},"stack":"AppError: Unhandled exception\n at AppError.internal (D:\\Fuentes\\Core\\ml-dev-rest-api\\dist\\shared\\errors\\app-error.js:61:16)\n at D:\\Fuentes\\Core\\ml-dev-rest-api\\dist\\presentation\\middlewares\\http\\error.middleware.js:36:40\n at Layer.handleError (D:\\Fuentes\\Core\\ml-dev-rest-api\\node_modules\\router\\lib\\layer.js:116:17)\n at trimPrefix (D:\\Fuentes\\Core\\ml-dev-rest-api\\node_modules\\router\\index.js:340:13)\n at D:\\Fuentes\\Core\\ml-dev-rest-api\\node_modules\\router\\index.js:297:9\n at processParams (D:\\Fuentes\\Core\\ml-dev-rest-api\\node_modules\\router\\index.js:582:12)\n at Immediate.next (D:\\Fuentes\\Core\\ml-dev-rest-api\\node_modules\\router\\index.js:291:5)\n at Immediate._onImmediate (D:\\Fuentes\\Core\\ml-dev-rest-api\\node_modules\\router\\index.js:688:15)\n at process.processImmediate (node:internal/timers:485:21)"}
|
|
162
|
+
[2025-12-10T21:04:21.565Z] [30] Users listed successfully {"count":2,"result":[{"id":"1","name":"John Doe","email":"***@***","card":"****-****-****-****"},{"id":"2","name":"Jane Doe","email":"***@***","card":"****-****-****-****"}]}
|
|
163
|
+
[2025-12-10T21:04:21.565Z] [30] Users listed successfully {"count":2,"result":[{"id":"1","card":"****-****-****-****","name":"John Doe","email":"***@***"},{"id":"2","card":"****-****-****-****","name":"Jane Doe","email":"***@***"}]}
|
|
164
|
+
[2025-12-10T21:03:32.910Z] [30] http.start {"host":"127.0.0.1","port":3000}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
#### 3. Búsqueda por texto (en el campo mensaje del log)
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
const userLogs = await _req.logger?.getLogs({
|
|
171
|
+
query: "Users",
|
|
172
|
+
});
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Resultado:
|
|
176
|
+
|
|
177
|
+
```ini
|
|
178
|
+
[2025-12-10T22:58:09.874Z] [30] Users listed successfully {"count":2,"result":[{"id":"1","card":"****-****-****-****","name":"John Doe","email":"***@***"},{"id":"2","card":"****-****-****-****","name":"Jane Doe","email":"***@***"}]}
|
|
179
|
+
[2025-12-10T22:57:28.483Z] [30] Users listed successfully {"count":2,"result":[{"id":"1","name":"John Doe","email":"***@***","card":"****-****-****-****"},{"id":"2","name":"Jane Doe","email":"***@***","card":"****-****-****-****"}]}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
#### 4. Combinación de filtros
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
const logs = await _req.logger?.getLogs({
|
|
186
|
+
levelMin: parseLogLevel("INFO"),
|
|
187
|
+
since: Date.now() - 2 * 60 * 60 * 1000, // Últimas 2 horas
|
|
188
|
+
query: "successfully",
|
|
189
|
+
});
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Resultado:
|
|
193
|
+
|
|
194
|
+
```ini
|
|
195
|
+
[2025-12-10T23:05:31.791Z] [30] Users listed successfully {"count":2,"result":[{"id":"1","name":"John Doe","email":"***@***","card":"****-****-****-****"},{"id":"2","name":"Jane Doe","email":"***@***","card":"****-****-****-****"}]}
|
|
196
|
+
[2025-12-10T23:05:31.791Z] [30] Users listed successfully {"count":2,"result":[{"id":"1","card":"****-****-****-****","name":"John Doe","email":"***@***"},{"id":"2","card":"****-****-****-****","name":"Jane Doe","email":"***@***"}]}
|
|
197
|
+
[2025-12-10T22:58:09.874Z] [30] Users listed successfully {"count":2,"result":[{"id":"1","name":"John Doe","email":"***@***","card":"****-****-****-****"},{"id":"2","name":"Jane Doe","email":"***@***","card":"****-****-****-****"}]}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## 🏗️ Arquitectura
|
|
201
|
+
|
|
202
|
+
El paquete sigue **Arquitectura Limpia**:
|
|
203
|
+
|
|
204
|
+
```
|
|
205
|
+
src/
|
|
206
|
+
├─ domain/ # Reglas de negocio puras
|
|
207
|
+
│ ├─ entities/
|
|
208
|
+
│ ├─ value-objects/ # LogLevel
|
|
209
|
+
│ ├─ services/ # PiiRedactor, MessageNormalizer
|
|
210
|
+
│ ├─ ports/ # Interfaces/contratos
|
|
211
|
+
│ └─ types/
|
|
212
|
+
├─ application/ # Casos de uso
|
|
213
|
+
│ ├─ use-cases/ # SaveLog, GetLogs, FlushBuffers
|
|
214
|
+
│ └─ factory/ # LoggerFactory
|
|
215
|
+
└─ infrastructure/ # Implementaciones concretas
|
|
216
|
+
└─ services/ # DataSourceService (composite)
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Casos de Uso Principales
|
|
220
|
+
|
|
221
|
+
- **[`SaveLogUseCase`](src/application/use-cases/save-log.use-case.ts)** - Guarda logs aplicando filtros de nivel y PII
|
|
222
|
+
- **[`GetLogsUseCase`](src/application/use-cases/get-logs.use-case.ts)** - Recupera logs con filtros
|
|
223
|
+
- **[`FlushBuffersUseCase`](src/application/use-cases/flush-buffers.use-case.ts)** - Vacía buffers de datasources
|
|
224
|
+
|
|
225
|
+
### Servicios de Dominio
|
|
226
|
+
|
|
227
|
+
- **[`PiiRedactor`](src/domain/services/pii-redactor.service.ts)** - Enmascara datos sensibles
|
|
228
|
+
- **[`MessageNormalizer`](src/domain/services/message-normalizer.service.ts)** - Normaliza mensajes de log
|
|
229
|
+
- **[`LogLevelService`](src/domain/services/log-level.service.ts)** - Maneja niveles de log
|
|
230
|
+
|
|
231
|
+
## 🔌 Plugins Disponibles
|
|
232
|
+
|
|
233
|
+
| Plugin | Descripción | NPM |
|
|
234
|
+
| -------------------------------- | -------------------------- | --------------------------------------------------------------- |
|
|
235
|
+
| `@jmlq/logger-plugin-fs` | Persistencia en archivos | [npm](https://npmjs.com/package/@jmlq/logger-plugin-fs) |
|
|
236
|
+
| `@jmlq/logger-plugin-mongo` | Persistencia en MongoDB | [npm](https://npmjs.com/package/@jmlq/logger-plugin-mongo) |
|
|
237
|
+
| `@jmlq/logger-plugin-postgresql` | Persistencia en PostgreSQL | [npm](https://npmjs.com/package/@jmlq/logger-plugin-postgresql) |
|
|
238
|
+
|
|
239
|
+
## 📄 Más Información
|
|
240
|
+
|
|
241
|
+
- **[Arquitectura Detallada](./architecture.md)** - Documentación técnica completa
|
|
242
|
+
- **[Guía de Instalación](./install.md)** - Configuración paso a paso
|
|
243
|
+
- **[Ejemplos](./examples/)** - Códigos de ejemplo funcionales
|
|
244
|
+
|
|
245
|
+
## 📝 Licencia
|
|
246
|
+
|
|
247
|
+
MIT © Mauricio Lahuasi
|
package/architecture.md
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# Arquitectura del Paquete @jmlq/logger
|
|
2
|
+
|
|
3
|
+
## Visión General
|
|
4
|
+
|
|
5
|
+
El paquete `@jmlq/logger` está diseñado siguiendo los principios de **Arquitectura Limpia**, proporcionando un sistema de logging extensible y desacoplado que permite registrar logs en múltiples destinos mediante plugins y soporta enmascarado de datos sensibles (PII - Personally Identifiable Information).
|
|
6
|
+
|
|
7
|
+
## Principios de Diseño
|
|
8
|
+
|
|
9
|
+
- **Separación de responsabilidades**: Cada capa tiene una responsabilidad específica
|
|
10
|
+
- **Inversión de dependencias**: Las capas internas no dependen de las externas
|
|
11
|
+
- **Extensibilidad**: Sistema de plugins para diferentes adaptadores de persistencia
|
|
12
|
+
- **Testabilidad**: Arquitectura que facilita la creación de pruebas unitarias
|
|
13
|
+
- **Configurabilidad**: Múltiples opciones de configuración mediante variables de entorno
|
|
14
|
+
|
|
15
|
+
## Estructura de Capas
|
|
16
|
+
|
|
17
|
+
### 📁 Domain (Capa de Dominio)
|
|
18
|
+
|
|
19
|
+
La capa más interna que contiene la lógica de negocio pura, sin dependencias externas.
|
|
20
|
+
|
|
21
|
+
#### **Entities & Value Objects**
|
|
22
|
+
|
|
23
|
+
- [`LogLevelVo`](src/domain/value-objects/log-level.vo.ts): Objeto de valor que representa los niveles de log
|
|
24
|
+
|
|
25
|
+
#### **Ports (Interfaces)**
|
|
26
|
+
|
|
27
|
+
- [`ILoggerPort`](src/domain/ports/logger.port.ts): Contrato principal del logger
|
|
28
|
+
- [`ILogDatasourcePort`](src/domain/ports/log-datasource.port.ts): Contrato para fuentes de datos
|
|
29
|
+
- [`ILoggerServicePort`](src/domain/ports/logger-service.port.ts): Contrato para servicios de logging
|
|
30
|
+
- [`IPiiRedactorPort`](src/domain/ports/pii-redactor.port.ts): Contrato para enmascarado PII
|
|
31
|
+
- [`ILoggerFactoryConfigPort`](src/domain/ports/logger-factory-config.port.ts): Contrato de configuración del factory
|
|
32
|
+
|
|
33
|
+
#### **Services (Servicios de Dominio)**
|
|
34
|
+
|
|
35
|
+
- [`LogLevelService`](src/domain/services/log-level.service.ts): Lógica para manejo de niveles de log
|
|
36
|
+
- [`MessageNormalizerService`](src/domain/services/message-normalizer.service.ts): Normalización de mensajes
|
|
37
|
+
- [`PiiPatternService`](src/domain/services/pii-pattern.service.ts): Gestión de patrones PII
|
|
38
|
+
- [`PiiRedactorService`](src/domain/services/pii-redactor.service.ts): Enmascarado de datos sensibles
|
|
39
|
+
|
|
40
|
+
#### **Types & DTOs**
|
|
41
|
+
|
|
42
|
+
- **Request**: [`SaveLogProps`](src/domain/request/save-log.props.ts), [`GetLogsFilterProps`](src/domain/request/get-logs-filter.props.ts), [`PiiOptionsProps`](src/domain/request/pii-options.props.ts)
|
|
43
|
+
- **Response**: [`LogResponse`](src/domain/response/log.response.ts)
|
|
44
|
+
- **Types**: [`LogMessageType`](src/domain/types/log-message.type.ts)
|
|
45
|
+
|
|
46
|
+
### 📁 Application (Capa de Aplicación)
|
|
47
|
+
|
|
48
|
+
Orquesta la lógica de negocio y coordina los casos de uso.
|
|
49
|
+
|
|
50
|
+
#### **Use Cases**
|
|
51
|
+
|
|
52
|
+
- [`SaveLogUseCase`](src/application/use-cases/save-log.use-case.ts): Guarda logs en los datasources configurados
|
|
53
|
+
- [`GetLogsUseCase`](src/application/use-cases/get-logs.use-case.ts): Recupera logs con filtros
|
|
54
|
+
- [`FlushBuffersUseCase`](src/application/use-cases/flush-buffers.use-case.ts): Vacía buffers de los datasources
|
|
55
|
+
|
|
56
|
+
#### **Factory**
|
|
57
|
+
|
|
58
|
+
- [`LoggerFactory`](src/application/factory/logger.factory.ts): Factory para crear instancias del logger
|
|
59
|
+
|
|
60
|
+
### 📁 Infrastructure (Capa de Infraestructura)
|
|
61
|
+
|
|
62
|
+
Implementaciones concretas de los contratos definidos en el dominio.
|
|
63
|
+
|
|
64
|
+
#### **Services**
|
|
65
|
+
|
|
66
|
+
- [`DatasourceService`](src/infrastructure/services/datasource.service.ts): Implementación del servicio de datasource
|
|
67
|
+
- [`DataSourceErrorHandlerType`](src/infrastructure/services/data-source-error-handler.type.ts): Manejo de errores de datasources
|
|
68
|
+
|
|
69
|
+
## Flujo de Dependencias
|
|
70
|
+
|
|
71
|
+
```txt
|
|
72
|
+
Infrastructure → Application → Domain
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
- **Domain**: No depende de nada, contiene la lógica de negocio pura
|
|
76
|
+
- **Application**: Depende solo del Domain, orquesta los casos de uso
|
|
77
|
+
- **Infrastructure**: Depende del Domain y Application, implementa los contratos
|
|
78
|
+
|
|
79
|
+
## Patrones Implementados
|
|
80
|
+
|
|
81
|
+
### **Factory Pattern**
|
|
82
|
+
|
|
83
|
+
El [`LoggerFactory`](src/application/factory/logger.factory.ts) centraliza la creación de instancias del logger con las configuraciones apropiadas.
|
|
84
|
+
|
|
85
|
+
### **Port-Adapter Pattern**
|
|
86
|
+
|
|
87
|
+
Los ports en el domain definen contratos que son implementados por adaptadores en infrastructure.
|
|
88
|
+
|
|
89
|
+
### **Use Case Pattern**
|
|
90
|
+
|
|
91
|
+
Cada operación principal está encapsulada en un caso de uso específico con un método `execute()`.
|
|
92
|
+
|
|
93
|
+
### **Dependency Injection**
|
|
94
|
+
|
|
95
|
+
Las dependencias se inyectan a través de constructores, facilitando el testing y la flexibilidad.
|
|
96
|
+
|
|
97
|
+
## Sistema de Plugins
|
|
98
|
+
|
|
99
|
+
El logger soporta múltiples adaptadores de persistencia:
|
|
100
|
+
|
|
101
|
+
- **Filesystem**: `@jmlq/logger-plugin-fs`
|
|
102
|
+
- **MongoDB**: `@jmlq/logger-plugin-mongo`
|
|
103
|
+
- **PostgreSQL**: `@jmlq/logger-plugin-postgresql`
|
|
104
|
+
|
|
105
|
+
Cada plugin implementa los ports definidos en el domain.
|
|
106
|
+
|
|
107
|
+
## Características Principales
|
|
108
|
+
|
|
109
|
+
### **PII (Personally Identifiable Information)**
|
|
110
|
+
|
|
111
|
+
- Enmascarado automático de datos sensibles
|
|
112
|
+
- Patrones configurables y extensibles
|
|
113
|
+
- Soporte para patrones por defecto y personalizados
|
|
114
|
+
|
|
115
|
+
### **Niveles de Log**
|
|
116
|
+
|
|
117
|
+
- debug, info, warn, error, fatal
|
|
118
|
+
- Configuración de nivel mínimo por entorno
|
|
119
|
+
|
|
120
|
+
### **Múltiples Datasources**
|
|
121
|
+
|
|
122
|
+
- Soporte simultáneo para múltiples backends
|
|
123
|
+
- Configuración opcional de cada adaptador
|
|
124
|
+
|
|
125
|
+
### **Configuración Flexible**
|
|
126
|
+
|
|
127
|
+
- Variables de entorno
|
|
128
|
+
- Configuración programática
|
|
129
|
+
- Políticas de retención de datos
|
|
130
|
+
|
|
131
|
+
## Ejemplo de Uso
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
import { LoggerBootstrap } from "@jmlq/logger";
|
|
135
|
+
|
|
136
|
+
const logger = await LoggerBootstrap.create({
|
|
137
|
+
minLevel: "debug",
|
|
138
|
+
pii: {
|
|
139
|
+
enabled: true,
|
|
140
|
+
includeDefaults: true,
|
|
141
|
+
deep: true,
|
|
142
|
+
},
|
|
143
|
+
adapters: {
|
|
144
|
+
fs: { basePath: "./logs" },
|
|
145
|
+
mongo: { url: "mongodb://localhost:27017", dbName: "logs" },
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
await logger.info("User logged in", {
|
|
150
|
+
userId: "12345",
|
|
151
|
+
email: "user@example.com",
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Testing
|
|
156
|
+
|
|
157
|
+
La arquitectura facilita el testing mediante:
|
|
158
|
+
|
|
159
|
+
> - Interfaces claramente definidas
|
|
160
|
+
> - Servicios desacoplados
|
|
161
|
+
> - Casos de uso aislados
|
|
162
|
+
> - Mocks sencillos de implementar
|
|
163
|
+
|
|
164
|
+
## Consideraciones de Rendimiento
|
|
165
|
+
|
|
166
|
+
> - Buffers para escritura asíncrona
|
|
167
|
+
> - Políticas de rotación de archivos
|
|
168
|
+
> - Retención configurable de datos
|
|
169
|
+
> - Procesamiento asíncrono de logs
|
|
170
|
+
|
|
171
|
+
Esta arquitectura asegura mantenibilidad, extensibilidad y testabilidad del sistema de logging, siguiendo las mejores prácticas de clean architecture.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./logger.factory";
|
|
@@ -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("./logger.factory"), exports);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ILoggerFactoryConfig, ILogger } from "../../domain/ports";
|
|
2
|
+
/**
|
|
3
|
+
* Factory principal de @jmlq/logger.
|
|
4
|
+
* Se encarga de:
|
|
5
|
+
* - Componer datasources (fan-out si hay varios)
|
|
6
|
+
* - Construir el redactor de PII
|
|
7
|
+
* - Instanciar y conectar los casos de uso
|
|
8
|
+
* - Exponer un servicio de logger de alto nivel
|
|
9
|
+
*/
|
|
10
|
+
export declare function createLogger(config: ILoggerFactoryConfig, source?: string): ILogger;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createLogger = createLogger;
|
|
4
|
+
const value_objects_1 = require("../../domain/value-objects");
|
|
5
|
+
const pii_redactor_service_1 = require("../../domain/services/pii-redactor.service");
|
|
6
|
+
const services_1 = require("../../infrastructure/services");
|
|
7
|
+
const use_cases_1 = require("../use-cases");
|
|
8
|
+
/**
|
|
9
|
+
* Factory principal de @jmlq/logger.
|
|
10
|
+
* Se encarga de:
|
|
11
|
+
* - Componer datasources (fan-out si hay varios)
|
|
12
|
+
* - Construir el redactor de PII
|
|
13
|
+
* - Instanciar y conectar los casos de uso
|
|
14
|
+
* - Exponer un servicio de logger de alto nivel
|
|
15
|
+
*/
|
|
16
|
+
function createLogger(config, source = "app-logger") {
|
|
17
|
+
// 1) Normalizar config
|
|
18
|
+
const minLevel = config.minLevel ?? value_objects_1.LogLevel.INFO;
|
|
19
|
+
const datasources = Array.isArray(config.datasources)
|
|
20
|
+
? config.datasources
|
|
21
|
+
: [config.datasources];
|
|
22
|
+
// 2) Componer datasource (si hay varios) con DataSourceService
|
|
23
|
+
const ds = datasources.length === 1
|
|
24
|
+
? datasources[0]
|
|
25
|
+
: new services_1.DataSourceService(datasources);
|
|
26
|
+
// 3) Crear/usar redactor de PII
|
|
27
|
+
const redactor = config.redactor ?? new pii_redactor_service_1.PiiRedactor(config.redactorOptions);
|
|
28
|
+
// 4) Construir casos de uso de Application
|
|
29
|
+
const saveLogUseCase = new use_cases_1.SaveLogUseCase({
|
|
30
|
+
ds,
|
|
31
|
+
minLevel,
|
|
32
|
+
redactor,
|
|
33
|
+
});
|
|
34
|
+
const getLogsUseCase = new use_cases_1.GetLogsUseCase(ds);
|
|
35
|
+
const flushBuffersUseCase = new use_cases_1.FlushBuffersUseCase(ds);
|
|
36
|
+
// 5) Construir facade de servicio de logging
|
|
37
|
+
const service = {
|
|
38
|
+
// Método genérico de logging
|
|
39
|
+
async log(level, message, meta) {
|
|
40
|
+
await saveLogUseCase.execute("app-logger", level, message, meta);
|
|
41
|
+
},
|
|
42
|
+
// Helpers por nivel
|
|
43
|
+
trace(message, meta) {
|
|
44
|
+
return saveLogUseCase.execute(source, value_objects_1.LogLevel.TRACE, message, meta);
|
|
45
|
+
},
|
|
46
|
+
debug(message, meta) {
|
|
47
|
+
return saveLogUseCase.execute(source, value_objects_1.LogLevel.DEBUG, message, meta);
|
|
48
|
+
},
|
|
49
|
+
info(message, meta) {
|
|
50
|
+
return saveLogUseCase.execute(source, value_objects_1.LogLevel.INFO, message, meta);
|
|
51
|
+
},
|
|
52
|
+
warn(message, meta) {
|
|
53
|
+
return saveLogUseCase.execute(source, value_objects_1.LogLevel.WARN, message, meta);
|
|
54
|
+
},
|
|
55
|
+
error(message, meta) {
|
|
56
|
+
return saveLogUseCase.execute(source, value_objects_1.LogLevel.ERROR, message, meta);
|
|
57
|
+
},
|
|
58
|
+
fatal(message, meta) {
|
|
59
|
+
return saveLogUseCase.execute(source, value_objects_1.LogLevel.FATAL, message, meta);
|
|
60
|
+
},
|
|
61
|
+
// Lectura de logs con filtros
|
|
62
|
+
async getLogs(filter) {
|
|
63
|
+
return getLogsUseCase.execute(filter);
|
|
64
|
+
},
|
|
65
|
+
// Flush explícito (si el datasource lo soporta)
|
|
66
|
+
async flush() {
|
|
67
|
+
await flushBuffersUseCase.execute();
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
return service;
|
|
71
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FlushBuffersUseCase = void 0;
|
|
4
|
+
class FlushBuffersUseCase {
|
|
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.FlushBuffersUseCase = FlushBuffersUseCase;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ILogDatasource } from "../../domain/ports";
|
|
2
|
+
import { LogFilterRequest } from "../../domain/request";
|
|
3
|
+
import { ILogResponse } from "../../domain/response";
|
|
4
|
+
export declare class GetLogsUseCase {
|
|
5
|
+
private readonly ds;
|
|
6
|
+
constructor(ds: ILogDatasource);
|
|
7
|
+
execute(filter?: LogFilterRequest): Promise<ILogResponse[]>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GetLogsUseCase = void 0;
|
|
4
|
+
class GetLogsUseCase {
|
|
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.GetLogsUseCase = GetLogsUseCase;
|
|
@@ -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.use-case"), exports);
|
|
18
|
+
__exportStar(require("./get-logs.use-case"), exports);
|
|
19
|
+
__exportStar(require("./flush-buffers.use-case"), exports);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./save-log.props";
|
|
@@ -0,0 +1,17 @@
|
|
|
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.props"), exports);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { LogMessage } from "../../domain/types";
|
|
2
|
+
import { LogLevel } from "../../domain/value-objects";
|
|
3
|
+
import { SaveLogDependencies } from "./save-log";
|
|
4
|
+
export declare class SaveLogUseCase {
|
|
5
|
+
private readonly props;
|
|
6
|
+
constructor(props: SaveLogDependencies);
|
|
7
|
+
execute(source: string, level: LogLevel, message: LogMessage, meta?: unknown): Promise<void>;
|
|
8
|
+
}
|