前書き
効果的なロギングソリューションは、アプリケーションの成功にとって重要です。 このガイドでは、Winstonと呼ばれるロギングパッケージに焦点を当てます。これは、非常に用途の広いロギングライブラリであり、NPMダウンロード統計に基づいてNode.jsアプリケーションで利用できる最も一般的なロギングソリューションです。 Winstonの機能には、複数のストレージオプションとログレベル、ログクエリ、さらには組み込みのプロファイラのサポートが含まれます。 このチュートリアルでは、Winstonを使用して、このプロセスの一部として作成するNode / http://expressjs.com/ [Express]アプリケーションをログに記録する方法を示します。 また、WinstonをMorganと呼ばれるNode.js用の別の人気のあるHTTPリクエストミドルウェアロガーと組み合わせて、HTTPリクエストデータログを他の情報と統合する方法についても見ていきます。
このチュートリアルを完了すると、Ubuntuサーバーで小さなNode / Expressアプリケーションが実行されます。 また、Winstonを実装して、エラーとメッセージをファイルとコンソールに記録します。
前提条件
このガイドを始める前に、次のものが必要です。
-
sudo非rootユーザーとファイアウォールを含むthe Ubuntu 16.04 initial server setup guideをフォローしてセットアップされた1つのUbuntu16.04サーバー。
-
Node.js installed using the official PPA, as explained in How To Install Node.js on Ubuntu 16.04.
これらの前提条件が整っていれば、アプリケーションをビルドしてWinstonをインストールできます。
[[step-1 -—- creating-a-basic-node-express-app]] ==ステップ1—基本的なノード/ Expressアプリの作成
Winstonの一般的な使用法は、Node.jsで構築されたWebアプリケーションからのイベントを記録することです。 Winstonの組み込み方法を完全に示すために、Expressフレームワークを使用して簡単なNode.js Webアプリケーションを作成します。 基本的なWebアプリケーションを実行するために、Node / Express Webアプリケーションをすばやく実行するためのコマンドラインツールであるexpress-generator
を使用します。 前提条件の一部としてNode Package Managerをインストールしたため、npm
コマンドを使用してexpress-generator
をインストールできます。 また、-g
フラグを使用します。これは、パッケージをグローバルにインストールして、既存のノードプロジェクト/モジュールの外部でコマンドラインツールとして使用できるようにします。 次のコマンドでパッケージをインストールします。
sudo npm install express-generator -g
express-generator
がインストールされている場合、express
コマンドを使用してアプリを作成し、その後にプロジェクトに使用するディレクトリの名前を続けることができます。 これにより、開始するために必要なすべてのアプリケーションが作成されます。
express myApp
次に、Nodemonをインストールします。これにより、変更を加えるたびにアプリケーションが自動的に再読み込みされます。 Node.jsアプリケーションは、それらの変更を有効にするために、ソースコードに変更が加えられるたびに再起動する必要があります。 Nodemonは自動的に変更を監視し、アプリケーションを再起動します。 また、nodemon
をコマンドラインツールとして使用できるようにしたいので、-g
フラグを付けてインストールします。
sudo npm install nodemon -g
アプリケーションのセットアップを完了するには、アプリケーションディレクトリに移動し、次のように依存関係をインストールします。
cd myApp
npm install
デフォルトでは、express-generator
で作成されたアプリケーションはポート3000で実行されるため、ポートがファイアウォールによってブロックされていないことを確認する必要があります。 ポート3000を開くには、次のコマンドを実行します。
sudo ufw allow 3000
これで、Webアプリケーションを起動するために必要なすべてが揃いました。 これを行うには、次のコマンドを実行します。
nodemon bin/www
これにより、ポート3000で実行されているアプリケーションが開始されます。 Webブラウザでhttp://your_server_ip:3000
に移動すると、機能していることをテストできます。 このようなものが見えるはずです。
この時点で、サーバーへの2番目のSSHセッションを開始してこのチュートリアルの残りの部分で使用し、元のセッションで実行を開始したWebアプリケーションをそのままにしておくことをお勧めします。 この記事の残りの部分では、これまで使用してきたSSHセッションを参照し、現在セッションAとしてアプリケーションを実行しています。 コマンドの実行とファイルの編集に新しいSSHセッションを使用します。このセッションをセッションBと呼びます。 特に注記がない限り、残りのコマンドはすべてセッションBで実行する必要があります。
ステップ2-Node.jsアプリケーションのカスタマイズ
express-generator
によって作成されたデフォルトのアプリケーションは、開始時に優れた役割を果たします。また、すべてのHTTPリクエストに関するデータをログに記録するために使用するMorganHTTPロギングミドルウェアも含まれています。 また、Morganは出力ストリームをサポートしているため、Winstonに組み込まれているストリームサポートとうまく組み合わせて、HTTPリクエストデータログをWinstonで記録することを選択した他のものと統合できます。
デフォルトでは、express-generator
ボイラープレートは、morgan
パッケージを参照するときに変数loggerを使用します。 どちらもロギングパッケージであるmorgan
とwinston
を使用するため、どちらか一方をloggerと呼ぶと混乱する可能性があります。 それでは、プロジェクトのルートにあるapp.js
ファイルを編集し、いくつかの変更を加えて、これを変更しましょう。
編集のためにapp.js
を開くには、nano
コマンドを使用します。
nano ~/myApp/app.js
ファイルの上部近くにある次の行を見つけます。
~/myApp/app.js
...
var logger = require('morgan');
...
次のように変更してください。
~/myApp/app.js
...
var morgan = require('morgan');
...
また、変数loggerがファイル内のどこで参照されているかを見つけて、それをmorgan
に変更する必要があります。 その間に、morgan
パッケージで使用されるログ形式をcombined
に変更しましょう。これは標準のApacheログ形式であり、リモートIPアドレスやユーザーなどの有用な情報がログに含まれます。 -エージェントHTTPリクエストヘッダー。
これを行うには、次の行を見つけます。
~/myApp/app.js
...
app.use(logger('dev'));
...
次のように変更してください。
~/myApp/app.js
...
app.use(morgan('combined'));
...
これらの変更は、Winston構成を統合した後、いつでも参照しているロギングパッケージをよりよく理解するのに役立ちます。
終了し、CTRL-X
、Y
、ENTER
の順に入力してファイルを保存します。
これでアプリがセットアップされたので、Winstonで作業を開始する準備が整いました。
[[step-3 -—- installing-and-configuring-winston]] ==ステップ3—Winstonのインストールと構成
これで、Winstonをインストールして構成する準備ができました。 このステップでは、winston
パッケージの一部として使用できるいくつかの構成オプションを調べ、情報をファイルとコンソールに記録するロガーを作成します。
winston
をインストールするには、次のコマンドを実行します。
cd ~/myApp
npm install winston
アプリケーションのサポートまたはユーティリティ構成ファイルを特別なディレクトリに保存すると便利なことが多いので、winston
構成を含むconfig
フォルダーを作成しましょう。
mkdir ~/myApp/config
次に、winston
構成を含むファイルを作成しましょう。これをwinston.js
と呼びます。
touch ~/myApp/config/winston.js
次に、ログファイルを含むフォルダーを作成します。
mkdir ~/myApp/logs
最後に、Node.jsでパスを指定するときに役立つパッケージであるapp-root-path
をインストールしましょう。 このパッケージはWinstonに直接関連していませんが、Node.jsコードでファイルへのパスを指定する際に非常に役立ちます。 これを使用して、プロジェクトのルートからWinstonログファイルの場所を指定し、andい相対パス構文を回避します。
npm install app-root-path --save
ロギングの処理方法を構成するために必要なものはすべて揃っているため、構成設定の定義に進むことができます。 編集のために~/myApp/config/winston.js
を開くことから始めます。
nano ~/myApp/config/winston.js
次に、app-root-path
およびwinston
パッケージが必要です。
~/myApp/config/winston.js
var appRoot = require('app-root-path');
var winston = require('winston');
これらの変数を配置すると、transportsの構成設定を定義できます。 トランスポートは、ログに使用されるストレージ/出力メカニズムを参照するWinstonによって導入された概念です。 Winstonには、console、file、およびHTTPの3つのコアトランスポートが付属しています。 このチュートリアルでは、コンソールとファイルトランスポートに焦点を当てます。コンソールトランスポートはコンソールに情報を記録し、ファイルトランスポートは指定されたファイルに情報を記録します。 各トランスポート定義には、ファイルサイズ、ログレベル、ログ形式などの独自の構成設定を含めることができます。 各トランスポートで使用する設定の概要は次のとおりです。
-
level-ログに記録するメッセージのレベル。
-
filename-ログデータの書き込みに使用されるファイル。
-
handleExceptions-未処理の例外をキャッチしてログに記録します。
-
json-ログデータをJSON形式で記録します。
-
maxsize-新しいファイルが作成される前のログファイルの最大サイズ(バイト単位)。
-
maxFiles-ログファイルのサイズを超えたときに作成されるファイルの数を制限します。
-
colorize-出力に色を付けます。 これは、コンソールログを見るときに役立ちます。
Logging levelsはメッセージの優先度を示し、整数で示されます。 Winstonは、0から5(最高から最低)に優先順位が付けられたnpm
のログレベルを使用します。
-
0:エラー
-
1:警告
-
2:情報
-
3:詳細
-
4:デバッグ
-
5:ばかげている
特定のトランスポートのログレベルを指定すると、そのレベル以上のログが記録されます。 たとえば、info
のレベルを指定すると、レベルerror
、warn
、またはinfo
のすべてがログに記録されます。 ログレベルはロガーを呼び出すときに指定されます。つまり、エラーを記録するために次の操作を実行できます:logger.error('test error message')
。
winston
構成のfile
およびconsole
トランスポートの構成設定は、次のように定義できます。
~/myApp/config/winston.js
...
var options = {
file: {
level: 'info',
filename: `${appRoot}/logs/app.log`,
handleExceptions: true,
json: true,
maxsize: 5242880, // 5MB
maxFiles: 5,
colorize: false,
},
console: {
level: 'debug',
handleExceptions: true,
json: false,
colorize: true,
},
};
次に、options
変数で定義されたプロパティを使用して、ファイルおよびコンソールトランスポートで新しいwinston
ロガーをインスタンス化します。
~/myApp/config/winston.js
...
var logger = new winston.Logger({
transports: [
new winston.transports.File(options.file),
new winston.transports.Console(options.console)
],
exitOnError: false, // do not exit on handled exceptions
});
デフォルトでは、morgan
はコンソールにのみ出力するので、morgan
で生成された出力をwinston
ログファイルに取り込むことができるストリーム関数を定義しましょう。 info
レベルを使用して、出力が両方のトランスポート(ファイルとコンソール)によって取得されるようにします。
~/myApp/config/winston.js
...
logger.stream = {
write: function(message, encoding) {
logger.info(message);
},
};
最後に、アプリケーションの他の部分で使用できるようにロガーをエクスポートします。
~/myApp/config/winston.js
...
module.exports = logger;
完成したwinston
構成ファイルは次のようになります。
~/myApp/config/winston.js
var appRoot = require('app-root-path');
var winston = require('winston');
// define the custom settings for each transport (file, console)
var options = {
file: {
level: 'info',
filename: `${appRoot}/logs/app.log`,
handleExceptions: true,
json: true,
maxsize: 5242880, // 5MB
maxFiles: 5,
colorize: false,
},
console: {
level: 'debug',
handleExceptions: true,
json: false,
colorize: true,
},
};
// instantiate a new Winston Logger with the settings defined above
var logger = new winston.Logger({
transports: [
new winston.transports.File(options.file),
new winston.transports.Console(options.console)
],
exitOnError: false, // do not exit on handled exceptions
});
// create a stream object with a 'write' function that will be used by `morgan`
logger.stream = {
write: function(message, encoding) {
// use the 'info' log level so the output will be picked up by both transports (file and console)
logger.info(message);
},
};
module.exports = logger;
ファイルを終了して保存します。
ロガーを構成しましたが、アプリケーションはまだロガーまたはその使用方法を認識していません。 ロガーをアプリケーションに統合します。
[[step-4 -—- integrating-winston-with-our-application]] ==ステップ4—Winstonとアプリケーションの統合
ロガーをアプリケーションで動作させるには、express
にそれを認識させる必要があります。 ステップ2で、express
構成がapp.js
にあることをすでに確認したので、ロガーをこのファイルにインポートしましょう。 以下を実行して、編集するファイルを開きます。
nano ~/myApp/app.js
ファイルの先頭近くにあるwinston
を、他のrequireステートメントとともにインポートします。
~/myApp/app.js
...
var winston = require('./config/winston');
...
実際にwinston
を使用する最初の場所は、morgan
です。 stream
オプションを使用し、winston
構成の一部として作成したストリームインターフェイスに設定します。 これを行うには、次の行を見つけます。
~/myApp/app.js
...
app.use(morgan('combined'));
...
これに変更してください。
~/myApp/app.js
...
app.use(morgan('combined', { stream: winston.stream }));
...
ファイルを終了して保存します。
ログデータを表示する準備ができました! Webブラウザーでページをリロードすると、SSHセッションAのコンソールに次のようなものが表示されます。
Output[nodemon] restarting due to changes...
[nodemon] starting `node bin/www`
info: ::ffff:72.80.124.207 - - [07/Mar/2018:17:29:36 +0000] "GET / HTTP/1.1" 304 - "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36"
info: ::ffff:72.80.124.207 - - [07/Mar/2018:17:29:37 +0000] "GET /stylesheets/style.css HTTP/1.1" 304 - "http://167.99.4.120:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36"
ここには2つのログエントリがあります。1つ目はHTMLページへのリクエスト用で、2つ目は付随するスタイルシート用です。 各トランスポートはinfo
レベルのログデータを処理するように構成されているため、~/myApp/logs/app.log
にあるファイルトランスポートにも同様の情報が表示されます。 ただし、ファイルトランスポート構成でjson: true
を指定したため、ファイルトランスポートの出力はJSONオブジェクトとして記述する必要があります。 JSONについて詳しくは、introduction to JSON tutorialをご覧ください。 ログファイルの内容を表示するには、次のコマンドを実行します。
tail ~/myApp/logs/app.log
次のようなものが表示されるはずです。
{"level":"info","message":"::ffff:72.80.124.207 - - [07/Mar/2018:17:29:36 +0000] \"GET / HTTP/1.1\" 304 - \"-\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36\"\n","timestamp":"2018-03-07T17:29:36.962Z"}
{"level":"info","message":"::ffff:72.80.124.207 - - [07/Mar/2018:17:29:37 +0000] \"GET /stylesheets/style.css HTTP/1.1\" 304 - \"http://167.99.4.120:3000/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36\"\n","timestamp":"2018-03-07T17:29:37.067Z"}
これまでのところ、ロガーはHTTPリクエストと関連データのみを記録しています。 これはログに記録する非常に重要な情報ですが、カスタムログメッセージをどのように記録しますか? たとえば、エラーの記録やデータベースクエリパフォーマンスのプロファイリングなどにこの機能が必要な場合があります。 これを行う方法を説明するために、エラーハンドラールートからロガーを呼び出しましょう。
express-generator
パッケージには、デフォルトで404および500エラーハンドラールートが含まれているため、これを使用します。 ~/myApp/app.js
ファイルを開きます。
nano ~/myApp/app.js
ファイルの下部で次のようなコードブロックを見つけます。
~/myApp/app.js
...
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
...
これは最終的にエラー応答をクライアントに送り返す最終的なエラー処理ルートです。 すべてのサーバー側エラーはこのルートを介して実行されるため、これはwinston
ロガーを含めるのに適した場所です。
現在エラーを処理しているので、error
ログレベルを使用します。 繰り返しますが、両方のトランスポートはerror
レベルのメッセージをログに記録するように構成されているため、コンソールとファイルログに出力が表示されます。 必要なものはすべてログに含めることができるため、次のような役立つ情報を必ず含めてください。
-
err.status-HTTPエラーステータスコード。 まだ存在しない場合は、デフォルトで500になります。
-
err.message-エラーの詳細。
-
req.originalUrl-要求されたURL。
-
req.path-リクエストURLのパス部分。
-
req.method-リクエストのHTTPメソッド(GET、POST、PUTなど)。
-
req.ip-リクエストのリモートIPアドレス。
エラーハンドラールートを更新して、次と一致するようにします。
~/myApp/app.js
...
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// add this line to include winston logging
winston.error(`${err.status || 500} - ${err.message} - ${req.originalUrl} - ${req.method} - ${req.ip}`);
// render the error page
res.status(err.status || 500);
res.render('error');
});
...
ファイルを終了して保存します。
これをテストするために、プロジェクト内の存在しないページにアクセスしてみてください。404エラーがスローされます。 Webブラウザーに戻り、次のURLをロードしてみます:http://your_server_ip:3000/foo
。 express-generator
によって作成されたボイラープレートのおかげで、アプリケーションはこのようなエラーに応答するようにすでに設定されています。 ブラウザには、次のようなエラーメッセージが表示されます(エラーメッセージは、表示されているものよりも詳細な場合があります)。
次に、SSHセッションAのコンソールをもう一度見てみましょう。 エラーのログエントリが必要です。色付け設定のおかげで、簡単に見つけることができます。
Output[nodemon] starting `node bin/www`
error: 404 - Not Found - /foo - GET - ::ffff:72.80.124.207
info: ::ffff:72.80.124.207 - - [07/Mar/2018:17:40:11 +0000] "GET /foo HTTP/1.1" 404 985 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36"
info: ::ffff:72.80.124.207 - - [07/Mar/2018:17:40:11 +0000] "GET /stylesheets/style.css HTTP/1.1" 304 - "http://167.99.4.120:3000/foo" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36"
ファイルロガーに関しては、tail
コマンドを再度実行すると、新しいログレコードが表示されます。
tail ~/myApp/logs/app.log
次のようなメッセージが表示されます。
{"level":"error","message":"404 - Not Found - /foo - GET - ::ffff:72.80.124.207","timestamp":"2018-03-07T17:40:10.622Z"}
エラーメッセージには、エラーステータス(404-見つかりません)、要求されたURL(localhost / foo)、要求メソッド(GET)など、エラーハンドラーの一部としてログに記録するようにwinston
に特に指示したすべてのデータが含まれます。 、リクエストを行ったIPアドレス、およびリクエストが行われたときのタイムスタンプ。
結論
このチュートリアルでは、単純なNode.js Webアプリケーションを構築し、アプリケーションのパフォーマンスに関する洞察を提供する効果的なツールとして機能するWinstonロギングソリューションを統合しました。 特にニーズが複雑になるにつれて、アプリケーションの堅牢なログソリューションを構築するためにさらに多くのことができます。 時間をかけて、これらの他のドキュメントのいくつかを確認することをお勧めします。
-
Winstonトランスポートの詳細については、Winston Transports Documentationを参照してください。
-
独自のトランスポートの作成の詳細については、Adding Custom Transportsを参照してください。
-
HTTPコアトランスポートで使用するHTTPエンドポイントを作成するには、
winstond
を参照してください。 -
Winstonをプロファイリングツールとして使用するには、Profilingを参照してください。