Winston: Permintaan: Fungsi dalam Metadata Default

Dibuat pada 29 Mar 2019  ·  3Komentar  ·  Sumber: winstonjs/winston

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.

Tolong beritahu kami tentang lingkungan Anda:

  • _ winston versi?_

    • [ ] winston@2

    • [x] winston@3

  • _ node -v keluaran:_ v10.15.2
  • _Sistem Operasi?_ macOS (tidak relevan)
  • _Bahasa?_ ES7 (tidak relevan)

Apa masalahnya?

Sebagai 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.

Apa yang Anda harapkan terjadi sebagai gantinya?

ID permintaan yang diharapkan dan yang sebenarnya (dari keluaran tertaut) cocok.

Informasi lainnya

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

Komentar yang paling membantu

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

Semua 3 komentar

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

Apakah halaman ini membantu?
0 / 5 - 0 peringkat