@jmlq/logger 0.1.0-alpha.8 → 0.1.0-alpha.9

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 (3) hide show
  1. package/architecture.md +171 -0
  2. package/install.md +367 -0
  3. package/package.json +4 -2
@@ -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.
package/install.md ADDED
@@ -0,0 +1,367 @@
1
+ # Guía de Instalación y Configuración para @jmlq/logger
2
+
3
+ ## Instalación
4
+
5
+ ```bash
6
+ npm install @jmlq/logger
7
+ ```
8
+
9
+ ## Configuración Básica
10
+
11
+ ### NestJS
12
+
13
+ 1. Crear el módulo de Logger
14
+
15
+ ```typescript
16
+ // src/logger/logger.module.ts
17
+ import { Module, Global } from "@nestjs/common";
18
+ import { ConfigModule, ConfigService } from "@nestjs/config";
19
+ import { LoggerFactory } from "@jmlq/logger";
20
+
21
+ @Global()
22
+ @Module({
23
+ imports: [ConfigModule],
24
+ providers: [
25
+ {
26
+ provide: "LOGGER",
27
+ useFactory: async (configService: ConfigService) => {
28
+ // Configurar datasources según necesidades
29
+ const datasources = [];
30
+
31
+ // Ejemplo: Plugin de filesystem (requiere @jmlq/logger-plugin-fs)
32
+ if (configService.get("LOG_FS_ENABLED") === "true") {
33
+ const { FileSystemDatasource } = await import(
34
+ "@jmlq/logger-plugin-fs"
35
+ );
36
+ datasources.push(
37
+ new FileSystemDatasource({
38
+ basePath: configService.get("LOG_FS_PATH", "./logs"),
39
+ })
40
+ );
41
+ }
42
+
43
+ return LoggerFactory.create({
44
+ datasources,
45
+ minLevel: configService.get("LOG_LEVEL", "info"),
46
+ redactorOptions: {
47
+ enabled: true,
48
+ deep: true,
49
+ includeDefaults: true,
50
+ },
51
+ });
52
+ },
53
+ inject: [ConfigService],
54
+ },
55
+ ],
56
+ exports: ["LOGGER"],
57
+ })
58
+ export class LoggerModule {}
59
+ ```
60
+
61
+ 2. Usar en servicios
62
+
63
+ ```typescript
64
+ // src/users/users.service.ts
65
+ import { Injectable, Inject } from "@nestjs/common";
66
+ import { ILogger } from "@jmlq/logger";
67
+
68
+ @Injectable()
69
+ export class UsersService {
70
+ constructor(@Inject("LOGGER") private readonly logger: ILogger) {}
71
+
72
+ async createUser(userData: any) {
73
+ try {
74
+ await this.logger.info("Creando usuario", { userData });
75
+ // ... lógica del servicio
76
+ await this.logger.info("Usuario creado exitosamente", {
77
+ userId: result.id,
78
+ });
79
+ return result;
80
+ } catch (error) {
81
+ await this.logger.error("Error al crear usuario", {
82
+ error: error.message,
83
+ userData,
84
+ });
85
+ throw error;
86
+ }
87
+ }
88
+ }
89
+ ```
90
+
91
+ ### Express
92
+
93
+ 1. Configurar logger en app.js
94
+
95
+ ```ts
96
+ // src/logger.ts
97
+ import { LoggerFactory, LogLevel } from "@jmlq/logger";
98
+
99
+ export const createLogger = async () => {
100
+ const datasources = [];
101
+
102
+ // Configurar datasources según variables de entorno
103
+ if (process.env.LOG_FS_ENABLED === "true") {
104
+ const { FileSystemDatasource } = await import("@jmlq/logger-plugin-fs");
105
+ datasources.push(
106
+ new FileSystemDatasource({
107
+ basePath: process.env.LOG_FS_PATH || "./logs",
108
+ })
109
+ );
110
+ }
111
+
112
+ return LoggerFactory.create({
113
+ datasources,
114
+ minLevel: process.env.LOG_LEVEL || LogLevel.INFO,
115
+ redactorOptions: {
116
+ enabled: true,
117
+ deep: true,
118
+ includeDefaults: true,
119
+ patterns: [
120
+ {
121
+ pattern: "\\b\\d{4}-\\d{4}-\\d{4}-\\d{4}\\b",
122
+ replaceWith: "****-****-****-****",
123
+ },
124
+ ],
125
+ },
126
+ });
127
+ };
128
+ ```
129
+
130
+ 2. Middleware de logging
131
+
132
+ ```ts
133
+ // src/middleware/logger.middleware.ts
134
+ import { Request, Response, NextFunction } from "express";
135
+ import { ILogger } from "@jmlq/logger";
136
+
137
+ export const createLoggerMiddleware = (logger: ILogger) => {
138
+ return async (req: Request, res: Response, next: NextFunction) => {
139
+ const startTime = Date.now();
140
+
141
+ await logger.info("Request iniciado", {
142
+ method: req.method,
143
+ url: req.url,
144
+ ip: req.ip,
145
+ userAgent: req.get("User-Agent"),
146
+ });
147
+
148
+ res.on("finish", async () => {
149
+ const duration = Date.now() - startTime;
150
+ const level = res.statusCode >= 400 ? "error" : "info";
151
+
152
+ await logger[level]("Request completado", {
153
+ method: req.method,
154
+ url: req.url,
155
+ statusCode: res.statusCode,
156
+ duration,
157
+ });
158
+ });
159
+
160
+ next();
161
+ };
162
+ };
163
+ ```
164
+
165
+ 3. Usar en la aplicación
166
+
167
+ ```ts
168
+ // src/app.ts
169
+ import express from "express";
170
+ import { createLogger } from "./logger";
171
+ import { createLoggerMiddleware } from "./middleware/logger.middleware";
172
+
173
+ const app = express();
174
+ const logger = await createLogger();
175
+
176
+ app.use(createLoggerMiddleware(logger));
177
+
178
+ app.get("/users/:id", async (req, res) => {
179
+ try {
180
+ await logger.info("Obteniendo usuario", { userId: req.params.id });
181
+ // ... lógica
182
+ res.json(user);
183
+ } catch (error) {
184
+ await logger.error("Error al obtener usuario", {
185
+ userId: req.params.id,
186
+ error: error.message,
187
+ });
188
+ res.status(500).json({ error: "Internal server error" });
189
+ }
190
+ });
191
+ ```
192
+
193
+ ## Configuración de PII (Datos Sensibles)
194
+
195
+ ### Patrones Predefinidos
196
+
197
+ El logger incluye patrones comunes para enmascarar datos sensibles:
198
+
199
+ ```ts
200
+ const logger = LoggerFactory.create({
201
+ datasources,
202
+ redactorOptions: {
203
+ enabled: true,
204
+ deep: true, // Busca en objetos anidados
205
+ includeDefaults: true, // Incluye patrones predefinidos (email, teléfono, etc.)
206
+ },
207
+ });
208
+ ```
209
+
210
+ Patrones Personalizados
211
+
212
+ ```ts
213
+ const logger = LoggerFactory.create({
214
+ datasources,
215
+ redactorOptions: {
216
+ enabled: true,
217
+ deep: true,
218
+ includeDefaults: true,
219
+ patterns: [
220
+ // Tarjetas de crédito
221
+ {
222
+ pattern: "\\b\\d{4}[-\\s]?\\d{4}[-\\s]?\\d{4}[-\\s]?\\d{4}\\b",
223
+ replaceWith: "****-****-****-****",
224
+ },
225
+ // CURP (México)
226
+ {
227
+ pattern: "[A-Z]{4}\\d{6}[HM][A-Z]{5}[A-Z0-9]\\d",
228
+ replaceWith: "****CURP****",
229
+ },
230
+ // Números de cuenta bancaria
231
+ {
232
+ pattern: "\\b\\d{10,20}\\b",
233
+ replaceWith: "****ACCOUNT****",
234
+ },
235
+ // Tokens JWT
236
+ {
237
+ pattern: "eyJ[A-Za-z0-9-_=]+\\.[A-Za-z0-9-_=]+\\.?[A-Za-z0-9-_.+/=]*",
238
+ replaceWith: "****JWT****",
239
+ },
240
+ ],
241
+ },
242
+ });
243
+ ```
244
+
245
+ ## Filtrado de Registros
246
+
247
+ ### Filtros Básicos
248
+
249
+ ```ts
250
+ // Obtener todos los logs de nivel ERROR o superior
251
+ const errors = await logger.getLogs({
252
+ levelMin: LogLevel.ERROR,
253
+ });
254
+
255
+ // Filtrar por rango de fechas
256
+ const yesterday = Date.now() - 24 * 60 * 60 * 1000;
257
+ const recentLogs = await logger.getLogs({
258
+ since: yesterday,
259
+ until: Date.now(),
260
+ });
261
+
262
+ // Búsqueda por texto
263
+ const userLogs = await logger.getLogs({
264
+ query: "usuario",
265
+ limit: 50,
266
+ });
267
+ ```
268
+
269
+ ### Filtros Avanzados
270
+
271
+ ```ts
272
+ // Combinación de filtros
273
+ const criticalRecentErrors = await logger.getLogs({
274
+ levelMin: LogLevel.ERROR,
275
+ since: Date.now() - 2 * 60 * 60 * 1000, // Últimas 2 horas
276
+ query: "database connection",
277
+ limit: 20,
278
+ offset: 0,
279
+ });
280
+
281
+ // Paginación
282
+ const page1 = await logger.getLogs({ limit: 10, offset: 0 });
283
+ const page2 = await logger.getLogs({ limit: 10, offset: 10 });
284
+ ```
285
+
286
+ ## Variables de Entorno
287
+
288
+ ```bash
289
+ # Nivel mínimo de logs
290
+ LOG_LEVEL=info
291
+
292
+ # Filesystem
293
+ LOG_FS_ENABLED=true
294
+ LOG_FS_PATH=./logs
295
+
296
+ # MongoDB (si usas @jmlq/logger-plugin-mongo)
297
+ LOG_MONGO_ENABLED=true
298
+ LOG_MONGO_URL=mongodb://localhost:27017
299
+ LOG_MONGO_DB=logs
300
+
301
+ # PostgreSQL (si usas @jmlq/logger-plugin-postgresql)
302
+ LOG_PG_ENABLED=true
303
+ LOG_PG_HOST=localhost
304
+ LOG_PG_PORT=5432
305
+ LOG_PG_DATABASE=logs
306
+ LOG_PG_USERNAME=logger
307
+ LOG_PG_PASSWORD=secret
308
+ ```
309
+
310
+ ## Ejemplo Completo
311
+
312
+ ```ts
313
+ import { LoggerFactory, LogLevel } from "@jmlq/logger";
314
+
315
+ // Configuración completa
316
+ const logger = await LoggerFactory.create({
317
+ datasources: [
318
+ // Múltiples datasources
319
+ new FileSystemDatasource({ basePath: "./logs" }),
320
+ new MongoDatasource({ url: "mongodb://localhost:27017", dbName: "logs" }),
321
+ ],
322
+ minLevel: LogLevel.DEBUG,
323
+ redactorOptions: {
324
+ enabled: true,
325
+ deep: true,
326
+ includeDefaults: true,
327
+ patterns: [
328
+ {
329
+ pattern: "\\b\\d{4}-\\d{4}-\\d{4}-\\d{4}\\b",
330
+ replaceWith: "****-****-****-****",
331
+ },
332
+ ],
333
+ },
334
+ });
335
+
336
+ // Uso con diferentes niveles
337
+ await logger.debug("Debug info", { userId: 123 });
338
+ await logger.info("User login", { email: "user@example.com" });
339
+ await logger.warn("High latency", { endpoint: "/api/users", duration: 950 });
340
+ await logger.error("Database error", { error: "Connection timeout" });
341
+ await logger.fatal("System crash", { stack: error.stack });
342
+
343
+ // Filtrar y consultar logs
344
+ const recentErrors = await logger.getLogs({
345
+ levelMin: LogLevel.ERROR,
346
+ since: Date.now() - 24 * 60 * 60 * 1000,
347
+ limit: 100,
348
+ });
349
+
350
+ // Limpiar buffers (útil antes de cerrar la aplicación)
351
+ await logger.flush();
352
+ ```
353
+
354
+ ## Plugins Disponibles
355
+
356
+ > - `@jmlq/logger-plugin-fs`: Persistencia en archivos
357
+ > - `@jmlq/logger-plugin-mongo`: Persistencia en MongoDB
358
+ > - `@jmlq/logger-plugin-postgresql`: Persistencia en PostgreSQL
359
+
360
+ Cada plugin se instala por separado según las necesidades de tu aplicación.
361
+
362
+ ## Consideraciones de Rendimiento
363
+
364
+ Los logs se procesan de forma asíncrona
365
+ Múltiples datasources escriben en paralelo
366
+ Usa [flush()](./examples/logger-factory.example.ts) antes de cerrar la aplicación para asegurar que todos los logs se persistan
367
+ Configura límites apropiados en las consultas para evitar sobrecarga de memoria
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@jmlq/logger",
3
3
  "description": "logger package with clean architecture",
4
- "version": "0.1.0-alpha.8",
4
+ "version": "0.1.0-alpha.9",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "scripts": {
@@ -35,6 +35,8 @@
35
35
  "typescript": "^5.2.2"
36
36
  },
37
37
  "files": [
38
- "dist"
38
+ "dist",
39
+ "architecture.md",
40
+ "install.md"
39
41
  ]
40
42
  }