@jmlq/logger 0.1.0-alpha.12 → 0.1.0-alpha.14
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 +141 -201
- package/dist/application/factory/logger.factory.d.ts +2 -2
- package/dist/domain/ports/index.d.ts +0 -1
- package/dist/domain/ports/index.js +1 -1
- package/dist/domain/ports/logger.port.d.ts +13 -8
- package/dist/infrastructure/services/datasource.service.d.ts +1 -0
- package/dist/infrastructure/services/datasource.service.js +41 -12
- package/install.md +556 -291
- package/package.json +1 -1
- package/dist/domain/ports/logger-service.port.d.ts +0 -19
- package/dist/domain/ports/logger-service.port.js +0 -2
package/README.md
CHANGED
|
@@ -16,195 +16,185 @@ npm install @jmlq/logger-plugin-postgresql # Para PostgreSQL
|
|
|
16
16
|
|
|
17
17
|
## 🚀 Uso Básico
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
Con esta configuración se busca:
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
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.
|
|
23
29
|
|
|
24
|
-
|
|
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
|
-
};
|
|
36
|
-
|
|
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
|
-
});
|
|
30
|
+
---
|
|
47
31
|
|
|
48
|
-
|
|
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
|
-
});
|
|
56
|
-
```
|
|
32
|
+
## 🎯 Características Principales
|
|
57
33
|
|
|
58
|
-
###
|
|
34
|
+
### Niveles de Log
|
|
59
35
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
|
64
42
|
|
|
65
|
-
|
|
66
|
-
datasources: [
|
|
67
|
-
new FileSystemDatasource({ basePath: "./logs" }),
|
|
68
|
-
new MongoDatasource({ url: "mongodb://localhost:27017", dbName: "logs" }),
|
|
69
|
-
],
|
|
70
|
-
minLevel: LogLevel.INFO,
|
|
71
|
-
});
|
|
43
|
+
### Enmascarado PII Automático
|
|
72
44
|
|
|
73
|
-
|
|
74
|
-
```
|
|
45
|
+
El logger detecta y enmascara automáticamente datos sensibles utilizando patrones personalizados:
|
|
75
46
|
|
|
76
|
-
|
|
47
|
+
```js
|
|
77
48
|
|
|
78
|
-
|
|
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
|
+
],
|
|
79
66
|
|
|
80
|
-
|
|
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
|
|
67
|
+
```
|
|
86
68
|
|
|
87
|
-
|
|
69
|
+
Por ejemplo se tiene la siguiente información:
|
|
88
70
|
|
|
89
|
-
|
|
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
|
+
```
|
|
90
87
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
+
}
|
|
97
116
|
```
|
|
98
117
|
|
|
118
|
+
> - **MongoDB** (`@jmlq/logger-plugin-mongo`)
|
|
119
|
+
|
|
120
|
+

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

