Masalah mendasar di sini agak rumit jadi saya akan merangkum apa yang kami minta di sini -- kami akan tertarik melihat kemampuan defaultMetadata
untuk memasukkan nilai yang dihasilkan secara dinamis dengan menyediakan fungsi sebagai bidang. Sebagai contoh:
const logger = createLogger({
defaultMeta: {
// Function to be evaluated every time a log is written.
timeWritten: () => { return `Date.now()`; }
},
// ...
});
Untuk pembenaran lengkap lihat di bawah.
winston
versi?_winston@2
winston@3
node -v
keluaran:_ v10.15.2Sebagai penulis Stackdriver Logging Transport untuk Winston, kami ingin mendukung korelasi permintaan log. Modul async_hooks
memberi kita kemampuan untuk menyimpan informasi khusus permintaan seperti ID permintaan. Namun, kami tidak memiliki cara yang andal untuk mengambilnya di sisi Transport
.
Masalahnya paling baik dijelaskan dengan contoh. Kode berikut menghasilkan output ini ketika ~100 permintaan dibuat secara berurutan:
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);
Perhatikan ketidakcocokan dalam ID permintaan "yang diharapkan" dan "aktual". Jika kami adalah pembuat MyTransport
, kami tidak akan memiliki cara untuk mendapatkan ID permintaan yang benar secara akurat dengan melihat ID eksekusi saat ini ("aktual"). Sebagai gantinya, kita harus mengandalkan pengguna yang meneruskan ID permintaan yang benar sebagai metadata ("diharapkan"). Ini karena Winston 3 mengelompokkan log (melalui readable-stream
) ketika sebuah Transport menangguhkan pemanggilannya dari log
callback-nya.
Masalahnya adalah kami tidak ingin bergantung pada pengguna yang mengirimkan ID permintaan untuk kami; kami ingin itu terjadi secara otomatis. Lagi pula, pengguna mungkin hanya ingin menulis
app.get('/', async (req, res) => {
logger.info('hello');
res.sendStatus(200);
});
dan meninggalkan untuk beristirahat kepada kami.
ID permintaan yang diharapkan dan yang sebenarnya (dari keluaran tertaut) cocok.
Tidak mungkin untuk memperbaiki kode sehingga ID permintaan yang sebenarnya cocok, jadi solusi untuk masalah ini adalah mengizinkan pengguna untuk menentukan fungsi sebagai bidang pada defaultMetadata
yang dipanggil saat logger dipanggil. Dengan begitu, kode akan berubah menjadi this , yang memungkinkan pengguna untuk menulis kode mereka tanpa mengetahui ID permintaan, dengan peringatan kecil yang mereka berikan sebagai bidang defaultMetadata
(yang mungkin disediakan oleh modul kami mereka untuk digunakan).
jadi solusi untuk masalah ini adalah mengizinkan pengguna untuk menentukan fungsi sebagai bidang pada defaultMetadata
Saya berpikir bahwa ini dapat diimplementasikan mirip dengan redux thunks . Alih-alih memiliki properti bernilai fungsi di defaultMetadata
, berpikir bahwa defaultMetadata
itu sendiri bisa menjadi fungsi opsional. Seperti thunk, winston
akan menggunakan nilai yang dikembalikan dengan memanggil defaultMetadata
dalam kasus seperti itu. Dengan kata lain:
const logger = createLogger({
defaultMeta: () => {
// Function to be evaluated every time a log is written.
return {
timeWritten: Date.now();
// ..
}
}
// ...
});
Saya telah menemukan solusi yang dapat digunakan hingga solusi yang diusulkan akan diimplementasikan - gunakan getter:
const logger = createLogger({
defaultMeta: {
get timeWritten () { return `Date.now()`; }
},
// ...
});
UPD: memperbaiki bug yang disebutkan oleh @mooski
~Solusi hebat @Alexsey - bagi siapa pun yang menggunakan kode ini, ketahuilah bahwa titik dua setelah timeWritten
seharusnya tidak ada di sana.~
Pembaruan: diperbaiki oleh @Alexsey
Komentar yang paling membantu
Saya telah menemukan solusi yang dapat digunakan hingga solusi yang diusulkan akan diimplementasikan - gunakan getter:
UPD: memperbaiki bug yang disebutkan oleh @mooski