👀

[Linux] SystemdのUnit定義ファイルで日時入りのログ出力を実装する方法

2024/05/21に公開

はじめに

Raspberry Pi内に入っているsystemdを使って、Raspberry Piを起動したらスクリプトを自動起動させるという設定を行う機会がありました。

当初はshファイルをただ実行するコマンドだけを設定していましたが、このshファイルのログを何時でも見られるようにする必要がでてきたため、ログ出力を実装することになりました。

今回は、Unit定義ファイル内でログ出力を実装する方法をまとめていきます。

環境

  • Raspberry Pi 4 Model B
    • OS: Raspbian 32bit(64bit版でも実装可能です)
  • Python 3.11.6

実装

事前準備

serviceで使用するshファイルを作成しておきます。
今回は~/test_serverディレクトリに置かれているソースコードを用いて、Pythonの仮想環境(venv)を立ち上げてFastAPIを立ち上げるという前提でshファイルを作成します(FastAPIの実装方法はこちらの記事では紹介しません)

start_test_server.sh
cd ~/test_server
source .venv/bin/activate
uvicorn main:app --host=0.0.0.0 --port=8000

また、Unit定義ファイルからshファイルを実行できるように実行権限を以下のコマンドでつけておきます。

sudo chmod +x start_test_server.sh

最後に、ログを保管しておくためのディレクトリを作成します。

mkdir -p ~/logs/test_server/

serviceファイルを作成

/etc/systemd/service/ディレクトリにtest.serviceというUnit定義ファイルを作成します。

test.service
[Unit]
Description=Start up Server.
After=network.target
[Service]
WorkingDirectory=/home/USERNAME/Desktop/test_server/
ExecStart=/bin/bash -c 'stdbuf -oL -eL /home/USERNAME/Desktop/test_server/start_test_server.sh 2>&1 | while IFS= read -r line; do echo "$(date "+%%Y-%%m-%%d %%H:%%M:%%S.%%3N") $line"; done >> /home/USERNAME/logs/test_server/test_server.log'
User=USERNAME
[Install]
WantedBy=multi-user.target

※USERNAMEは適宜書き換えてください。

ExectStartで設定しているコマンドを要素ごとに分けると以下のようになります。

  1. /bin/bash -c : bashシェルを起動し、次に続くコマンドを実行します。
  2. stdbuf -oL -eL /home/USERNAME/Desktop/test_server/start_test_server.sh : start_test_server.shスクリプトを実行し、その標準出力と標準エラー出力をリアルタイムで処理します。
  3. 2>&1 : 標準エラー出力を標準出力にリダイレクトします。
  4. while IFS= read -r line; do echo "$(date "+%%Y-%%m-%%d %%H:%%M:%%S.%%3N") $line"; done : start_test_server.shからの出力を1行ずつ処理し、時刻とともにログファイルに書き込むための処理を行います。(serviceファイルに#
  5. >> /home/USERNAME/logs/test_server/test_server.log : 処理したログをtest_server.logファイルに追記します。

Unit定義ファイルでログの保管場所を指定したため、ディレクトリを作成します。

mkdir -p ~/logs/test_server/

Unit定義ファイルが新たに追加されたためdaemon-reloadを実行します。

sudo systemctl daemon-reload

サーバーを起動します。

sudo systemctl start test.service

test.serviceが起動しているか確認します。

sudo systemctl status test.service

スクリプトを自動起動したいため、enableを指定して自動起動を設定します

sudo systemctl enable test.service

ログファイルの中身を確認すると、以下のようにログの前に日時が登録されていることを確認できます。

test_server.log
2024-05-21 17:26:02.789 INFO:     Started server process [705]
2024-05-21 17:26:02.793 INFO:     Waiting for application startup.
2024-05-21 17:26:02.797 INFO:     Application startup complete.
2024-05-21 17:26:02.800 INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)

ログローテーションを設定

このままでも良いのですが、ログが溜まりすぎてしまうとストレージの容量を圧迫してしまうため、ログローテーションも設定しておきます。

/etc/logrotate.d/logrotate_test_serverを作成します。

sudo nano /etc/logrotate.d/logrotate_test_server

logrotate_test_serverの中身を以下のように書き換えます

logrotate_test_server
/home/USERNAME/logs/test_server/test_server.log {
	daily
	rotate 7
	copytruncate
	dateext 
	dateformat -%Y%m%d%H%s
	notifempty
	compress
	missingok
}

以下、ログローテーションの中身は以下の通りです。

  • ローテーション対象: 1行目に書かれているログファイル
  • daily: 毎日実行する
  • rotate: 7つ(7日分)のローテーションファイルを保持する
  • copytruncate: コピーを作成した後に元のログファイルを空にする
  • dateext: 旧バージョンのログファイルに、YYYYMMDD のように日付を付加してアーカイブする
  • dateformat -%Y%m%d%H%s: ローテーションファイルに付けられる日付のフォーマットを指定する
  • notifempty: ログファイルが空の場合、ローテーションを行わない
  • compress: 旧バージョンのログファイルがgzipで圧縮される
  • missingok: ログファイルが存在しない場合でも、エラーメッセージを出力せずに次の対象のファイルへ移る(ログファイルがなくても処理を止めない)

例えば5/9~5/16の8日間、サーバーが立ち上がりっぱなしで何かしらのログを一日必ず出力する場合、以下のようにログファイルが保存されていきます。ログローテーションが実行されると、test_server.logのコピーが作成され、ログローテーションされる前まで使われていたtest_server.logはgzipで圧縮されます。7バージョン前までローテーションファイルは保管され、8バージョンより前のものは削除されるようになっています。また、各ローテーションファイルには圧縮された日時が記載されています。

$ ls ~/logs/test_server/ -la

合計 40
drwxr-xr-x 2 pi pi 4096  514 12:10 .
drwxr-xr-x 4 pi pi 4096  59 11:13 ..
-rw-r--r-- 1 pi pi 2310  516 11:33 test_server.log
-rw-r--r-- 1 pi pi   20  59 13:38 test_server.log-20240510001715315923.gz
-rw-r--r-- 1 pi pi   20  510 13:38 test_server.log-20240511001715315935.gz
-rw-r--r-- 1 pi pi   20  511 13:38 test_server.log-20240512001715315942.gz
-rw-r--r-- 1 pi pi  425  512 14:37 test_server.log-20240513001715322207.gz
-rw-r--r-- 1 pi pi  114  513 15:24 test_server.log-20240514001715322276.gz
-rw-r--r-- 1 pi pi  815  514 18:00 test_server.log-20240515001715556165.gz
-rw-r--r-- 1 pi pi  559  515 19:32 test_server.log-20240516001715612419.gz

最後に

今回はSystemdのUnit定義ファイルで日時入りのログ出力を実装する方法について説明しました。ログ出力の方法は様々ありますが、Raspberry Pi起動時に自動実行するアプリやサーバーのログを保存しておく1つの手法として参考になるかと思います。

参考記事

systemdを使ってスクリプト自動起動
systemdの*.serviceファイルの書き方 #Linux - Qiita
【日本語man】logrotateの全オプション解説 – Hacker's High

ヘッドウォータース

Discussion