@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.
Files changed (99) hide show
  1. package/README.md +247 -0
  2. package/architecture.md +171 -0
  3. package/dist/application/factory/index.d.ts +1 -0
  4. package/dist/{config → application/factory}/index.js +1 -2
  5. package/dist/application/factory/logger.factory.d.ts +10 -0
  6. package/dist/application/factory/logger.factory.js +71 -0
  7. package/dist/application/index.d.ts +2 -0
  8. package/dist/{presentation → application}/index.js +1 -0
  9. package/dist/application/use-cases/flush-buffers.use-case.d.ts +6 -0
  10. package/dist/application/use-cases/flush-buffers.use-case.js +13 -0
  11. package/dist/application/use-cases/get-logs.use-case.d.ts +8 -0
  12. package/dist/application/use-cases/get-logs.use-case.js +24 -0
  13. package/dist/application/use-cases/index.d.ts +3 -0
  14. package/dist/application/use-cases/index.js +19 -0
  15. package/dist/application/use-cases/save-log/index.d.ts +1 -0
  16. package/dist/application/use-cases/save-log/index.js +17 -0
  17. package/dist/application/use-cases/save-log/save-log.props.d.ts +7 -0
  18. package/dist/{config/interfaces/index.js → application/use-cases/save-log/save-log.props.js} +0 -1
  19. package/dist/application/use-cases/save-log.use-case.d.ts +8 -0
  20. package/dist/application/use-cases/save-log.use-case.js +27 -0
  21. package/dist/domain/index.d.ts +7 -0
  22. package/dist/domain/index.js +7 -0
  23. package/dist/domain/model/index.d.ts +3 -0
  24. package/dist/domain/model/index.js +19 -0
  25. package/dist/domain/model/log-entry.model.d.ts +8 -0
  26. package/dist/domain/model/log-entry.model.js +2 -0
  27. package/dist/domain/model/pii-options.model.d.ts +8 -0
  28. package/dist/domain/model/pii-options.model.js +2 -0
  29. package/dist/domain/model/pii-replacement-rule.d.ts +5 -0
  30. package/dist/domain/model/pii-replacement-rule.js +2 -0
  31. package/dist/domain/ports/create-logger-options.port.d.ts +7 -0
  32. package/dist/domain/ports/create-logger-options.port.js +2 -0
  33. package/dist/domain/ports/index.d.ts +5 -0
  34. package/dist/domain/ports/index.js +22 -0
  35. package/dist/domain/ports/log-datasource.port.d.ts +10 -0
  36. package/dist/domain/ports/log-datasource.port.js +2 -0
  37. package/dist/domain/ports/logger-factory-config.port.d.ts +28 -0
  38. package/dist/domain/ports/logger-factory-config.port.js +2 -0
  39. package/dist/domain/ports/logger.port.d.ts +15 -0
  40. package/dist/domain/ports/logger.port.js +2 -0
  41. package/dist/domain/ports/pii-redactor.port.d.ts +5 -0
  42. package/dist/domain/ports/pii-redactor.port.js +2 -0
  43. package/dist/domain/request/index.d.ts +1 -0
  44. package/dist/domain/request/index.js +17 -0
  45. package/dist/domain/request/log-filter.request.d.ts +9 -0
  46. package/dist/domain/request/log-filter.request.js +2 -0
  47. package/dist/domain/response/index.d.ts +1 -0
  48. package/dist/domain/response/index.js +17 -0
  49. package/dist/domain/response/log.response.d.ts +8 -0
  50. package/dist/domain/response/log.response.js +2 -0
  51. package/dist/domain/services/index.d.ts +1 -1
  52. package/dist/domain/services/index.js +1 -1
  53. package/dist/domain/services/pii-redactor.service.d.ts +10 -0
  54. package/dist/domain/services/pii-redactor.service.js +68 -0
  55. package/dist/domain/types/index.d.ts +1 -0
  56. package/dist/domain/types/index.js +17 -0
  57. package/dist/domain/types/log-message.type.d.ts +1 -0
  58. package/dist/domain/types/log-message.type.js +2 -0
  59. package/dist/domain/utils/index.d.ts +3 -0
  60. package/dist/domain/utils/index.js +19 -0
  61. package/dist/domain/utils/normalize-message.util.d.ts +3 -0
  62. package/dist/domain/utils/normalize-message.util.js +8 -0
  63. package/dist/domain/utils/parse-log-level.util.d.ts +2 -0
  64. package/dist/domain/utils/parse-log-level.util.js +27 -0
  65. package/dist/domain/utils/pii-regex.util.d.ts +2 -0
  66. package/dist/domain/utils/pii-regex.util.js +13 -0
  67. package/dist/domain/value-objects/index.d.ts +1 -0
  68. package/dist/domain/value-objects/index.js +17 -0
  69. package/dist/domain/value-objects/log-level.vo.d.ts +8 -0
  70. package/dist/domain/value-objects/log-level.vo.js +13 -0
  71. package/dist/index.d.ts +8 -4
  72. package/dist/index.js +32 -7
  73. package/dist/infrastructure/index.d.ts +1 -0
  74. package/dist/infrastructure/index.js +17 -0
  75. package/dist/infrastructure/services/datasource.service.d.ts +18 -0
  76. package/dist/infrastructure/services/datasource.service.js +102 -0
  77. package/dist/infrastructure/services/index.d.ts +1 -0
  78. package/dist/infrastructure/services/index.js +17 -0
  79. package/dist/infrastructure/types/index.d.ts +1 -0
  80. package/dist/infrastructure/types/index.js +17 -0
  81. package/dist/infrastructure/types/on-data-source-error.type.d.ts +5 -0
  82. package/dist/infrastructure/types/on-data-source-error.type.js +2 -0
  83. package/install.md +632 -0
  84. package/package.json +37 -11
  85. package/dist/Composite/index.d.ts +0 -9
  86. package/dist/Composite/index.js +0 -54
  87. package/dist/Factory/index.d.ts +0 -5
  88. package/dist/Factory/index.js +0 -23
  89. package/dist/config/index.d.ts +0 -2
  90. package/dist/config/interfaces/index.d.ts +0 -67
  91. package/dist/config/types/index.d.ts +0 -10
  92. package/dist/config/types/index.js +0 -13
  93. package/dist/domain/services/pii-redactor.d.ts +0 -27
  94. package/dist/domain/services/pii-redactor.js +0 -139
  95. package/dist/interfaces/index.d.ts +0 -35
  96. package/dist/interfaces/index.js +0 -13
  97. package/dist/presentation/factory/index.d.ts +0 -2
  98. package/dist/presentation/factory/index.js +0 -74
  99. 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