|
|
125
|
+
|
|
99
126
|
### Filtros y Consultas
|
|
100
127
|
|
|
101
|
-
|
|
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
|
-
});
|
|
128
|
+
#### 1. Obtener todos los logs de nivel WARN o superior (en este caso retorna WARN, ERROR y FATAL)
|
|
108
129
|
|
|
109
|
-
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
limit: 100,
|
|
130
|
+
```ts
|
|
131
|
+
const errors = await _req.logger?.getLogs({
|
|
132
|
+
levelMin: parseLogLevel("WARN"),
|
|
113
133
|
});
|
|
114
134
|
```
|
|
115
135
|
|
|
116
|
-
|
|
136
|
+
Resultado:
|
|
117
137
|
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
|
|
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}
|
|
121
142
|
|
|
122
|
-
|
|
123
|
-
LOGGER_FS_PATH=./logs
|
|
143
|
+
```
|
|
124
144
|
|
|
125
|
-
|
|
126
|
-
MONGO_URL=mongodb://localhost:27017
|
|
127
|
-
MONGO_DB_NAME=logs
|
|
145
|
+
**NOTA**: `parseLogLevel` es un helper para convertir un `string` a `LogLevel`.
|
|
128
146
|
|
|
129
|
-
|
|
130
|
-
POSTGRES_URL=postgresql://user:pass@localhost:5432/logs
|
|
147
|
+
#### 2. Filtrar por rango de fechas
|
|
131
148
|
|
|
132
|
-
|
|
133
|
-
|
|
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
|
+
});
|
|
134
155
|
```
|
|
135
156
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
El proyecto incluye ejemplos completos en el directorio [`examples/`](examples/):
|
|
157
|
+
Resultado:
|
|
139
158
|
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
|
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}
|
|
149
165
|
```
|
|
150
166
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
```typescript
|
|
154
|
-
import express from "express";
|
|
155
|
-
import { LoggerFactory, LogLevel } from "@jmlq/logger";
|
|
167
|
+
#### 3. Búsqueda por texto (en el campo mensaje del log)
|
|
156
168
|
|
|
157
|
-
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
minLevel: LogLevel.INFO
|
|
169
|
+
```ts
|
|
170
|
+
const userLogs = await _req.logger?.getLogs({
|
|
171
|
+
query: "Users",
|
|
161
172
|
});
|
|
173
|
+
```
|
|
162
174
|
|
|
163
|
-
|
|
164
|
-
await logger.info("Request iniciado", {
|
|
165
|
-
method: req.method,
|
|
166
|
-
url: req.url,
|
|
167
|
-
ip: req.ip
|
|
168
|
-
});
|
|
169
|
-
next();
|
|
170
|
-
});
|
|
175
|
+
Resultado:
|
|
171
176
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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",
|
|
184
189
|
});
|
|
185
190
|
```
|
|
186
191
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
async createUser(userData: any) {
|
|
195
|
-
await this.logger.info("Creando usuario", { userData });
|
|
196
|
-
try {
|
|
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,
|
|
203
|
-
});
|
|
204
|
-
throw error;
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}
|
|
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":"****-****-****-****"}]}
|
|
208
198
|
```
|
|
209
199
|
|
|
210
200
|
## 🏗️ Arquitectura
|
|
@@ -246,56 +236,6 @@ src/
|
|
|
246
236
|
| `@jmlq/logger-plugin-mongo` | Persistencia en MongoDB | [npm](https://npmjs.com/package/@jmlq/logger-plugin-mongo) |
|
|
247
237
|
| `@jmlq/logger-plugin-postgresql` | Persistencia en PostgreSQL | [npm](https://npmjs.com/package/@jmlq/logger-plugin-postgresql) |
|
|
248
238
|
|
|
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
|
-
});
|
|
273
|
-
|
|
274
|
-
await logger.info("Usuario: user@example.com");
|
|
275
|
-
|
|
276
|
-
expect(logs[0].message).not.toContain("user@example.com");
|
|
277
|
-
expect(logs[0].message).toContain("[EMAIL]");
|
|
278
|
-
});
|
|
279
|
-
});
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
## 🚨 Troubleshooting
|
|
283
|
-
|
|
284
|
-
**Error: No datasources válidos**
|
|
285
|
-
|
|
286
|
-
- Asegúrate de configurar al menos un datasource
|
|
287
|
-
- Verifica las variables de entorno
|
|
288
|
-
|
|
289
|
-
**MongoDB no conecta**
|
|
290
|
-
|
|
291
|
-
- Verifica la URL de conexión
|
|
292
|
-
- Incluye `authSource=admin` si usas usuarios root
|
|
293
|
-
|
|
294
|
-
**Alto uso de memoria**
|
|
295
|
-
|
|
296
|
-
- Implementa límites en las consultas
|
|
297
|
-
- Usa `flush()` periódicamente para vaciar buffers
|
|
298
|
-
|
|
299
239
|
## 📄 Más Información
|
|
300
240
|
|
|
301
241
|
- **[Arquitectura Detallada](./architecture.md)** - Documentación técnica completa
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ILoggerFactoryConfig,
|
|
1
|
+
import { ILoggerFactoryConfig, ILogger } from "../../domain/ports";
|
|
2
2
|
/**
|
|
3
3
|
* Factory principal de @jmlq/logger.
|
|
4
4
|
* Se encarga de:
|
|
@@ -8,5 +8,5 @@ import { ILoggerFactoryConfig, ILoggerService } from "../../domain/ports";
|
|
|
8
8
|
* - Exponer un servicio de logger de alto nivel
|
|
9
9
|
*/
|
|
10
10
|
export declare class LoggerFactory {
|
|
11
|
-
static create(config: ILoggerFactoryConfig):
|
|
11
|
+
static create(config: ILoggerFactoryConfig): ILogger;
|
|
12
12
|
}
|
|
@@ -19,4 +19,4 @@ __exportStar(require("./logger.port"), exports);
|
|
|
19
19
|
__exportStar(require("./pii-redactor.port"), exports);
|
|
20
20
|
__exportStar(require("./create-logger-options.port"), exports);
|
|
21
21
|
__exportStar(require("./logger-factory-config.port"), exports);
|
|
22
|
-
|
|
22
|
+
// export * from "./logger-service.port";
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
+
import { IGetLogsFilterProps } from "../request";
|
|
2
|
+
import { ILogResponse } from "../response";
|
|
3
|
+
import { LogMessage } from "../types";
|
|
4
|
+
import { LogLevel } from "../value-objects";
|
|
1
5
|
export interface ILogger {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
log(level: LogLevel, message: LogMessage, meta?: unknown): Promise<void>;
|
|
7
|
+
trace(message: LogMessage, meta?: unknown): Promise<void>;
|
|
8
|
+
debug(message: LogMessage, meta?: unknown): Promise<void>;
|
|
9
|
+
info(message: LogMessage, meta?: unknown): Promise<void>;
|
|
10
|
+
warn(message: LogMessage, meta?: unknown): Promise<void>;
|
|
11
|
+
error(message: LogMessage, meta?: unknown): Promise<void>;
|
|
12
|
+
fatal(message: LogMessage, meta?: unknown): Promise<void>;
|
|
13
|
+
getLogs(filter?: IGetLogsFilterProps): Promise<ILogResponse[]>;
|
|
14
|
+
flush(): Promise<void>;
|
|
10
15
|
}
|
|
@@ -6,6 +6,7 @@ export declare class DataSourceService implements ILogDatasource {
|
|
|
6
6
|
private readonly targets;
|
|
7
7
|
private readonly onError?;
|
|
8
8
|
readonly name = "composite";
|
|
9
|
+
private logs;
|
|
9
10
|
constructor(targets: ILogDatasource[], onError?: DataSourceErrorHandler | undefined);
|
|
10
11
|
private handleError;
|
|
11
12
|
save(log: ILogProps): Promise<void>;
|
|
@@ -6,6 +6,7 @@ class DataSourceService {
|
|
|
6
6
|
this.targets = targets;
|
|
7
7
|
this.onError = onError;
|
|
8
8
|
this.name = "composite";
|
|
9
|
+
this.logs = [];
|
|
9
10
|
this.targets = (targets ?? []).filter(Boolean);
|
|
10
11
|
}
|
|
11
12
|
handleError(op, i, reason) {
|
|
@@ -29,19 +30,47 @@ class DataSourceService {
|
|
|
29
30
|
}
|
|
30
31
|
});
|
|
31
32
|
}
|
|
33
|
+
// async find(filter?: IGetLogsFilterProps) {
|
|
34
|
+
// const results = await Promise.allSettled(
|
|
35
|
+
// this.targets.map((ds) => ds.find?.(filter))
|
|
36
|
+
// );
|
|
37
|
+
// const logs: ILogResponse[] = [];
|
|
38
|
+
// results.forEach((r, i) => {
|
|
39
|
+
// if (r.status === "fulfilled" && Array.isArray(r.value)) {
|
|
40
|
+
// logs.push(...r.value);
|
|
41
|
+
// } else if (r.status === "rejected") {
|
|
42
|
+
// this.handleError("find", i, r.reason);
|
|
43
|
+
// }
|
|
44
|
+
// });
|
|
45
|
+
// logs.sort((a, b) => b.timestamp - a.timestamp);
|
|
46
|
+
// return logs;
|
|
47
|
+
// }
|
|
32
48
|
async find(filter) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
49
|
+
let results = [...this.logs];
|
|
50
|
+
if (filter?.levelMin !== undefined) {
|
|
51
|
+
results = results.filter((log) => log.level >= filter.levelMin);
|
|
52
|
+
}
|
|
53
|
+
if (filter?.since !== undefined) {
|
|
54
|
+
results = results.filter((log) => log.timestamp >= filter.since);
|
|
55
|
+
}
|
|
56
|
+
if (filter?.until !== undefined) {
|
|
57
|
+
results = results.filter((log) => log.timestamp <= filter.until);
|
|
58
|
+
}
|
|
59
|
+
if (filter?.query) {
|
|
60
|
+
const q = filter.query.toLowerCase();
|
|
61
|
+
results = results.filter((log) => {
|
|
62
|
+
const msg = typeof log.message === "string"
|
|
63
|
+
? log.message.toLowerCase()
|
|
64
|
+
: JSON.stringify(log.message).toLowerCase();
|
|
65
|
+
return msg.includes(q);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
// Ordenar por timestamp ascendente (opcional pero útil para visualizar)
|
|
69
|
+
results.sort((a, b) => a.timestamp - b.timestamp);
|
|
70
|
+
// Paginación
|
|
71
|
+
const offset = filter?.offset ?? 0;
|
|
72
|
+
const limit = filter?.limit ?? results.length;
|
|
73
|
+
return results.slice(offset, offset + limit);
|
|
45
74
|
}
|
|
46
75
|
async flush() {
|
|
47
76
|
const results = await Promise.allSettled(this.targets.map((ds) => ds.flush?.()));
|