Raspberry Piで記録する温度、湿度などのログをローテーションする


これは何

Raspberry Pi 4にて記録中の室温などの環境データのファイルがなかなかのサイズになってきたので

  1. logging機能を使って、データファイルをローテーション
  2. データを収集する機会が増えることが予想されるためデータファイルをcsvからjsonに移行

を行うこととしました。

環境

  1. Raspberry Pi 4
  2. Linux raspi4 5.4.42-v8+ #1319 SMP PREEMPT Wed May 20 14:18:56 BST 2020 aarch64 GNU/Linux
  3. 気圧・気温・湿度センサー(BME280)
  4. Python 3.8.7 (default, Jan 27 2021, 11:28:25)

作業方針

  1. 標準のloggingとjsonのフォーマッターはこちらを使用する
  2. BME280のコードはSwitch ScienceのGitHubから拝借したものを修正して使用する。

BME280の設定

Switch Scienceのgithubから入手したbme280_sample.pyを2to3を使用して変換して

% 2to3-2.7 -w bme280_sample.py

bme280.pyにコピーして、以下を修正

$ diff bme280_sample.py bme280.py 
69,71c69
<       compensate_T(temp_raw)
<       compensate_P(pres_raw)
<       compensate_H(hum_raw)
---
>       return compensate_T(temp_raw), compensate_P(pres_raw), compensate_H(hum_raw)
95c93
<       print("pressure : %7.2f hPa" % (pressure/100))
---
>       return pressure/100
103c101
<       print("temp : %-6.2f ℃" % (temperature)) 
---
>       return temperature
117c115
<       print("hum : %6.2f %" % (var_h))
---
>       return var_h

loggingの設定から温度などの読み出しまで

ちゃちゃっと行きます。
まずは、thp_logger.pyとproject_logging.pyを作成します。

  1. bme280.py
  2. project_logging.py

をthp_logger.pyでインポートし、thp_logger.pyを実行します。

thp_logger.pyは、こんな感じにしました。

thp_logger.py
#coding: utf-8
import time
import datetime
import os

import bme280

import logging
from project_logging import getLogger
logger = getLogger(__name__)

def main():
    mesuredData = []
    mesuredData = bme280.readData()
    logger.info({'timestamp':time.time(),'temp(deg.)': mesuredData[0],'pressure(hPa)':mesuredData[1],'humid.(%)':mesuredData[2]})

if __name__ == '__main__':
    while True:
        main()
        time.sleep(1)

time.sleep()は、お好みでどうぞ。

おつぎは、project_logging.pyです。

project_logging.py
import logging
import datetime
from pytz import timezone
from pythonjsonlogger import jsonlogger
import logging.handlers

now = datetime.datetime.now()
filename = now.strftime("%Y%m%d") +'_thpLog.json'

# https://github.com/madzak/python-json-logger#customizing-fields
class JsonFormatter(jsonlogger.JsonFormatter):

    def parse(self):
        """
        他に出したいフィールドがあったらこのリストに足す
        https://docs.python.jp/3/library/logging.html
        """
        return [
            'process',
            'timestamp',
            'level',
            'temp(deg.)',
            'pressure(hPa)',
            'humid.(%)',    
        ]

    def add_fields(self, log_record, record, message_dict):
        super().add_fields(log_record, record, message_dict)
        if not log_record.get('timestamp'):
            # https://qiita.com/yoppe/items/4260cf4ddde69287a632
            now = datetime.datetime.now(timezone('Asia/Tokyo')).strftime('%Y-%m-%dT%H:%M:%S%z')
            log_record['timestamp'] = now
        if log_record.get('level'):
            log_record['level'] = log_record['level'].upper()
        else:
            log_record['level'] = record.levelname


def getLogger(module_name):
    """プロジェクトごとにハンドラの設定などをしたい場合はここでやる"""
    logger = logging.getLogger(module_name)
    #handler = logging.FileHandler(filename, mode="w")
    handler = logging.handlers.TimedRotatingFileHandler(filename, encoding='utf-8',
        when='S', 
        interval=10, 
        backupCount=5,)
    handler.suffix = "%Y-%m-%d_%H%M%S.json"
    formatter = JsonFormatter()
    handler.setFormatter(formatter)
    logger.addHandler(handler)
    logger.setLevel(logging.DEBUG)
    return logger

テスト用に10秒ごとでローテーションするようにしていますが、実際の運用時にはwhenに月曜日始まりのW0を設定するつもりです。

詳細は、公式ドキュメントで確認ください。

稼働風景

1秒ごとセンサーデータを取得して、10秒ごとにログローテーションする設定での動作です。

jsonファイルのpandasからの読み出し

readjson.py
import json
import pandas as pd

filename = '20210202_thpLog.json'
df = pd.read_json(filename, lines=True)
print(df)