+ ![MongoDB log](./assets/mongo-log.png)
121
+
122
+ > - **MongoDB** (`@jmlq/logger-plugin-mongo`)
123
+
124
+ ![Postgresql log](./assets/pg-log.png)
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
@@ -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("./interfaces"), exports);
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,2 @@
1
+ export * from "./factory";
2
+ export * from "./use-cases";
@@ -15,3 +15,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./factory"), exports);
18
+ __exportStar(require("./use-cases"), exports);
@@ -0,0 +1,6 @@
1
+ import { ILogDatasource } from "../../domain/ports";
2
+ export declare class FlushBuffersUseCase {
3
+ private readonly ds;
4
+ constructor(ds: ILogDatasource);
5
+ execute(): Promise<void>;
6
+ }
@@ -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,3 @@
1
+ export * from "./save-log.use-case";
2
+ export * from "./get-logs.use-case";
3
+ export * from "./flush-buffers.use-case";
@@ -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,7 @@
1
+ import { LogLevel } from "../../../domain/value-objects";
2
+ import { ILogDatasource, IPiiRedactor } from "../../../domain/ports";
3
+ export interface SaveLogDependencies {
4
+ ds: ILogDatasource;
5
+ minLevel: LogLevel;
6
+ redactor: IPiiRedactor;
7
+ }
@@ -1,3 +1,2 @@
1
1
  "use strict";
2
- // ---- Contratos ----
3
2
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -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
+ }