EC2上のアプリケーション(Node.js)のログをpinoを使って、CloudWatch Logsに出力してみた
こんにちは、ゲームソリューション部のsoraです。
今回は、EC2上のアプリケーション(Node.js)のログをpinoを使って、CloudWatch Logsに出力してみたことについて書いていきます。
CloudWatch AgentとNode.jsのインストール
EC2にCloudWatch AgentとNode.jsをインストールします。
今回はユーザデータを使ってインストールします。
AMIはAmazon Linux 2023を使用します。
CloudWatch Agentの設定として、システムログとアプリケーションログの2つを出力するように設定しています。
#!/bin/bash
# Update the package list and install necessary packages
dnf update -y
# タイムゾーンの設定
timedatectl set-timezone Asia/Tokyo
# rsyslogのインストール(システムログがjournalで保存され文字化けするため)
dnf install -y rsyslog
systemctl enable rsyslog
systemctl start rsyslog
# CloudWatch Agentのインストール
dnf install -y amazon-cloudwatch-agent
# Create the CloudWatch Agent configuration file for logging
bash -c 'cat <<EOL > /opt/aws/amazon-cloudwatch-agent/bin/config.json
{
"logs": {
"logs_collected": {
"files": {
"collect_list": [
{
"file_path": "/var/log/messages",
"log_group_name": "/api-server-system",
"log_stream_name": "{instance_id}",
"retention_in_days": 30
},
{
"file_path": "/var/log/api-app/*.log",
"log_group_name": "/api-server-app",
"log_stream_name": "{instance_id}",
"retention_in_days": 30
}
]
}
}
}
}
EOL'
# Start the CloudWatch Agent
/opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -c file:/opt/aws/amazon-cloudwatch-agent/bin/config.json -s
# デフォルトでenableだが念のため明示的にenableしておく
systemctl enable amazon-cloudwatch-agent
# nodejsのインストール
dnf update -y
dnf install -y gcc-c++ make
# マルチパート形式
Content-Type: multipart/mixed; boundary="//"
MIME-Version: 1.0
--//
Content-Type: text/cloud-config; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="cloud-config.txt"
#cloud-config
cloud-init directives
--//
Content-Type: text/x-shellscript; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="userdata.txt"
#!/bin/bash
# nodejsのインストール
if ! su - ec2-user -c "command -v node" > /dev/null 2>&1; then
su - ec2-user -c "curl -sL https://rpm.nodesource.com/setup_20.x | sudo -E bash -"
fi
--//--
dnf install -y nodejs
# ディレクトリ作成
mkdir /home/ec2-user/myexpressapi
chown -R ec2-user:ec2-user /home/ec2-user/myexpressapi
EC2インスタンスのロール変更
EC2からCloudWatch Logsに出力するため、以下ポリシーをアタッチしたロールをEC2で使用します。
今回はメトリクスも取得しています。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"cloudwatch:PutMetricData",
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogStreams",
"logs:DescribeLogGroups"
],
"Resource": "*"
}
]
}
また、今回はパブリックサブネットに配置していますが、プライベートサブネットに配置してインターネットに出るルートがない場合は、以下エンドポイントも必要です。
- com.amazonaws.{region}.logs
- com.amazonaws.{region}.monitoring
- com.amazonaws.{region}.ec2
プロジェクト初期化・パッケージのインストール
プロジェクトを初期化して、必要なパッケージをインストールします。
cd myexpressapi
# プロジェクトの初期化
npm init
# entry pointだけ入力しました。
package name: (myexpressapi)
version: (1.0.0)
description:
entry point: (index.js) index.mjs
test command:
git repository:
keywords:
author:
license: (ISC)
# Expressとpinoのインストール
npm install express pino
ソースコード
ソースコードは以下です。
ログ出力のためのパッケージであるpinoを使って、/var/log/api-app/app.log
に出力しています。
import express from 'express';
import fs from 'fs';
import pino from 'pino';
// ログファイルを指定
const logFile = fs.createWriteStream('/var/log/api-app/app.log', { flags: 'a' });
const logger = pino(logFile);
const app = express();
app.get('/api', (req, res) => {
logger.info({
method: req.method,
url: req.url,
headers: req.headers
}, 'GET /api called');
res.json({ message: 'Hello, World!' });
});
app.use((req, res) => {
logger.warn({
method: req.method,
url: req.url,
headers: req.headers
}, `404 Not Found: ${req.originalUrl}`);
res.status(404).json({ message: 'Not Found' });
});
app.listen(80, () => {
logger.info('Server is running on port 80');
});
サーバの起動・動作確認
最後にコードを実行して、APIサーバを起動します。
sudo node index.mjs
実行後にHTTP接続してみると、CloudWatch Logsに以下のようにログが出力できていました。
参考
最後に
今回は、EC2上のアプリケーション(Node.js)のログをpinoを使って、CloudWatch Logsに出力してみたことを記事にしました。
どなたかの参考になると幸いです。