Winston: Solicitud: funciones en metadatos predeterminados

Creado en 29 mar. 2019  ·  3Comentarios  ·  Fuente: winstonjs/winston

El problema subyacente aquí es algo complejo, así que resumiré lo que estamos pidiendo aquí: nos interesaría ver la capacidad de defaultMetadata para incluir valores generados dinámicamente al proporcionar una función como campo. Por ejemplo:

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

Para una justificación completa ver más abajo.

Cuéntanos sobre tu entorno:

  • _ winston versión?_

    • [ ] winston@2

    • [x] winston@3

  • _ node -v salidas:_ v10.15.2
  • _¿Sistema operativo?_ macOS (irrelevante)
  • _¿Idioma?_ ES7 (irrelevante)

¿Cuál es el problema?

Como autores de Stackdriver Logging Transport para Winston, nos gustaría admitir la correlación de solicitudes de registros. El módulo async_hooks nos brinda la capacidad de almacenar información específica de la solicitud, como una ID de solicitud. Sin embargo, no tenemos un medio confiable para recuperarlo en el lado Transport .

El problema se describe mejor con un ejemplo. El siguiente código produce este resultado cuando se realizan ~100 solicitudes en rápida sucesión:

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

Tenga en cuenta la discrepancia entre los ID de solicitud "esperados" y "reales". Si somos el autor de MyTransport , no tendríamos forma de obtener con precisión el ID de solicitud correcto consultando el ID de ejecución actual ("real"). En su lugar, debemos confiar en que el usuario pase el ID de solicitud correcto como metadatos ("esperados"). Esto se debe a que Winston 3 registra los lotes (a través readable-stream ) cuando un transporte difiere la invocación de su devolución de llamada log .

El problema es que no queremos confiar en que los usuarios nos pasen una ID de solicitud; queremos que suceda automáticamente. Después de todo, los usuarios probablemente querrían simplemente escribir

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

y dejar descansar hasta nosotros.

¿Qué esperas que suceda en su lugar?

Los ID de solicitud esperados y reales (de la salida vinculada) coinciden.

Otra información

No es factible corregir el código para que coincidan los ID de solicitud reales, por lo que una solución a este problema sería permitir a los usuarios especificar funciones como campos en defaultMetadata que se invocan cuando se llama al registrador. De esa manera, el código cambiaría a this , que permite a los usuarios escribir su código sin ningún conocimiento de la ID de la solicitud, con la pequeña advertencia de que proporcionan un thunk como un campo defaultMetadata (que tal vez nuestro módulo proporcionaría para ellos para usar).

Comentario más útil

Encontré una solución alternativa que se puede usar hasta que se implemente la solución propuesta: use captadores:

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

UPD: corrigió el error mencionado por @mooski

Todos 3 comentarios

por lo que una solución a este problema sería permitir a los usuarios especificar funciones como campos en defaultMetadata

Estaba pensando que esto podría implementarse de manera similar a redux thunks . En lugar de tener una propiedad con valor de función en defaultMetadata , estaba pensando que defaultMetadata en sí mismo podría ser opcionalmente una función. Como un thunk, winston usaría el valor devuelto al invocar defaultMetadata en tal caso. En otras palabras:

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

Encontré una solución alternativa que se puede usar hasta que se implemente la solución propuesta: use captadores:

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

UPD: corrigió el error mencionado por @mooski

~Gran solución @Alexsey - para cualquier persona que use este código, tenga en cuenta que los dos puntos después timeWritten no deberían estar allí.~

Actualización: corregido por @Alexsey

¿Fue útil esta página
0 / 5 - 0 calificaciones