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.
winston
versión?_winston@2
winston@3
node -v
salidas:_ v10.15.2Como 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.
Los ID de solicitud esperados y reales (de la salida vinculada) coinciden.
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).
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
Comentario más útil
Encontré una solución alternativa que se puede usar hasta que se implemente la solución propuesta: use captadores:
UPD: corrigió el error mencionado por @mooski