10

私はexpress.jsを使用しています。誰かがメッセージを記録しようとするたびに、特定の要求データを記録できるようにする必要があります。このために、次のようなヘルパーメソッドを作成したいと思います

function log_message(level, message){
  winston.log(level, req.path + "" + message);
}

次に、そのような方法を使用します。

exports.index = function(req, res){
  log_message("info", "I'm here");
}

req オブジェクトを log_message 関数に渡していないことに注意してください。log_message API ユーザーがログに記録されている共通データを意識する必要がないように、これを透過的に行いたいと考えています。

Express.js/node.js でこれを達成する方法はありますか。リクエストオブジェクトは、何らかのグローバル変数から利用できますか?

4

6 に答える 6

11

これを行う興味深い方法は、新しいドメイン機能です。 http://nodejs.org/api/domain.html

ドメインは、優れたエラー回復機能を提供する一方で、「スレッド ローカル ストレージ」の一種として使用できます。つまり、基本的に各リクエストのデータを保存します。

すべてのリクエスト/レスポンスをドメインに追加するミドルウェアを作成します。

app.use(function(req, res, next) {
  var reqd = domain.create();
  reqd.add(req);
  reqd.add(res);
  reqd._req = req; // Add request object to custom property
  // TODO: hook error event on reqd (see docs)
  next();
});

ログ機能で、現在のドメインを取得し、リクエスト オブジェクトを引き出すことができるようになりました。

function log_message(level, message) {
  // Pull the request from the current domain.
  var request = process.domain._req;

  // TODO: log message
};

ドメインはまだ実験段階ですが、今から 1.0 リリースまでの間に大きな変更はないようです。

于 2012-09-25T04:14:12.067 に答える
5

ミドルウェアを作成します。

app.use(function(req, res, next) {
       var tid = uuid.v4();
     var cls = require('continuation-local-storage');
       var namespace = cls.createNamespace('com.storage');
      var pre_ip;
          if(get_ip(req))
          { ip_info= get_ip(req).clientIp;
              pre_ip=ip_info
          }
          namespace.bindEmitter(req);
          namespace.bindEmitter(res);
        namespace.run(function() {
               console.log(logobj);
              namespace.set('tid', tid);
              namespace.set('ip',ip_info);
           namespace.set('logobj',logobj);   
              next();
          });
      });

そしてそれを使用します:

var cls = require('continuation-local-storage');
 var namespace = cls.getNamespace('com.storage');
      namespace.get('ip');
于 2015-03-16T09:10:49.707 に答える
3

log_message呼び出し元 (モジュールなど) にどのように公開され、ルートの前のパイプラインをどのように制御しますか?

このルートの呼び出しの前にミドルウェアを適用log_messageしてクロージャ内から関数を取得するか、reqEventEmitter 機能を利用して winston.log への呼び出しを req.end のハンドラ内にラップし、すべてのメッセージをログに記録することができます。リクエスト中に作成されました。これによりlog_message、ログメッセージのアキュムレータ (おそらく配列内) に効果的に変更され、リクエストの最後にすべてのログが記録されます。

ただし、これはすべて、このものをどのように公開しているかによって異なります。

多くの猫がここで皮を剥がされています:)

于 2012-09-25T03:43:35.373 に答える
2

次の解決策は私に受け入れられます。

ここには、log_messageメソッドをリクエストオブジェクトに追加するミドルウェアがあります。その後、req.log_messageを呼び出してメッセージをログに記録します。これは、すべてのロギング呼び出しにreqオブジェクトを渡すのと非常に似ていますが、少しだけクリーンです。

function logging_middleware(req, res, next){
    req.log_message = function(level, message){
        winston.log(level, req.path + ":" + message);
    }
    next();
}
于 2012-09-25T16:15:43.493 に答える
1

reqオブジェクトを変更せずにリクエスト全体にデータを渡し、すべてのレイヤーに渡す「スレッドセーフ」ソリューションが機能していることがわかりました。

そのため、req の開始時に req をバインドし、次の呼び出しで再利用できるcontinuation-local-storageパッケージがあります。しかし、非同期呼び出しのチェーンを使用しようとすると気分が悪くなります.CLSは「スレッドセーフ」ではなく、少数の同時リクエスト中に呼び出そうとすると失敗し、「コンテキスト」が失われます.

そのため、この問題を修正するcls-hookedパッケージがあり、リクエストの開始時に現在のコンテキスト バインディング リクエストを簡単にキャッチできます。

awilix (依存性注入フレームワーク) を使用する小さな例があり、「RequestContext」クラスを作成し、必要な場所で使用できるようにします

RequestContext クラスを作成しましょう:

module.exports = class RequestContext {

 constructor(req, res, dependencies) {
 const { myService1, myService2 } = dependencies;    
 this.req = req;
 this.res = res;
}

getMyTestHeader() {
 return this.req.headers.testHeader;
}

シンプルな「リクエストラッパー」です。必要に応じてリクエスト、レスポンス、その他の依存関係を消費し、クラス外で使用する getMyTestHeader を提供します。

リクエストの開始時に (他のすべての前に) ミドルウェアを作成しましょう:

  //Registering our namespace. creating it one per app
  const clsNamespace = require('cls-hooked').createNamespace('my-per-request-session'); 
  app.use((req, res, next) => {
      // binding continuation-local-storage (cls-hooked) to request and response
      clsNamespace.bind(req);
      clsNamespace.bind(res);
      clsNamespace.run(() => {
      // save here req and res to use it later in requestContext instance, it will alive during request and could be picked up from DI as other dependencies
       clsNamespace.set('req', req);
       clsNamespace.set('res', res);
       next();
      });

  });

それでは、 awilix を使用して DI 呼び出しを登録しましょう。

container.register({  
requestContextProvider: asFunction(dependencies => ({
 getCurrentContext: () => {
  //retrieve res and req on each DI call "from current request"
  //also, clsNamespace should be the same as we registered before, on original code it is also registered using DI container.
  const req = dependencies.clsNamespace.get('req');
  const res = dependencies.clsNamespace.get('res');
  //and return our RequestContext (wrapper for req and res)
  return new RequestContext(req, res, dependencies);
 },
})),

そのため、requestContextProvider を関数として登録しています。これは、各 DI 依存関係呼び出しの各要求で、外部コンテキスト (cls) からの req および res でインスタンス化されます。その結果、次の方法で使用できます(たとえば、コントローラーで):

module.exports = (dependencies) => ({
 myControllerAction: async (req, res) => {
  const {requestContextProvider} = dependencies;
  const requestContext = requestContextProvider.getCurrentContext();
  //it will return header from req!
  const myHeader = requestContext.getMyTestHeader(); 
  res.status(200).json({ myHeader });
 },    
});

ご覧のとおり、すべてのレイヤー レベル (コントローラー/BLL/DAL/ヘルパーなど) で DI にアクセスできるすべての場所に "requestContext" があります。したがって、「スレッドセーフ」であり、テストが容易であり、すべての「中間」レイヤーを介して req オブジェクトをスローする必要はありません。

同意します。最良で簡単な例ではありませんが、誰かの役に立てば幸いです。

于 2020-03-20T23:53:06.057 に答える