で読み出すことができます。あとは、ご自由に。

PS E:\Dropbox\00_works\Python\20210202_\json> python .\readJson.py
      process                     timestamp level  temp(deg.)  pressure(hPa)  humid.(%)
0       19035 2021-02-02 10:40:22.837137920  INFO   23.369868    1000.237530  33.152283
1       19035 2021-02-02 10:40:23.839900416  INFO   23.364816    1000.228997  33.072454
2       19035 2021-02-02 10:40:24.842537984  INFO   23.364816    1000.282805  32.972641
3       19035 2021-02-02 10:40:25.845187328  INFO   23.369868    1000.237530  32.912729
4       19035 2021-02-02 10:40:26.847422208  INFO   23.369868    1000.237530  32.822892
...       ...                           ...   ...         ...            ...        ...
2653    19035 2021-02-02 11:24:43.185660416  INFO   23.051628    1000.587428  29.506088
2654    19035 2021-02-02 11:24:44.188551680  INFO   23.051628    1000.533646  29.531049
2655    19035 2021-02-02 11:24:45.191405824  INFO   23.051628    1000.479865  29.645868
2656    19035 2021-02-02 11:24:46.194267136  INFO   23.056680    1000.542184  29.950316
2657    19035 2021-02-02 11:24:47.197230080  INFO   23.056680    1000.569075  29.815537

[2658 rows x 6 columns]

補足(タイムゾーンの変換)

上記のスクリプトだと、デフォルトでtimestampの値が日付に変換されてしまいます。
(詳しくはこちらを確認してください。)

これはよろしくないので、タイムゾーンを変換します。

私の場合はpyqtgraphを使うのでunixtimeでOKですが、JSTに変換する場合も記載します。

import pandas as pd

filename = '20210202_thpLog.json'

# timezoneをJSTに変換する場合
df_jst = pd.read_json(filename, lines=True).set_index('timestamp').tz_localize('UTC').tz_convert('Asia/Tokyo')
print(df_jst)

# unixtimeのまま使用する場合
df_unixtime = pd.read_json(filename, lines=True, convert_dates=False)
print(df_unixtime)

上記を実行すると以下のようになります。
タイムゾーンの変換は、indexのみが対象となるようでして。。。
timestampをindex列にしてから、タイムゾーンの変換を行っています。

PS E:\Dropbox\00_works\Python\20210202_\json> python .\readJson.py
                                     process level  temp(deg.)  pressure(hPa)  humid.(%)
timestamp
2021-02-02 19:40:22.837137920+09:00    19035  INFO   23.369868    1000.237530  33.152283
2021-02-02 19:40:23.839900416+09:00    19035  INFO   23.364816    1000.228997  33.072454
2021-02-02 19:40:24.842537984+09:00    19035  INFO   23.364816    1000.282805  32.972641
2021-02-02 19:40:25.845187328+09:00    19035  INFO   23.369868    1000.237530  32.912729
2021-02-02 19:40:26.847422208+09:00    19035  INFO   23.369868    1000.237530  32.822892
...                                      ...   ...         ...            ...        ...
2021-02-02 20:24:43.185660416+09:00    19035  INFO   23.051628    1000.587428  29.506088
2021-02-02 20:24:44.188551680+09:00    19035  INFO   23.051628    1000.533646  29.531049
2021-02-02 20:24:45.191405824+09:00    19035  INFO   23.051628    1000.479865  29.645868
2021-02-02 20:24:46.194267136+09:00    19035  INFO   23.056680    1000.542184  29.950316
2021-02-02 20:24:47.197230080+09:00    19035  INFO   23.056680    1000.569075  29.815537

[2658 rows x 5 columns]
      process     timestamp level  temp(deg.)  pressure(hPa)  humid.(%)
0       19035  1.612262e+09  INFO   23.369868    1000.237530  33.152283
1       19035  1.612262e+09  INFO   23.364816    1000.228997  33.072454
2       19035  1.612262e+09  INFO   23.364816    1000.282805  32.972641
3       19035  1.612262e+09  INFO   23.369868    1000.237530  32.912729
4       19035  1.612262e+09  INFO   23.369868    1000.237530  32.822892
...       ...           ...   ...         ...            ...        ...
2653    19035  1.612265e+09  INFO   23.051628    1000.587428  29.506088
2654    19035  1.612265e+09  INFO   23.051628    1000.533646  29.531049
2655    19035  1.612265e+09  INFO   23.051628    1000.479865  29.645868
2656    19035  1.612265e+09  INFO   23.056680    1000.542184  29.950316
2657    19035  1.612265e+09  INFO   23.056680    1000.569075  29.815537

[2658 rows x 6 columns]

参考にさせて頂いたサイト

ログ出力のための print と import logging はやめてほしい - Qiita
pythonでjson形式でログを出す - Qiita