Winston: リクエスト:デフォルトのメタデータの関数

作成日 2019年03月29日  ·  3コメント  ·  ソース: winstonjs/winston

ここでの根本的な問題はやや複雑なので、ここで私たちが求めているものを要約します-関数をフィールドとして提供することにより、動的に生成された値をdefaultMetadataに含める機能を確認したいと思います。 例えば:

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

完全な正当化については、以下を参照してください。

あなたの環境について教えてください:

  • _ winstonバージョン?_

    • [] winston@2

    • [x] winston@3

  • _ node -v出力:_ v10.15.2
  • _オペレーティングシステム?_macOS(無関係)
  • _Language?_ ES7(無関係)

何が問題ですか?

WinstonのStackdriverLogging Transportの作成者として、ログとリクエストの相関関係をサポートしたいと考えています。 async_hooksモジュールを使用すると、リクエストIDなどのリクエスト固有の情報を保存できます。 ただし、 Transport側で確実に取得する手段はありません。

問題は例によって最もよく説明されます。 次のコードは、最大100のリクエストがすばやく連続して行われると、この出力を生成します。

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

「予期された」および「実際の」リクエストIDのミスマッチに注意してください。 私たちがMyTransportの作成者である場合、現在の実行ID(「実際の」)を調べて正しい要求IDを正確に取得する方法はありません。 代わりに、メタデータ(「期待される」)として正しいリクエストIDを渡すユーザーに依存する必要があります。 これは、トランスポートがlogコールバックの呼び出しを延期すると、Winston 3が( readable-streamを介して)ログをバッチ処理するためです。

問題は、ユーザーがリクエストIDを渡してくれることに依存したくないということです。 自動的に発生させたいのです。 結局のところ、ユーザーはおそらくただ書きたいと思うでしょう

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

そして私たちに休むために残します。

代わりに何が起こると思いますか?

(リンクされた出力からの)期待される要求IDと実際の要求IDは一致します。

その他の情報

実際のリクエストIDが一致するようにコードを修正することは不可能であるため、この問題の解決策は、ユーザーがロガーが呼び出されたときに呼び出されるdefaultMetadataのフィールドとして関数を指定できるようにすることです。 そうすれば、コードはこれに変更されます。これにより、ユーザーはリクエストIDを意識せずにコードを記述できますが、サンクをdefaultMetadataフィールドとして提供するという小さな注意が必要です(モジュールが提供する可能性があります)。それらを使用する)。

最も参考になるコメント

提案されたソリューションが実装されるまで使用できる回避策を見つけました-ゲッターを使用してください:

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

UPD:@mooskiが言及したバグを修正しました

全てのコメント3件

したがって、この問題の解決策は、ユーザーがdefaultMetadataのフィールドとして関数を指定できるようにすることです。

これは、 reduxthunksと同じように実装できると思っていました。 defaultMetadataに関数値のプロパティを設定する代わりに、 defaultMetadata自体をオプションで関数にできると考えていました。 サンクのように、 winstonは、そのような場合にdefaultMetadataを呼び出すことによって返される値を使用します。 言い換えると:

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

提案されたソリューションが実装されるまで使用できる回避策を見つけました-ゲッターを使用してください:

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

UPD:@mooskiが言及したバグを修正しました

〜優れたソリューション@ Alexsey-このコードを使用している人は、 timeWrittenの後のコロンがないことに注意してください。〜

更新:@Alexseyによって修正されました

このページは役に立ちましたか?
0 / 5 - 0 評価