Phoenixでアクセスログとエラーログを出力する~本番編~

三部作に分かれる記事となりました。
今回で完結です！

前回、前々回の記事はこちら↓

https://qiita.com/mito_mito/items/b5c34311455773d91533

https://qiita.com/mito_mito/items/e05489932b8a95904ded

要件のおさらいから始めます。

# 要件
- アクセスログとエラーログをそれぞれ別ファイルで保管したい
- 1日1ファイルで日付が変わると新しいファイルにログが書き込まれるようにしたい
- 元々書き込まれていたログは新しいファイルに書き込まれたタイミングで消える
- ファイル名はファイルが作られた時の日付
- ログのローテーション(保存期間)は1週間
- ログファイルができたタイミングでslack通知(新しい要件)

前回まででローカルのDocker上で指定した時間にローテーションできることまで確認できました。

今回は本番環境サーバーに設定をしていきます。
ローカルと本番で若干ユーザーが異なったりしたので備忘録として書きます。


# 本番サーバーでのファイル設定

基本的にローカルと同じです。以下3つのファイルを追加します。
Linuxの操作コマンドがわからないときはこちらをどうそ↓

https://qiita.com/mito_mito/items/0405aa984852f3f31f31

## 1. `etc/cron.d`に`sample-project`ファイルを作成

```bash
# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file
# and files in /etc/cron.d. These files also have username fields,
# that none of the other crontabs do.

SHELL=/bin/sh
# PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# Example of job definition:
# .---------------- minute (0 - 59)
# |  .------------- hour (0 - 23)
# |  |  .---------- day of month (1 - 31)
# |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
# |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# |  |  |  |  |
# *  *  *  *  * user-name command to be executed
0 4  *  *  * root /usr/local/bin/get_daily_log.sh
```

本番ではslack通知するという要件も追加されたので、ローテーションの実行とslackの通知をまとめてシェルスクリプトにしました。毎日4時に`get_daily_log.sh`が実行されます。

## ２.`etc/logrotate.d`に`sample-project`を追加

ローテーションの設定ファイルです。
ローカルではユーザーを`devel-server`にしていましたが、本番では`ubuntu`だったので`ubuntu`に合わせています。

```
/home/ubuntu/sample-project/apps/sample-project/log/*.log {
    daily
    rotate 7
    nocompress
    missingok
    notifempty
    copytruncate
    dateext
    dateformat %Y-%m-%d
    dateyesterday
    extension .log
    create 640 ubuntu ubuntu 
    olddir /home/ubuntu/sample-project/apps/sample-project/log/old
    createolddir 755 ubuntu ubuntu
    su ubuntu ubuntu
}
```
`logrotate.d`を`ls -l`し、パーミションを確認するとrootユーザーに読み書きの権限がある状態なので、rootユーザーに変えてファイルを作るなどしてパーミション情報を合わせます。

```bash
ubuntu@dummy:/etc/logrotate.d$ ls -l
total 60
-rw-r--r-- 1 root root 120 Sep 12  2021 alternatives
-rw-r--r-- 1 root root 126 Oct 27  2021 apport
-rw-r--r-- 1 root root 173 Apr  8  2022 apt
-rw-r--r-- 1 root root  91 Mar 18  2022 bootlog
-rw-r--r-- 1 root root 130 Oct 14  2019 btmp
-rw-r--r-- 1 root root 112 Sep 12  2021 dpkg
-rw-r--r-- 1 root root 336 Mar 15 16:32 nginx
-rw-r--r-- 1 root root 329 May 31  2023 nginx.org
-rw-r--r-- 1 root root 380 Apr  8 11:33 sample-project
-rw-r--r-- 1 root root 173 Feb 21  2021 postgresql-common
-rw-r--r-- 1 root root 374 Dec 24  2021 rsyslog
-rw-r--r-- 1 root root 270 Mar  8  2022 ubuntu-advantage-tools
-rw-r--r-- 1 root root 209 Sep 19  2021 ufw
-rw-r--r-- 1 root root 235 Feb 19  2021 unattended-upgrades
-rw-r--r-- 1 root root 145 Oct 14  2019 wtmp

```

## 3. `usr/local/bin`にシェルスクリプトの配置

`get_daily_log.sh`という名前で作成しました。

