platform-nest : le journal de Nest en JSON structuré, au niveau de LOG_LEVEL, corrélé à la requête (request_id, trace_id) #171

Closed
opened 2026-10-16 12:58:58 +00:00 by nicolas-fryder · 0 comments

Constat (mesuré sur nuage, socle 0.24.0)

Un satellite NestJS assemblé par createEqsApp() produit deux journaux qui ne se ressemblent pas, et le plus utilisé des deux n'est pas exploitable :

  1. Le Logger de Nest — utilisé partout dans le code métier — perd tout ce qui n'est pas avertissement ou erreur.
    @eqs/platform-nest/dist/assemblage.js:335 : logger: options.logger === false ? false : [...(options.logger ?? ['warn', 'error', 'fatal'])].
    Nuage compte 75 appels new Logger(...) dans apps/api/src, dont 35 log/debug (« Session … relancée », « COPC copié sur SMB »…) : tous jetés. Vérifié : aucune ligne [Nest] … LOG dans docker logs nuage-api ni docker logs nuage-worker.

  2. LOG_LEVEL ne pilote pas ce journal-là. @eqs/config valide LOG_LEVEL (config/dist/index.js:133, debug|info|warn|error), mais seul le journal de @eqs/telemetry le lit (telemetry/dist/index.js:50-53, niveauDepuisEnv). Poser LOG_LEVEL=debug ne fait rien apparaître du code métier.

  3. Le format est du texte, celui de ConsoleLogger, alors que le socle a déjà le bon journal : creerLogger() de @eqs/telemetry écrit des JSON lines (ts, level, service, event, trace_id, span_id), masque les secrets et émet en OTLP quand le SDK tourne (telemetry/dist/index.js:55-100). Rien ne relie les deux : l'option logger de OptionsEqsApp n'accepte qu'une liste de niveaux, pas un LoggerService.

  4. Aucune corrélation à la requête. L'enveloppe d'erreur rend un requestId au client (EqsExceptionFilter, platform-nest/dist/assemblage.js:61-66, requestIdMiddleware l.400), mais :

    • le filtre l'écrit dans une ligne TEXTE (errors/dist/nest.js:37, POST /api/… → 500 [req:<uuid>] …), illisible par un collecteur ;
    • creerLogger annonce request_id dans sa documentation (telemetry/dist/index.js:56) mais ne le pose jamais : seuls trace_id/span_id sont lus, sur le span actif — donc rien quand OTEL_ENABLED=false.
      Une erreur signalée par un utilisateur avec son identifiant de requête ne se retrouve dans aucun journal.
  5. Le nom de service ne distingue pas les processus d'une même image. Le journal JSON prend OTEL_SERVICE_NAME, sinon SERVICE_NAME (telemetry/dist/index.js:62) ; la télémétrie prend SERVICE_NAME brut (platform-nest/dist/index.js:99), et le préfixe Redis aussi (assemblage.js:195, eqs:<SERVICE_NAME>:). Un satellite qui lance son worker depuis la même image doit choisir entre un nom distinct (et un préfixe Redis distinct, non documenté) et des journaux indiscernables. Nuage pose désormais SERVICE_NAME=nuage-worker pour son worker.

Demande

  1. createEqsApp() installe par défaut un LoggerService Nest qui écrit par creerLogger() (JSON lines, masquage, OTLP), le contexte Nest en champ (contexte: 'ConversionProcessor'), le message en message. Le ConsoleLogger texte reste disponible pour le développement (LOG_FORMAT=texte ou équivalent).
  2. Son niveau suit LOG_LEVEL (debug → tout ; info → log compris ; défaut info en production). L'option logger accepte un LoggerService en plus d'une liste de niveaux.
  3. request_id sur chaque ligne émise pendant une requête — lu dans le contexte asynchrone posé par requestIdMiddleware (comme appelantCourant()), indépendamment d'OTel ; trace_id/span_id quand un span est actif. Le filtre d'erreurs écrit sa ligne par ce journal (event: 'requete.erreur', status, path, request_id, operation_id).
  4. Documenter OTEL_SERVICE_NAME (ou un SERVICE_ROLE) comme la façon de nommer un processus secondaire (worker) sans changer le préfixe Redis ni l'identité HTTP (X-Client-Name), et que la télémétrie le lise aussi.
  5. Un contrôle de conformité (platform.journal-structure ?) : une ligne émise par Logger de Nest pendant une requête sort en JSON, porte request_id, et une ligne log sort au niveau info.

Ce que nuage fait en attendant

  • SERVICE_NAME=nuage-worker pour le worker.
  • Les erreurs que l'application garde déjà (échecs de jobs, conversions et dalles en erreur, maintenances échouées) se consultent par GET /api/admin/erreurs et /admin/erreurs.
  • Le code neuf de maintenance journalise par log de @eqs/telemetry (JSON).

Ouvert depuis Nuage (EQS-Sandbox/NUAGE), règle « le socle par tickets ».

