Winston: Demande de fonctionnalité : Fonctions dans les métadonnées par défaut

Créé le 29 mars 2019  ·  3Commentaires  ·  Source: winstonjs/winston

Le problème sous-jacent ici est quelque peu complexe, je vais donc résumer ce que nous demandons ici - nous serions intéressés de voir la possibilité pour defaultMetadata d'inclure des valeurs générées dynamiquement en fournissant une fonction en tant que champ. Par example:

const logger = createLogger({
  defaultMeta: {
    // Function to be evaluated every time a log is written.
    timeWritten: () => { return `Date.now()`; }
  },
  // ...
});

Pour une justification complète, voir ci-dessous.

Veuillez nous parler de votre environnement :

  • _ Version winston ?_

    • [ ] winston@2

    • [x] winston@3

  • _ node -v sorties :_ v10.15.2
  • _Système d'exploitation ?_ macOS (non pertinent)
  • _Langue ?_ ES7 (non pertinent)

Quel est le problème?

En tant qu'auteurs de Stackdriver Logging Transport pour Winston, nous aimerions prendre en charge la corrélation des requêtes de journal. Le module async_hooks nous donne la possibilité de stocker des informations spécifiques à la demande, telles qu'un ID de demande. Cependant, nous n'avons aucun moyen de le récupérer de manière fiable du côté Transport .

Le problème est mieux décrit par un exemple. Le code suivant produit cette sortie lorsque ~100 requêtes sont effectuées en succession rapide :

const ah = require('async_hooks');
const express = require('express');
const { createLogger } = require('winston');
const TransportStream = require('winston-transport');

// Activate async hooks.
let requestIdHighWater = 0;
const requestIdTable = {};
ah.createHook({
  init: (child, resource, parent) => {
    requestIdTable[child] = requestIdTable[parent];
  }
}).enable();

// OUR CODE

class MyTransport extends TransportStream {
  log(info, next) {
    // From the transport, the request ID is not reliable.
    const actualRequestId = requestIdTable[ah.executionAsyncId()];
    console.log(`Expected: ${info.expectedRequestId}, Actual: ${actualRequestId}`);
    setImmediate(next);
  }
}

// OUR USER'S CODE

const logger = createLogger({
  transports: [new MyTransport()]
});

const app = express();

app.get('/', async (req, res) => {
  // Store a request ID.
  const requestId = requestIdHighWater++;
  requestIdTable[ah.executionAsyncId()] = requestId;
  logger.info('hello', { expectedRequestId: requestId });
  res.sendStatus(200);
});

app.listen(3000);

Notez l'incompatibilité entre les ID de requête "attendus" et "réels". Si nous sommes l'auteur de MyTransport , nous n'aurions aucun moyen d'obtenir avec précision l'ID de requête correct en consultant l'ID d'exécution actuel ("réel"). Au lieu de cela, nous devons compter sur l'utilisateur qui transmet l'ID de demande correct en tant que métadonnées ("attendu"). En effet, Winston 3 regroupe les journaux (via readable-stream ) lorsqu'un transport diffère son invocation de son rappel log .

Le problème est que nous ne voulons pas compter sur les utilisateurs qui nous transmettent un ID de demande ; nous voulons que cela se produise automatiquement. Après tout, les utilisateurs voudraient probablement simplement écrire

app.get('/', async (req, res) => {
  logger.info('hello');
  res.sendStatus(200);
});

et laissez-nous reposer.

Qu'attendez-vous qu'il se passe à la place ?

Les ID de demande attendus et réels (de la sortie liée) correspondent.

Les autres informations

Il est impossible de corriger le code afin que les ID de demande réels correspondent, donc une solution à ce problème serait de permettre aux utilisateurs de spécifier des fonctions en tant que champs sur defaultMetadata qui sont invoqués lorsque l'enregistreur est appelé. De cette façon, le code changerait en this , ce qui permet aux utilisateurs d'écrire leur code sans aucune connaissance de l'ID de la requête, avec la petite mise en garde qu'ils fournissent un thunk en tant que champ defaultMetadata (que notre module fournirait peut-être pour à utiliser).

Commentaire le plus utile

J'ai trouvé une solution de contournement qui peut être utilisée jusqu'à ce que la solution proposée soit implémentée - utilisez des getters :

const logger = createLogger({
  defaultMeta: {
    get timeWritten () { return `Date.now()`; }
  },
  // ...
});

UPD : correction du bug mentionné par @mooski

Tous les 3 commentaires

donc une solution à ce problème serait de permettre aux utilisateurs de spécifier des fonctions en tant que champs sur defaultMetadata

Je pensais que cela pourrait être implémenté de la même manière que les thunks redux . Au lieu d'avoir une propriété à valeur de fonction dans defaultMetadata , je pensais que defaultMetadata lui-même pourrait éventuellement être une fonction. Comme un thunk, winston utiliserait la valeur renvoyée en appelant defaultMetadata dans un tel cas. En d'autres termes:

const logger = createLogger({
  defaultMeta: () => {
    // Function to be evaluated every time a log is written.
    return {
       timeWritten: Date.now();
       // ..
    }
  }
  // ...
});

J'ai trouvé une solution de contournement qui peut être utilisée jusqu'à ce que la solution proposée soit implémentée - utilisez des getters :

const logger = createLogger({
  defaultMeta: {
    get timeWritten () { return `Date.now()`; }
  },
  // ...
});

UPD : correction du bug mentionné par @mooski

~ Excellente solution @Alexsey - pour quiconque utilise ce code, sachez simplement que les deux-points après timeWritten ne devraient pas être là.~

Mise à jour : corrigée par @Alexsey

Cette page vous a été utile?
0 / 5 - 0 notes