@jmlq/logger 0.1.0-alpha.20 → 0.1.0-alpha.21

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/install.md DELETED
@@ -1,632 +0,0 @@
1
- # GUÍA DE CONFIGURACIÓN @jmlq/logger Y SUS PLUGINS
2
-
3
- ## PASO 1 – BOOTSTRAP DEL LOGGER CORE
4
-
5
- ### 1.1. OPCIONES DE BOOTSTRAP
6
-
7
- Path sugerido: `src/infrastructure/adapters/jmlq/logger/core/logger.bootstrap.options.ts`
8
-
9
- `LoggerBootstrapOptions` es un **contrato de configuración** usado al momento de inicializar el logger. Define:
10
-
11
- - El **nivel mínimo de log** permitido.
12
- - Configuración de **redacción de PII** (datos sensibles).
13
- - Los **datasources** del logger que se deben habilitar (FS, Mongo, PostgreSQL).
14
-
15
- Describe cómo debe construirse el logger, qué debe censurar, y a dónde deben enviarse los logs.
16
-
17
- ```ts
18
- import { LogLevel } from "@jmlq/logger";
19
- import { IFilesystemDatasourceOptions } from "@jmlq/logger-plugin-fs";
20
- import { MongoPluginConfig } from "@jmlq/logger-plugin-mongo";
21
- import { PostgresPluginConfig } from "@jmlq/logger-plugin-postgresql";
22
-
23
- export interface LoggerBootstrapOptions {
24
- minLevel: LogLevel;
25
- pii?: {
26
- enabled?: boolean;
27
- whitelistKeys?: string[];
28
- blacklistKeys?: string[];
29
- patterns?: any[];
30
- deep?: boolean;
31
- includeDefaults?: boolean;
32
- };
33
- adapters?: {
34
- // NOTA: Opcionales depende de las necesidades del cliente
35
- fs?: IFilesystemDatasourceOptions;
36
- mongo?: MongoPluginConfig;
37
- pg?: PostgresPluginConfig;
38
- };
39
- }
40
- ```
41
-
42
- - `minLevel`: nivel mínimo de log (ss de tipo `LogLevel` de `@jmlq/logger`).
43
- - `pii`: configuración de redacción (redaction) de datos sensibles.
44
- - `adapters`: configuración específica para cada plugin (**todos opcionales**).
45
-
46
- ---
47
-
48
- ### 1.2. IMPLEMENTACIÓN DEL BOOTSTRAP
49
-
50
- Path sugerido: `src/infrastructure/adapters/jmlq/logger/core/logger.bootstrap.ts`
51
-
52
- La clase `LoggerBootstrap` es un componente de infraestructura cuyo objetivo es:
53
-
54
- - Crear e inicializar el **logger principal** de la aplicación.
55
- - Construir la lista de **datasources** (FS, Mongo, PostgreSQL) según la configuración del usuario.
56
- - Exponer funciones para **flush** y **dispose**, importantes para apagado limpio.
57
- - Encapsular la instancia del logger para no exponer detalles internos.
58
-
59
- La clase completa se encarga de arrancar correctamente el sistema de logging.
60
-
61
- ```ts
62
- import { type ILogDatasource, LoggerFactory } from "@jmlq/logger";
63
- import { FsAdapter } from "../fs/fs.adapter";
64
- import { MongoAdapter } from "../mongo/mongo.adapter";
65
- import { PgAdapter } from "../pg/pg.adapter";
66
- import { LoggerBootstrapOptions } from "./logger.bootstrap.options";
67
-
68
- export class LoggerBootstrap {
69
- private constructor(
70
- private readonly _logger: ReturnType<typeof LoggerFactory.create>
71
- ) {}
72
-
73
- /**
74
- * Este es el punto de entrada para construir el logger.
75
- */
76
- static async create(opts: LoggerBootstrapOptions): Promise<LoggerBootstrap> {
77
- // Almacena los plugins inicializados.
78
- const dsList: ILogDatasource[] = [];
79
-
80
- // Inyectar datasources
81
- // NOTA: Opcionales depende de las necesidades del cliente
82
- // FS si está configurado
83
- if (opts.adapters?.fs) {
84
- const fs = FsAdapter.create(opts.adapters.fs);
85
- if (fs) dsList.push(fs.datasource);
86
- }
87
- // MongoDB si está configurado
88
- if (opts.adapters?.mongo) {
89
- const mongo = await MongoAdapter.create(opts.adapters.mongo);
90
- if (mongo) dsList.push(mongo.datasource);
91
- }
92
- // Postgresql si está configurado
93
- if (opts.adapters?.pg) {
94
- const pg = await PgAdapter.create(opts.adapters.pg);
95
- if (pg) dsList.push(pg.datasource);
96
- }
97
-
98
- if (dsList.length === 0)
99
- throw new Error("[logger] No hay datasources válidos.");
100
-
101
- // Crear el logger central
102
- const logger = LoggerFactory.create({
103
- datasources: dsList,
104
- minLevel: opts.minLevel,
105
- redactorOptions: {
106
- enabled: opts.pii?.enabled ?? false,
107
- deep: opts.pii?.deep ?? true,
108
- patterns: opts.pii?.patterns ?? [],
109
- },
110
- });
111
-
112
- console.log("✅ Logger creado correctamente.\n");
113
-
114
- // Retornar la instancia final
115
- return new LoggerBootstrap(logger);
116
- }
117
-
118
- // Permite acceder al logger real pero manteniendo encapsulación.
119
- get logger() {
120
- return this._logger;
121
- }
122
-
123
- // Fuerza a que todos los logs pendientes se escriban en los datasources.
124
- // Lo usa el ciclo de vida del servidor (shutdown, errores críticos, etc.).
125
- async flush() {
126
- const logs = this._logger as any;
127
- if (typeof logs.flush === "function") await logs.flush();
128
- }
129
-
130
- // Cierra conexiones y limpia recursos (como conexiones a BD).
131
- // Asegura un apagado ordenado.
132
- async dispose() {
133
- const logs = this._logger as any;
134
- if (typeof logs.dispose === "function") await logs.dispose();
135
- }
136
- }
137
- ```
138
-
139
- ---
140
-
141
- ## PASO 2 – CONFIGURAR PLUGINS / DATASOURCES
142
-
143
- ### 2.1. Plugin FileSystem (`@jmlq/logger-plugin-fs`)
144
-
145
- Path sugerido: `src/infrastructure/adapters/jmlq/logger/fs/fs.adapter.ts`
146
-
147
- `FsAdapter` es un **adaptador de infraestructura** cuyo propósito es `crear`, `inicializar` y `exponer` un `datasource` de logs basado en archivos (FileSystem) usando el plugin `@jmlq/logger-plugin-fs`.
148
-
149
- ```ts
150
- import {
151
- // Función del plugin que crea realmente el datasource de FileSystem.
152
- createFsDatasource,
153
- // Tipo de configuración que el plugin requiere
154
- IFilesystemDatasourceOptions,
155
- } from "@jmlq/logger-plugin-fs";
156
- // Interfaz estándar que todos los datasources deben implementar para funcionar dentro de @jmlq/logger
157
- import type { ILogDatasource } from "@jmlq/logger";
158
-
159
- export class FsAdapter {
160
- private constructor(private readonly ds: ILogDatasource) {}
161
-
162
- // Este es el punto central del adaptador.
163
- static create(opts: IFilesystemDatasourceOptions): FsAdapter | undefined {
164
- try {
165
- // Se inicializa el plugin.
166
- // Se configuran rutas, políticas de rotación, serializer, etc.
167
- // El datasource queda listo para recibir logs.
168
- const ds = createFsDatasource(opts);
169
- console.log("[logger] Conectado a FS para logs");
170
- return new FsAdapter(ds);
171
- } catch (e: any) {
172
- console.warn("[logger] FS deshabilitado:", e?.message ?? e);
173
- }
174
- }
175
- // Permite a otras capas (especialmente LoggerBootstrap) obtener el datasource real que necesita LoggerFactory.create
176
- get datasource(): ILogDatasource {
177
- return this.ds;
178
- }
179
- }
180
- ```
181
-
182
- ---
183
-
184
- ### 2.2. Plugin MongoDB (`@jmlq/logger-plugin-mongo`)
185
-
186
- Path sugerido: `src/infrastructure/adapters/jmlq/logger/mongo/mongo.adapter.ts`
187
-
188
- `MongoAdapter` es un **adaptador de infraestructura** cuyo propósito es `crear`, `inicializar` y `exponer` un `datasource` de los logs almacenados en `MongoDB` usando el plugin `@jmlq/logger-plugin-mongo`.
189
-
190
- ```ts
191
- // Interfaz estándar que todos los datasources deben implementar para funcionar dentro de @jmlq/logger
192
- import type { ILogDatasource } from "@jmlq/logger";
193
- import {
194
- // Función del plugin que crea realmente el datasource de MongoDb.
195
- createMongoDatasource,
196
- // Tipo de configuración que el plugin requiere
197
- MongoPluginConfig,
198
- } from "@jmlq/logger-plugin-mongo";
199
-
200
- export class MongoAdapter {
201
- private constructor(private readonly ds: ILogDatasource) {}
202
-
203
- // Este es el punto central del adaptador.
204
- static async create(
205
- opts: MongoPluginConfig
206
- ): Promise<MongoAdapter | undefined> {
207
- try {
208
- const ds = await createMongoDatasource(opts);
209
- console.log("[logger] Conectado a MongoDB para logs");
210
- return new MongoAdapter(ds);
211
- } catch (e: any) {
212
- console.warn("[logger] MongoDB deshabilitado:", e?.message ?? e);
213
- }
214
- }
215
- get datasource(): ILogDatasource {
216
- return this.ds;
217
- }
218
- }
219
- ```
220
-
221
- ---
222
-
223
- ### 2.3. Plugin PostgreSQL (`@jmlq/logger-plugin-postgresql`)
224
-
225
- Path sugerido: `src/infrastructure/adapters/jmlq/logger/pg/pg.adapter.ts`
226
-
227
- `PgAdapter` es un **adaptador de infraestructura** cuyo propósito es `crear`, `inicializar` y `exponer` un `datasource` de los logs almacenados en `Postgresal` usando el plugin `@jmlq/logger-plugin-postgresql`.
228
-
229
- ```ts
230
- // Interfaz estándar que todos los datasources deben implementar para funcionar dentro de @jmlq/logger
231
- import type { ILogDatasource } from "@jmlq/logger";
232
- import {
233
- // Función del plugin que crea realmente el datasource de Postgresql.
234
- createPostgresDatasource,
235
- // Tipo de configuración que el plugin requiere
236
- PostgresPluginConfig,
237
- } from "@jmlq/logger-plugin-postgresql";
238
-
239
- export class PgAdapter {
240
- private constructor(private readonly ds: ILogDatasource) {}
241
-
242
- // Este es el punto central del adaptador.
243
- static async create(
244
- opts: PostgresPluginConfig
245
- ): Promise<PgAdapter | undefined> {
246
- try {
247
- const ds = await createPostgresDatasource(opts);
248
- console.log("[logger] Conectado a PostgreSQL para logs");
249
- return new PgAdapter(ds);
250
- } catch (e: any) {
251
- console.warn("[logger] PostgreSQL deshabilitado:", e?.message ?? e);
252
- }
253
- }
254
-
255
- get datasource(): ILogDatasource {
256
- return this.ds;
257
- }
258
- }
259
- ```
260
-
261
- ### 2.4. Configuración Global @jmlq/Logger
262
-
263
- Path sugerido: `src/infrastructure/adapters/jmlq/logger/index.ts`
264
-
265
- Este archivo implementa el **bootstrap global** y singleton del sistema de logging de tu proyecto.
266
- En términos simples: garantiza que **el logger, sus adapters y sus reglas de PII se inicialicen una sola vez**, sin importar cuántas veces se importe este archivo en distintos módulos.
267
-
268
- ```ts
269
- import { envs } from "../../../../config/plugins";
270
- import { LoggerBootstrap } from "./core/logger.bootstrap";
271
- import { parseLogLevel } from "./helper";
272
-
273
- // Promesa Singleton
274
- declare global {
275
- var __LOGGER_BOOT__: Promise<LoggerBootstrap> | undefined;
276
- }
277
-
278
- // Construye toda la infraestructura del logger
279
- async function init() {
280
- // Envía la configuración del logger en envs (se puede usar .env)
281
- return LoggerBootstrap.create({
282
- // Nivel mínimo de log
283
- minLevel: parseLogLevel(envs.logger.LOGGER_LEVEL),
284
- // Configuración de PII (ocultamiento de datos sensibles)
285
- pii: {
286
- // Activación global
287
- enabled: envs.logger.LOGGER_PII_ENABLED,
288
- // Incluir reglas de validación declaradas en @jmlq/logger
289
- includeDefaults: envs.logger.LOGGER_PII_INCLUDE_DEFAULTS,
290
- // Incluye recursión profunda
291
- deep: true,
292
- patterns: [
293
- // Patrón para ocultar tarjetas
294
- {
295
- pattern: "\\b\\d{4}-\\d{4}-\\d{4}-\\d{4}\\b",
296
- replaceWith: "****-****-****-****",
297
- },
298
- // Patrón para ocultar correos
299
- {
300
- pattern: "[\\w.-]+@[\\w.-]+",
301
- replaceWith: "***@***",
302
- },
303
- // NOTA: Se pueden agregar mas reglas
304
- ],
305
- },
306
- adapters: {
307
- // NOTA: Opcionales depende de las necesidades del cliente
308
- // Adapter FS
309
- fs: envs.logger.LOGGER_FS_PATH
310
- ? {
311
- // Path donde se guarda el log
312
- basePath: envs.logger.LOGGER_FS_PATH,
313
- // Patrón de archivo por fecha
314
- fileNamePattern: "app-{yyyy}{MM}{dd}.log",
315
- // Tipo de Rotacion (Generación de un nuevo archivo puede ser por día, peso, etc)
316
- rotation: { by: "day" },
317
- // Crear directorio nuevo
318
- mkdir: true,
319
- // Darle formato a la trama
320
- serializer: {
321
- serialize(log: any) {
322
- // Serializer personalizado
323
- return JSON.stringify(log, null, 2);
324
- },
325
- },
326
- // Se ejecuta acción cuando se genera un nuevo archivo (Se podría enviar un email por ejempo)
327
- onRotate: (oldPath, newPath) => {
328
- console.log(
329
- ` [Rotate] Rotación completada: ${oldPath.absolutePath} → ${newPath.absolutePath}`
330
- );
331
- },
332
- // Se ejecuta acción cuando se presenta un error
333
- onError: (err) => {
334
- console.error(" [Error Handler]", err.message);
335
- },
336
- }
337
- : undefined,
338
- // Adapter MongoDB
339
- mongo: envs.logger.LOGGER_MONGO_DB_URL
340
- ? {
341
- url: envs.logger.LOGGER_MONGO_DB_URL,
342
- dbName: envs.logger.LOGGER_MONGO_DB_NAME ?? "logs",
343
- collectionName:
344
- envs.logger.LOGGER_MONGO_COLLECTION_NAME ?? "app_logs",
345
- ensureIndexes: true,
346
- // Tiempo de retención del log
347
- retentionDays: 7,
348
- // Crear Index extra (opcional)
349
- extraIndexes: [{ key: { level: 1 } }],
350
- // writeOrdered: true,
351
- createIfMissing: true,
352
- }
353
- : undefined,
354
- // Adapter Postgresql
355
- pg: envs.logger.LOGGER_PG_CONNECTION_STRING
356
- ? {
357
- connectionString: envs.logger.LOGGER_PG_CONNECTION_STRING,
358
- table: envs.logger.LOGGER_PG_TABLE_NAME ?? "app_logs",
359
- schema: envs.logger.LOGGER_PG_SCHEMA ?? "public",
360
- createIfMissing: true,
361
- // Tiempo de retención del log
362
- retentionDays: 7,
363
- }
364
- : undefined,
365
- //----
366
- },
367
- });
368
- }
369
-
370
- // Inicializar promesa Singleton
371
- export const bootReady: Promise<LoggerBootstrap> =
372
- globalThis.__LOGGER_BOOT__ ?? (globalThis.__LOGGER_BOOT__ = init());
373
-
374
- // Acceso directo al logger
375
- export const loggerReady = bootReady.then((b) => b.logger);
376
-
377
- // Vaciar buffers antes de terminar el proceso
378
- export async function flushLogs() {
379
- const boot = await bootReady;
380
- await boot.flush();
381
- }
382
-
383
- // Cerrar recursos
384
- export async function disposeLogs() {
385
- const boot = await bootReady;
386
- await boot.dispose();
387
- }
388
- ```
389
-
390
- **NOTA**: `parseLogLevel` es un helper para convertir un `string` en `LogLevel`:
391
-
392
- ```ts
393
- import { LogLevel } from "@jmlq/logger";
394
-
395
- export function parseLogLevel(value?: string): LogLevel {
396
- if (!value) return LogLevel.DEBUG; // fallback seguro
397
-
398
- const normalized = value.toUpperCase();
399
-
400
- const map: Record<string, LogLevel> = {
401
- TRACE: LogLevel.TRACE,
402
- DEBUG: LogLevel.DEBUG,
403
- INFO: LogLevel.INFO,
404
- WARN: LogLevel.WARN,
405
- ERROR: LogLevel.ERROR,
406
- FATAL: LogLevel.FATAL,
407
- };
408
-
409
- return map[normalized] ?? LogLevel.DEBUG; // fallback en caso de valor inválido
410
- }
411
- ```
412
-
413
- ---
414
-
415
- ## PASO 3 – INTEGRACIÓN CON EXPRESS
416
-
417
- ### 3.1. Extender `Request` con logger y requestId
418
-
419
- Path sugerido: `src/types/express.d.ts`
420
-
421
- ```ts
422
- import "express";
423
- import type { ILogger } from "@jmlq/logger";
424
-
425
- // Cada req trae un logger y un requestId.
426
- declare module "express-serve-static-core" {
427
- interface Request {
428
- logger?: ILogger;
429
- requestId?: string;
430
- }
431
- }
432
- ```
433
-
434
- Esto permite escribir `req.logger` y `req.requestId` con type safety.
435
-
436
- ### 3.2. Crear el servidor y adjuntar el logger
437
-
438
- Path sugerido: `src/presentation/server.ts`
439
-
440
- ```ts
441
- import { ILogger } from "@jmlq/logger";
442
- import express, { Request, Response, NextFunction } from "express";
443
- import router from "./routes";
444
- import { randomUUID } from "crypto";
445
-
446
- // Middleware para adjuntar logger por request
447
- function attachLogger(base: ILogger) {
448
- return async (req: Request, _res: Response, next: NextFunction) => {
449
- req.logger = base;
450
- req.requestId = (req.headers["x-request-id"] as string) ?? randomUUID();
451
- next();
452
- };
453
- }
454
-
455
- export function createServer(base: ILogger) {
456
- const app = express();
457
- app.use(express.json());
458
- app.use(attachLogger(base));
459
-
460
- // Aquí se montan las rutas que llamarán a tus casos de uso
461
- // <-- los handlers pueden usar req.logger
462
-
463
- app.get("/health", (_req, res) => res.json({ ok: true }));
464
-
465
- // IMPORTANTE: Siempre colocar un errorHandler al final
466
-
467
- return app;
468
- }
469
- ```
470
-
471
- En tus controladores, la idea es:
472
-
473
- ```ts
474
- // controlador de ejemplo (no incluido en este archivo)
475
- req.logger?.info("user.create.request", {
476
- requestId: req.requestId,
477
- // aquí podrías pasar info de la request (sin PII sensible)
478
- });
479
-
480
- // aquí llamarías al caso de uso correspondiente (no se implementa en este doc)
481
- ```
482
-
483
- ---
484
-
485
- ## PASO 4 – BOOTSTRAP DE LA APLICACIÓN (Express + logger)
486
-
487
- Path sugerido: `src/app.ts`
488
-
489
- Aquí se orquesta todo:
490
-
491
- ```ts
492
- import { envs } from "./config/plugins";
493
- import {
494
- disposeLogs,
495
- flushLogs,
496
- loggerReady,
497
- } from "./infrastructure/adapters/jmlq/logger";
498
- import { createServer } from "./presentation/server";
499
-
500
- async function bootstrap() {
501
- const logger = await loggerReady;
502
-
503
- const app = createServer(logger);
504
- const server = app.listen(envs.PORT, envs.HOST, () => {
505
- console.log(`🚀 Server running at http://${envs.HOST}:${envs.PORT}`);
506
- logger.info("http.start", { host: envs.HOST, port: envs.PORT });
507
- });
508
-
509
- server.on("error", async (err: any) => {
510
- logger.error("http.listen.error", {
511
- message: err?.message,
512
- stack: err?.stack,
513
- });
514
- await flushLogs().catch(() => {});
515
- await disposeLogs().catch(() => {});
516
- process.exit(1);
517
- });
518
-
519
- // Señales de apagado limpio
520
- const shutdown = async (reason: string, code = 0) => {
521
- logger.info("app.shutdown.begin", { reason });
522
- server.close(async () => {
523
- try {
524
- await flushLogs();
525
- } catch {}
526
- try {
527
- await disposeLogs();
528
- } catch {}
529
- logger.info("app.shutdown.end", { code });
530
- process.exit(code);
531
- });
532
- };
533
-
534
- process.on("SIGINT", () => shutdown("SIGINT"));
535
- process.on("SIGTERM", () => shutdown("SIGTERM"));
536
-
537
- // Fallos no controlados
538
- process.on("unhandledRejection", async (err) => {
539
- const e = err as any;
540
- logger.error("unhandled.rejection", {
541
- message: e?.message,
542
- stack: e?.stack,
543
- });
544
- // Aquí se podría enviar una notificación (email, etc.)
545
- await shutdown("unhandledRejection", 1);
546
- });
547
-
548
- process.on("uncaughtException", async (err) => {
549
- logger.error("uncaught.exception", {
550
- message: err.message,
551
- stack: err.stack,
552
- });
553
- // Aquí se podría enviar una notificación (email, etc.)
554
- await shutdown("uncaughtException", 1);
555
- });
556
- }
557
-
558
- bootstrap().catch(async (err) => {
559
- // Falla durante el bootstrap
560
- const logger = await loggerReady.catch(() => null);
561
- if (logger) {
562
- logger.error("bootstrap.fatal", {
563
- message: (err as Error)?.message,
564
- stack: (err as Error)?.stack,
565
- });
566
- await flushLogs().catch(() => {});
567
- await disposeLogs().catch(() => {});
568
- } else {
569
- // Último recurso si el logger no llegó a inicializar
570
- console.error("Fatal error (no logger):", err);
571
- }
572
- process.exit(1);
573
- });
574
- ```
575
-
576
- Puntos clave:
577
-
578
- - `loggerReady` asegura que el logger está inicializado antes de levantar HTTP.
579
- - `flushLogs` y `disposeLogs` se usan:
580
- - En errores de `server.listen`.
581
- - En apagado por señales.
582
- - En fallos no controlados.
583
-
584
- ---
585
-
586
- ### Filtros Avanzados
587
-
588
- ```ts
589
- // Paginación
590
- const page1 = await logger.getLogs({ limit: 10, offset: 0 });
591
- const page2 = await logger.getLogs({ limit: 10, offset: 10 });
592
- ```
593
-
594
- ## 🔧 Configuración con Variables de Entorno
595
-
596
- ```env
597
- ###############################################
598
- # @jmlq/logger – Dummy Environment Variables
599
- ###############################################
600
-
601
- # ---------------------------------------------
602
- # Nivel mínimo del logger
603
- # Valores: TRACE, DEBUG, INFO, WARN, ERROR, FATAL
604
- # ---------------------------------------------
605
- LOG_LEVEL=INFO
606
-
607
- # ---------------------------------------------
608
- # Protección de PII (datos sensibles)
609
- # ---------------------------------------------
610
- LOGGER_PII_ENABLED=true
611
- LOGGER_PII_INCLUDE_DEFAULTS=false
612
-
613
- # ---------------------------------------------
614
- # FileSystem Plugin
615
- # ---------------------------------------------
616
- LOGGER_FS_PATH=./logs
617
-
618
- # ---------------------------------------------
619
- # MongoDB Plugin
620
- # ---------------------------------------------
621
- LOGGER_MONGO_DB_URL=mongodb://demo_user:demo_pass@localhost:27018/?authSource=admin
622
- LOGGER_MONGO_DB_NAME=logger_demo_db
623
- LOGGER_MONGO_COLLECTION_NAME=logger_demo_logs
624
-
625
- # ---------------------------------------------
626
- # PostgreSQL Plugin
627
- # ---------------------------------------------
628
- LOGGER_PG_CONNECTION_STRING=postgres://demo_user:demo_pass@localhost:5436/logger_demo_db
629
- LOGGER_PG_TABLE_NAME=logger_demo_table
630
- LOGGER_PG_SCHEMA=public
631
-
632
- ```