## Constat (mesuré sur nuage, socle 0.24.0) Un satellite NestJS assemblé par `createEqsApp()` produit deux journaux qui ne se ressemblent pas, et le plus utilisé des deux n'est pas exploitable : 1. **Le `Logger` de Nest — utilisé partout dans le code métier — perd tout ce qui n'est pas avertissement ou erreur.** `@eqs/platform-nest/dist/assemblage.js:335` : `logger: options.logger === false ? false : [...(options.logger ?? ['warn', 'error', 'fatal'])]`. Nuage compte 75 appels `new Logger(...)` dans `apps/api/src`, dont 35 `log`/`debug` (« Session … relancée », « COPC copié sur SMB »…) : tous jetés. Vérifié : aucune ligne `[Nest] … LOG` dans `docker logs nuage-api` ni `docker logs nuage-worker`. 2. **`LOG_LEVEL` ne pilote pas ce journal-là.** `@eqs/config` valide `LOG_LEVEL` (`config/dist/index.js:133`, `debug|info|warn|error`), mais seul le journal de `@eqs/telemetry` le lit (`telemetry/dist/index.js:50-53`, `niveauDepuisEnv`). Poser `LOG_LEVEL=debug` ne fait rien apparaître du code métier. 3. **Le format est du texte**, celui de `ConsoleLogger`, alors que le socle a déjà le bon journal : `creerLogger()` de `@eqs/telemetry` écrit des JSON lines (`ts, level, service, event, trace_id, span_id`), masque les secrets et émet en OTLP quand le SDK tourne (`telemetry/dist/index.js:55-100`). Rien ne relie les deux : l'option `logger` de `OptionsEqsApp` n'accepte qu'une **liste de niveaux**, pas un `LoggerService`. 4. **Aucune corrélation à la requête.** L'enveloppe d'erreur rend un `requestId` au client (`EqsExceptionFilter`, `platform-nest/dist/assemblage.js:61-66`, `requestIdMiddleware` l.400), mais : - le filtre l'écrit dans une ligne TEXTE (`errors/dist/nest.js:37`, `POST /api/… → 500 [req:<uuid>] …`), illisible par un collecteur ; - `creerLogger` annonce `request_id` dans sa documentation (`telemetry/dist/index.js:56`) mais ne le pose jamais : seuls `trace_id`/`span_id` sont lus, sur le span actif — donc **rien** quand `OTEL_ENABLED=false`. Une erreur signalée par un utilisateur avec son identifiant de requête ne se retrouve dans aucun journal. 5. **Le nom de service ne distingue pas les processus d'une même image.** Le journal JSON prend `OTEL_SERVICE_NAME`, sinon `SERVICE_NAME` (`telemetry/dist/index.js:62`) ; la télémétrie prend `SERVICE_NAME` brut (`platform-nest/dist/index.js:99`), et le préfixe Redis aussi (`assemblage.js:195`, `eqs:<SERVICE_NAME>:`). Un satellite qui lance son worker depuis la même image doit choisir entre un nom distinct (et un préfixe Redis distinct, non documenté) et des journaux indiscernables. Nuage pose désormais `SERVICE_NAME=nuage-worker` pour son worker. ## Demande 1. `createEqsApp()` installe par défaut un **`LoggerService` Nest qui écrit par `creerLogger()`** (JSON lines, masquage, OTLP), le contexte Nest en champ (`contexte: 'ConversionProcessor'`), le message en `message`. Le `ConsoleLogger` texte reste disponible pour le développement (`LOG_FORMAT=texte` ou équivalent). 2. Son niveau suit **`LOG_LEVEL`** (`debug` → tout ; `info` → `log` compris ; défaut `info` en production). L'option `logger` accepte un `LoggerService` en plus d'une liste de niveaux. 3. **`request_id`** sur chaque ligne émise pendant une requête — lu dans le contexte asynchrone posé par `requestIdMiddleware` (comme `appelantCourant()`), indépendamment d'OTel ; `trace_id`/`span_id` quand un span est actif. Le filtre d'erreurs écrit sa ligne par ce journal (`event: 'requete.erreur'`, `status`, `path`, `request_id`, `operation_id`). 4. Documenter **`OTEL_SERVICE_NAME`** (ou un `SERVICE_ROLE`) comme la façon de nommer un processus secondaire (worker) sans changer le préfixe Redis ni l'identité HTTP (`X-Client-Name`), et que la télémétrie le lise aussi. 5. Un contrôle de conformité (`platform.journal-structure` ?) : une ligne émise par `Logger` de Nest pendant une requête sort en JSON, porte `request_id`, et une ligne `log` sort au niveau `info`. ## Ce que nuage fait en attendant - `SERVICE_NAME=nuage-worker` pour le worker. - Les erreurs que l'application garde déjà (échecs de jobs, conversions et dalles en erreur, maintenances échouées) se consultent par `GET /api/admin/erreurs` et /admin/erreurs. - Le code neuf de maintenance journalise par `log` de `@eqs/telemetry` (JSON). _Ouvert depuis Nuage (EQS-Sandbox/NUAGE), règle « le socle par tickets »._
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Infrastructure/eqs-platform#171
No description provided.