Pythonで動かしっぱなしのプログラムのログを毎日違うファイルに保存したい

# きっかけ
とあるプログラムを延々と動かしてたんです。１週間ほど。
ログを出していたのですが、動かしっぱなしなので一つのログファイルにログを溜め込み・・・。（ちょっと怖い）

いや、これメモリーを圧迫したりとかしないの？（コンピューターは（も）よく知らない）

とういうことで、「動かしっぱなしのプログラムのログファイルを**日毎**に取得したい」って思いました。
精神的にも安心できそうだしね。

<br>
<br>
<br>

# 環境
2020 M1 MacBookAir
Python==3.10.8

<br>
<br>
<br>

# ドキュメントを読んでみた

https://docs.python.org/ja/3.10/library/logging.html#module-logging

ム・ズ・カ・シ・イ
それっぽいのも見当たらない

<br>
<br>
<br>

# GPTに聞いてみた
:relaxed::relaxed::relaxed:さすが！！！

とあるクラスを紹介してくれました。そして使ってみたらバッチリ。
だから紹介します！キリッ！
<br>
<br>
<br>

そのクラスとは
**TimedRotatingFileHandler**!!!
<br>
<br>
<br>

たいむどろーていてぃんぐふぁいるはんどら
時間設定されたくるくる回るファイルを取り扱うやつ

<br>
そんな感じ（意訳が過ぎるだろ）

<br>
<br>

ということで、ドキュメントを索引から探す・・・見つからん
見つけられないのでクラス名でサイト内検索したらあった！喜

<br>


https://docs.python.org/ja/3.10/library/logging.handlers.html?highlight=timedrotating#timedrotatingfilehandler


<br>
<br>

さっき見た索引をよく見ると
![索引.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655320/7c272ce6-9970-b24e-70c7-c9056b62c3e8.png)

横の`+`をクリックして開いたら`logging.handlers`があった。**ここか！**


<br>
<br>
<br>


# 実装例
```python
import logging
from logging.handlers import TimedRotatingFileHandler
import time

log_file_path = './log/my_watchdog.log'

formatter = logging.Formatter('%(asctime)s:%(levelname)s:%(message)s')
logger = logging.getLogger(__name__)
handler = TimedRotatingFileHandler(
    filename=log_file_path,    # ログファイルのパス
    when='M',                  # ファイルを作る時間単位。この場合は分。
    interval=1,                # 1分ごとにログファイルを新たに作成
    backupCount=7,             # 過去７つのログファイルを保存
    encoding='utf-8',          # エンコーディングをUTF-8に設定
    delay=False,               # ちょっと謎
    # utc=False                  # ローカルタイムでローテーション（時間設定にした場合）
    )

handler.setLevel(logging.DEBUG)
handler.setFormatter(formatter)
logger.setLevel(logging.DEBUG)
logger.addHandler(handler) # ここで色々な設定を入れたハンドラーをloggerに設定


logger.info('**************** start ***************')

while True:
    # 処理
    logger.info('処理中')
    time.sleep(20)  # サンプルとして20秒毎にログを記録
```

<br>
<br>
<br>

# 動作結果
うん、目的達成できそう。


<br>
<br>

まずは1分経過後にできた最初のファイル最初のファイル
![スクリーンショット 2024-08-28 22.46.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655320/0598cb9e-2580-c987-54ea-ad4427d41550.png)


スタートの時だけ1行付け加えているのでわかりやすいですね。
`**************** start ***************`

指定したファイル名の後ろに`2024-08-28_22-40`と日付と時間が付与されてます。

<br>
<br>
<br>

この時、書き込まれているファイルの名前はは指定した通りで、`my_watchdog.log`です。
![スクリーンショット 2024-08-28 22.48.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655320/a509ce80-518b-1b71-e303-ea6eeb1b576d.png)



<br>
<br>
<br>

と、ここまで書いていたら最初のファイルが消えてました。
![スクリーンショット 2024-08-28 22.50.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655320/d648f3cb-ad18-33b5-807e-87a6c21e9fce.png)
上に記した最初のファイル`my_watchdog.log.2024-08_22−40`が無いです。
そして末尾が`−42`までのファイルが消えてます。


<br>
<br>
<br>

今回は上の通り、毎分新しいログファイルを作り、過去７ファイルを保持するということが達成できました。
常時起動のプログラムで膨大な行数になりそうなログファイルになりそうな時はうまく時間設定、ファイル数設定をしてメモリーやらストレージをうまく使えそうです。



<br>
<br>
<br>

# TimedRotatingFileHandler解説
## filename:

ログファイルのパスを指定します。
例: './log/my_watchdog.log'
そういや、連続稼働しているプログラムの名前がバレそうだ。www


<br>
<br>

## when:

ログファイルをどんな周期で作成するか、指定するための時間単位を指定します。
主要なオプション:
'S': 秒ごと
'M': 分ごと
'H': 時ごと
'D': 日ごと
'midnight': 日付が変わるとき
'W': 曜日ごと（デフォルトは月曜日）

今回は検証なので、`M`：分を使いました。（秒とか目で追うだけで精一杯になっちまう。）


<br>
<br>

## interval:
ログファイルを生成し直す間隔を指定します。whenで指定した単位に対する数値です。
今回は`1`を設定したので、`when`の設定で単位を**分**としたので**1分毎にログファイルを生成**します。


## backupCount:
古いログファイルをいくつ保存するかを指定します。
今回は７を指定したので、７つのファイルを保持することになります。
これで、when='D'とすると日毎の１週間分のファイルが保持できます。


<br>
<br>

## encoding:
ログファイルのエンコーディングを指定します。
まぁ、`utf-8`でええんちゃう？w

<br>
<br>


## delay:
`delay が true なら、ファイルを開くのは emit() の最初の呼び出しまで延期されます。`と書いてあるけど、正直よくわからない。ということで、GPTに聞くとこう返してくれた。

`Trueに設定すると、実際にログファイルが書き込みされるまでファイルを開かないようにします。`
ま、特に設定はいらんかな（知らんけど）

<br>
<br>


## utc:
Trueに設定すると、ローテーションの時間をUTC（協定世界時）で計算します。
ま、時間設定する時には必須ですね。
日付変更に設定したいのに、9時間ズレたとかやらかしそう。www
基本は`False`でいいでしょう。

<br>
<br>
<br>


## atTime
ここに`datetime.time`で時刻を渡してあげるとこの時間で更新されるらしい（未確認）
マニュアルには`datetime.time`インスタンスじゃないといけないって明示されてました。
ここにハンドラだけ書いておく。（自分用）
```python
from logging.handlers import TimedRotatingFileHandler
from datetime.datetime import time

handler = TimeRotatingFileHandler(
    filename='./log/my_watchdog.log',
    atTime=time(8, 30),
    utc=False,
    )
    
```

<br>
<br>
<br>

# おわりに
GPTさんの手を借りながらなんとか目的を果たすことができました。
監視系のソフトや、１週間ほど動きっぱなしの現場系ソフトを作ったことがあります。
ちょっと気になっていた、**一つのログファイルが大きくなりすぎる問題**がこれで解決できそうです。
あ、今んとこ問題は出てないけどね。（知らんけど）

じゃ、また！