Winston: Solicitação: funções em metadados padrão

Criado em 29 mar. 2019  ·  3Comentários  ·  Fonte: winstonjs/winston

A questão subjacente aqui é um pouco complexa, então vou resumir o que estamos pedindo aqui -- estaríamos interessados ​​em ver a capacidade de defaultMetadata incluir valores gerados dinamicamente fornecendo uma função como um campo. Por exemplo:

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

Para uma justificativa completa veja abaixo.

Por favor, conte-nos sobre o seu ambiente:

  • _ winston versão?_

    • [ ] winston@2

    • [x] winston@3

  • _ node -v saídas:_ v10.15.2
  • _Sistema Operacional?_ macOS (irrelevante)
  • _Idioma?_ ES7 (irrelevante)

Qual é o problema?

Como autores do Stackdriver Logging Transport for Winston, gostaríamos de oferecer suporte à correlação de solicitação de log. O módulo async_hooks nos dá a capacidade de armazenar informações específicas de solicitação, como um ID de solicitação. No entanto, não temos meios de recuperá-lo de forma confiável no lado Transport .

O problema é melhor descrito por exemplo. O código a seguir produz essa saída quando ~100 solicitações são feitas em rápida sucessão:

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);

Observe a incompatibilidade nos IDs de solicitação "esperado" e "real". Se formos o autor de MyTransport , não teríamos como obter com precisão o ID de solicitação correto consultando o ID de execução atual ("real"). Em vez disso, devemos confiar no usuário passando o ID de solicitação correto como metadados ("esperado"). Isso ocorre porque o Winston 3 agrupa logs (via readable-stream ) quando um Transporte adia sua invocação de seu retorno de chamada log .

O problema é que não queremos depender de usuários passando um ID de solicitação para nós; queremos que isso aconteça automaticamente. Afinal, os usuários provavelmente iriam querer apenas escrever

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

e deixe descansar por nossa conta.

O que você espera que aconteça em vez disso?

Os IDs de solicitação esperados e reais (da saída vinculada) correspondem.

Outra informação

É inviável corrigir o código para que os IDs de solicitação reais correspondam, portanto, uma solução para esse problema seria permitir que os usuários especificassem funções como campos em defaultMetadata que são invocados quando o registrador é chamado. Dessa forma, o código mudaria para this , o que permite que os usuários escrevam seu código sem qualquer conhecimento do ID da solicitação, com a pequena ressalva de que eles fornecem um thunk como um campo defaultMetadata (que talvez nosso módulo forneça para que eles usem).

Comentários muito úteis

Encontrei uma solução alternativa que pode ser usada até que a solução proposta seja implementada - use getters:

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

UPD: corrigido o bug mencionado por @mooski

Todos 3 comentários

então uma solução para este problema seria permitir que os usuários especificassem funções como campos em defaultMetadata

Eu estava pensando que isso poderia ser implementado semelhante ao redux thunks . Em vez de ter uma propriedade com valor de função em defaultMetadata , estava pensando que defaultMetadata si poderia ser opcionalmente uma função. Como um thunk, winston usaria o valor retornado invocando defaultMetadata nesse caso. Em outras palavras:

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

Encontrei uma solução alternativa que pode ser usada até que a solução proposta seja implementada - use getters:

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

UPD: corrigido o bug mencionado por @mooski

~ Ótima solução @Alexsey - para quem usa este código, esteja ciente de que os dois pontos depois de timeWritten não devem estar lá.~

Atualização: corrigida por @Alexsey

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

tagyoureit picture tagyoureit  ·  4Comentários

Buzut picture Buzut  ·  3Comentários

JaehyunLee-B2LiNK picture JaehyunLee-B2LiNK  ·  3Comentários

pocesar picture pocesar  ·  3Comentários

anks333 picture anks333  ·  3Comentários