web-dev-qa-db-ja.com

node.jsのスタックトレースでエラーをconsole.logする方法は?

「_Error: Can't set headers after they are sent_」とのみ表示され、トレース情報もコンテキストも表示されないログのエラーの原因を見つけるために、ノードアプリをデバッグしようとしました。

それが起こると、私は今これを修正したと思います... _connect-timeout_ を使用しており、非同期ネットワーク操作に渡されたコールバックの処理を続けていました。ネットワーク操作中に_req.timedout_によって_connect-timeout_が「true」に設定されていたにもかかわらず、res.send()を実行します。

しかし、ログにこのエラーのトレース情報が表示されなかった理由をまだ理解できません。私のコードでエラーが返されるところはどこでも次のようにコンソールに記録します:

_console.log(err);
_

errオブジェクトに利用可能なトレース情報があり、これが_err.stack_に配置されているようであれば、上記のステートメントはwhole contenterr(_err.stack_を含む)をコンソールログに追加しますか?私の理解では、上記を行うことで情報を失うことはありません。に:

_console.log(err.stack);
_

しかし、 this one のような投稿は、そうでないことを示唆しているようです(ただし、リンクされた投稿は更新されています)。

実際にさらに進んで、関連するテキストを追加してエラーを見つけやすくします。

_console.log('error in dodgyFunction:', err);
_

しかし、それにもかかわらず、「_Error: Can't set headers after they are sent_」のみを取得し、コンテキストを設定していませんでした。これは、このコンソールエラーメッセージが外部ライブラリ(expressなど)内に出力されるためでしょうか?外部ライブラリは、メインコードにエラーを返して、それに応じて処理する必要があると思いましたか?

編集:ここでは、非同期操作に渡されるコールバック関数の先頭にエラーとタイムアウトのチェックを配置する例を示します。

_var execFile = require('child_process').execFile;
execFile('dodgycommand', options, function(error, stdout, stderr) {
    if (req.timedout) {
        console.log('timeout detected whilst running dodgycommand, so aborting...');
        return;
    }
    if (error) {
        console.log('error running dodgycommand:', error);
        res.sendStatus(400);
        return;
    }

    // ... it's safe to continue ...

}
_

私は基本的にこの同じパターンに従います。

6
drmrbrewer

私はちょうど何が起こっていたかを解決したばかりで、これが他の人がこの初心者のエラーを回避するのに役立つことを願っています。

エラーログの一部では、次のようなものを使用し、文字列連結を使用してエラーメッセージを作成しました。

console.log('error in function abc: ' + err + ' whilst doing xyz');

一方、他の場所では次のようなものを使用し、エラーメッセージの断片を個別の引数としてconsole.logに渡すだけでした。

console.log('error in function xyz:', err, 'whilst doing abc');

これらの結果が異なることがわかりました!

前者はerrを文字列化し、メッセージの他の部分と連結できるようにし、 this に従って、メッセージ部分のみを使用するようにします。

ただし、後者の形式では、errオブジェクトをconsole.logで処理し、全体としてダンプする必要があります。

これは、私が予想していたように、エラーの内容全体が表示されない場合があった理由と、そうでない場合を説明しています。

他のライブラリによってそこに置かれたコンソールログメッセージについては、ログビューアのログメッセージの「スタック」部分をフィルタリングしていないことを確認する必要があります... Iwas(ログクォータを節約するため... papertrailを使用しています)... d'oh。 ____at(たとえば、4つのスペースの後に 'at')で始まる行をフィルターで除外して、たとえば____at Request.self.callbackのようにしました。

7
drmrbrewer

あなたのパターンは一般的に見えますが、私は原則として私はそれが好きではないと言いますが、それについては後ほど詳しく説明します。

あなたの主な質問に関しては、あなたが提供したものに基づいて答えることは本当に難しいです。 「私は一般的にこのパターンに従います」ではなく、実際のコードを表示すると、役立つ場合があります。しかし、予想外の場所にエラーがスローされていた可能性もあり、そのため_console.log_がまったく呼び出されていませんでした。

あなたはベストプラクティスを探しているようですので、私が今まで見つけた中で最高だと思うものを紹介します。

まず、ロギングに_console.log_を使用しないでください。それは恐ろしいことではありませんが、もっと多くのことができます。私のお気に入りは、リクエスト情報を記録するためのミドルウェアとして morgan を使用し、アプリケーションのログを記録するために debug を使用することです。

debugを使用すると、カスタムログレベルを設定し、必要な粒度のレベルで必要なレベルを聞くことができます。 DEBUG環境変数を設定することですべて制御され、本番環境ではファイルまたは他の目的の場所にリダイレクトできます。さらに、多くのノードモジュール(ExpressおよびConnectを含む)は、内部でロガーとしてデバッグを使用するため、DEBUG変数を調整することで、必要なだけ内部ロギングを確認できます。 veryどこで何がうまくいかなかったかを理解するのに役立ちます。

第二に、私が言ったように、ルーティングに関してはあなたが持っているパターンをまったく使用しません。注意しないと、誤って複数回ヘッダーを送信するのが簡単であることがわかったため、ミドルウェアは常にnext()を返し、応答は実際のハンドラーでのみ送信されます。エラーになると、常にnext(e)を渡し、それをエラーハンドラー関数で処理できます。また、Webステータスコードと一般的なエラーハンドラーに基づいて標準エラーを提供する praeter ライブラリを作成しました。

パターンは次のようになります。

_// middleware function to put something on the request object
app.use((req, res, next) => {
  MyModel.doSomething((e, thing) => {
    if (e) return next(e);
    if (!thing) return next(new NotFound()); // NotFound is an error in praeter that equates to a 404. 
    req.thing = thing;
    return next();
  });
});
_

じゃあ後で

_// log in here is a reference to my debug configured log object
app.use((err, req, res, next) => {
  log.error(err);
  log.error(err.stack);
  return res.status(err.statusCode || 500).send(err.message)
});
_

これは、最終的なエラーハンドラの簡単な例です。アプリケーションのニーズに応じて、異なるエラーコードを異なる方法で処理する可能性のあるこれらのいくつかをしばしば持っています。

5
Paul

n をインストールしました。次を確認できます。

ノード4.0.0

console.log(err)を使用すると、エラーメッセージのみが出力されます。

ノード7.7.0(最新)

console.log(err)を使用すると、エラーメッセージと完全なスタックが出力されます。


バージョン6.0.0でこの動作が変更されたことを確認しました。したがって、古いバージョンを使用する場合は、Node.jsを更新するか、代わりにconsole.log(err.stack)を使用して完全なスタックを印刷することをお勧めします。

3
Zanon