```shell
###
### Phoenix の日次ログをローテートする
###


# 通知のPrefix
NOTIFICATION_PREFIX="[日次Phoenixログローテート処理]"

# エラー通知のPrefix
ERROR_NOTIFICATION_PREFIX=":exclamation:$NOTIFICATION_PREFIX"

# 正常終了通知のPrefix
INFO_NOTIFICATION_PREFIX=":information_source:$NOTIFICATION_PREFIX"

# logrotate実行パス
LOGROTATE_CONF="/etc/logrotate.d/sample-project"

/usr/sbin/logrotate $LOGROTATE_CONF
result=$?

# logrotateの実行結果に応じて通知を送信
if [ $result -eq 0 ]; then
          send_slack_notification.sh "$INFO_NOTIFICATION_PREFIX logrotate処理が正常に終了しました。"
  else
            send_slack_notification.sh "$ERROR_NOTIFICATION_PREFIX logrotate処理に失敗しました。 result_code: $result"
              exit 1
fi

exit 0

```

`send_slack_notification.sh`にslackの通知先URLなどが書かれています。

# 本番サーバーTips
本番サーバーはコンソール1つで操作するため特殊なコマンド操作も必要です。
今回は、使用したコマンドについて解説します。

## ジョブサービスについてDockerは`sysvinit`で本番は`systemed`を使用

どちらもLinuxシステムでプロセスの初期化と管理を行うシステムですが、それぞれに異なる設計思想と機能があります。

### SysVinit
---
- 歴史的背景: SysVinitは古いLinuxおよびUNIXシステムの標準的な初期化システムで、シンプルなスクリプトベースのアプローチを採用
- スクリプトベース: SysVinitは、/etc/init.d/ディレクトリに配置されたスタートアップスクリプトを使用してサービスを管理。これらのスクリプトは手動で書く必要があり、依存関係の管理が難しい
- ランレベル: システムの異なる状態（ランレベル）に応じて、特定のサービスを起動または停止。ランレベルは、マルチユーザーモード、シングルユーザーモード、シャットダウンなどの状態を定義
- シンプルな管理: SysVinitは基本的な機能しか提供しないため、システムリソースの消費は少ないですが、現代の多くの要求には対応できない場合がある

### systemd
---
- 近代的なアプローチ: systemdはSysVinitの問題点を解決するために設計されたモダンな初期化システムで、より効率的なサービス管理とシステムの起動時間の短縮を実現
- 依存関係の自動解決: systemdはサービス間の依存関係を自動的に解決し、サービスを適切な順序で起動します。これにより、システムの起動がより速く、確実に
- 統合された機能: ログ管理（journalctl）、タイマー（cronの代替）、ユーザーセッションの管理、スナップショット機能など、多くの統合された機能を提供
コンフィグレーションファイル: systemdは.serviceファイルを用いてサービスを管理し、これによりサービスの設定と管理がより簡単かつ柔軟になる

### `systemd`の場合のジョブの起動
---

```bash
systemctl restart cron.service
```

### 起動の確認
---

```
systemctl status cron.servicie
```
## サーバーをkillするコマンド

```
lsof -i :4000
```
表示されたPID(プロセスID)を指定してkillします

```
kill PID
```

## ひとつの画面上で複数のターミナルセッションを同時に実行、管理することができるツール(tmux)

tmuxは簡単にいうとコンソール。セッションを切ってもコンソールを維持し続けるのが`tmux`というイメージです。
### `tmux`を使用する理由

- セッションの保持: サーバーにリモートで接続して作業しているときに、接続が切れた場合でも、tmux セッションは保持され、再接続時に作業をそのまま再開できる
- 複数のウィンドウとペイン: 一つの画面内で複数のウィンドウを開いたり、ウィンドウを複数のペインに分割して、複数のコマンドやアプリケーションを同時に見ることができる
- カスタマイズとスクリプト自動化: 設定ファイルを編集することで、キーバインドや外観などをカスタマイズしたり、一連のコマンドをスクリプトとして実行して、作業を効率化することが可能

### コマンド

| コマンド      | やりたいこと                           |
|-----------|--------------------------------|
| tmux  | tmuxでコンソールを立ち上げる                     |
| ctrl+c ctrl+D  | tmuxコンソールから抜ける                    |
| tmux -a  | 直前のtmuxセッションに戻る                    |
| tmux -ls | セッションの一覧を表示                    |
| Control+B とその後に D  | セッションからデタッチ（切断）※                   |

※現在の tmux セッションからログアウトせずに一時的に抜けるために使用します。セッションはバックグラウンドで活動を続け、後で再接続することができます。

# さいごに

ローカルでログを出力し、本番で設定するところまで解説しました。Elixirの知識だけでなくUnixの知識も必要だということがわかりました。
パズルのピースが1つでもないと解けない問題を解いているような気分でした。

また、thewaggleではElixirでの開発にジョインしてくださる仲間を募集しています！
ご興味ある方、お気軽にご連絡ください。