Raspberry Piのセンサーで検出したデータをPythonでテキストログに出力させる

# はじめに
本記事はRaspberry Piのセンサーで検出したデータをPythonでテキストログに出力させる方法について記載しています。

以前、[Pythonで作る簡単植物通知システム　Raspberry Pi + 水分センサ + Messaging API](https://qiita.com/Brutus/items/7ea1fcd3aeb2795e2612)で、Raspberry Piと水分センサを使用して植物の水分を検知するプログラムを作りました。しかし、プログラムを作ったのはいいけど運用が全然できていなかったので、自動起動の設定やログ管理をちゃんとやろうと思い、カスタマイズしました。

## Raspberry Piの自動起動設定
Raspberry Piの自動起動設定の考え方はLinuxと基本的に同じと考え方になります。
サービスとして使うのであればinitあるいはsystemdを使用しますが、スクリプトレベルで動かしたいだけなら「/etc/rc.local」に記述するのが一番簡単でしょう。

### /etc/rc.local
例えば、以下のようにファイル末尾の「exit 0」の前に自動起動させたいプログラム（moisture.py）を記述することで、Raspberry Pi起動時にroot権限で自動実行します。

```bash
#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.

# Print the IP address
_IP=$(hostname -I) || true
if [ "$_IP" ]; then
  printf "My IP address is %s\n" "$_IP"
fi

echo `date` "moisture.py start" >> /var/log/python/moisture.log
/usr/bin/python3 /home/pi/python/moisture.py & 

exit 0
```

## ログ出力
Pythonで作成したプログラムから、センサーで取得したオブジェクト（※）のデータとセンサー検知時の時間をテキストログに出力させたいです。Python標準モジュールのtimeモジュールとopen関数を使って出力させるため、以下の関数を作成してプログラムに追加しました。

（※）本記事の場合はvoltsにセンサーで検知した植物の水分量のデータを格納しています。

```python
def log_write():
    output_time = time.asctime()
    log_file = open("/var/log/python/moisture.log","a+", encoding="UTF-8")
    log_file.write(output_time + " : " + str(volts) + "V" + "\n")
    log_file.close()
```

### ログ出力例
![スクリーンショット 2018-04-07 16.35.38.png](https://qiita-image-store.s3.amazonaws.com/0/221759/3f22abe2-c9c3-9677-cdee-ca750a7f438f.png)

### ログローテーション
何もしないとログが肥大化するので、とりあえず、rsyslogを使用してOSのログと一緒にログローテーションさせます。やり方は、「/etc/logrotate.d/rsyslog」に記述するだけです。必要に応じて、カスタマイズするといいでしょう。以下の場合は４世代で一週間おきにログローテーションを行ってくれます。（デフォルト設定）

```bash
/var/log/mail.info
/var/log/mail.warn
/var/log/mail.err
/var/log/mail.log
/var/log/daemon.log
/var/log/kern.log
/var/log/auth.log
/var/log/user.log
/var/log/lpr.log
/var/log/cron.log
/var/log/debug
/var/log/messages
# ローテーションさせるログファイルを追加する
/var/log/python/moisture.log
{
	rotate 4
	weekly
	missingok
	notifempty
	compress
	delaycompress
	sharedscripts
	postrotate
		invoke-rc.d rsyslog rotate > /dev/null
	endscript
}
```
### ログローテーションのテスト
rsyslogの設定を行ったときはテストをすると安全です。
ログローテーションのテストは以下のコマンドを実行します。
`sudo logrotate -dv /etc/logrotate.d/rsyslog`

正常の場合

```bash
considering log /var/log/python/moisture.log
Creating new state
  Now: 2018-04-08 00:15
  Last rotated at 2018-04-08 00:00
  log does not need rotating (log has been already rotated)
not running postrotate script, since no logs were rotated
```

エラーの場合

```bash
considering log /var/log/python/moisture.log
error: skipping "/var/log/python/moisture.log" because parent directory has insecure permissions (It's world writable or writable by group which is not "root") Set "su" directive in config file to tell logrotate which user/group should be used for rotation.
Creating new state
```
※上記例はログファイルの親ディレクトリのパーミッションに対して、"root"以外のグループによって書き込み可能な状態の場合

### Pythonプログラムのログ出力の考え方について
例えば、単なるスクリプトでその実行結果が欲しいだけなら、スクリプト実行結果をLinuxコマンドのリダイレクト**>**で行うやり方でもいいと思いましたが、今回のようなプログラム内でwhileを使って常時動かす場合は向いてないので、Python側から出力させることが必要と思いました。

Pythonのプログラムからログを出力させる時は今回のようなopen関数や、バイナリデータを扱いたい場合はpickleモジュールを使用してファイルに書き込みを行います。他にもやり方はあると思いますが、LinuxでできることはLinuxでやる。PythonでやることはPythonでやる。適材適所ですね。