postqueue-pをcsvにする方法

```monitor_postfix_queue_sub.py
"""
機能       : ログファイルをCSV形式に変換して、指定された時間より古いキュー情報を抽出します。

引数       : progname.py $1 $2 $3 $4

             $1: 入力ログファイルパス
             $2: 出力CSVファイルパス
             $3: 最近の到着情報CSVパス
             $4: 指定された時間
"""
import os
import csv
from datetime import datetime, timedelta
import sys
import logging
import socket
from pathlib import Path
import inspect

#===============================================================================
# ロギング設定
#===============================================================================
def setup_logging():
    host_name = socket.gethostname()
    program_name = os.path.basename(__file__)
    log_file_path = Path(__file__).resolve().parent.parent / 'log' / 'pythonlog.log'
    logger = logging.getLogger()
    logger.setLevel(logging.INFO)
    file_handler = logging.FileHandler(log_file_path, encoding='utf-8')
    file_handler.setFormatter(logging.Formatter(f'%(asctime)s {host_name} [{program_name}:%(levelname)s] [%(process)d] %(message)s', datefmt='%Y-%m-%d %H:%M:%S'))
    stream_handler = logging.StreamHandler(sys.stdout)
    stream_handler.setFormatter(logging.Formatter(f'%(asctime)s {host_name} [{program_name}:%(levelname)s] [%(process)d] %(message)s', datefmt='%Y-%m-%d %H:%M:%S'))
    logger.addHandler(file_handler)
    logger.addHandler(stream_handler)

#===============================================================================
# ユーティリティ関数
#===============================================================================
def convert_to_datetime(arrival_time_str):
    """
    処理:
      指定された日時文字列をdatetimeオブジェクトに変換する
    備忘:
      datetime型には年情報が必要だが、Arrival Timeには年情報がない。
      よって、現在の年を年情報 or 前年を年情報とする。
      現在の年にするか、前年度にするかは以下処理で考える。
      現在の年情報＋Arrival Time vs 現在の時間
        <=: 現在の年情報
        > : 前年の年情報
    """
    try:
        current_year = datetime.now().year
        full_date_str = f"{current_year} {arrival_time_str}"
        arrival_datetime = datetime.strptime(full_date_str, '%Y %a %b %d %H:%M:%S')
        if arrival_datetime > datetime.now():
            full_date_str = f"{current_year - 1} {arrival_time_str}"
            arrival_datetime = datetime.strptime(full_date_str, '%Y %a %b %d %H:%M:%S')
        return arrival_datetime
    except Exception as e:
        logging.error(f"{e}, 関数: {inspect.currentframe().f_code.co_name}")
        return None

def process_file(input_file_path, output_file_path, recent_arrivals_file_path, minutes):
    """
    ログ種類と処理方針
    No: 処理方針: ログ
    1: skip   : -Queue ID-  --Size-- ----Arrival Time---- -Sender/Recipient-------
    2: execute: 3BA8F44328E    7188 Mon Mar 11 01:00:13  btmu_info@t.ebusiness.bk.mufg.jp
    3: execute:                (connect to rvxp.avz.se[3.64.163.50]:25: Connection time out)
    4: execute:                                         pwlvspw.cptpewkp@rvxxp.avz.se
    5: skip   :<空行>
    """
    data_list = []
    recent_arrivals_list = []
    smtp_send_failure_reason = ''
    current_data = []

    try:
        with open(input_file_path, 'r') as file:
            for line in file:
                line = line.strip()
                if line.startswith('-Queue ID-') or line.startswith('--') or not line:  # 不要情報の行(ヘッダ、末尾、空行)
                    continue
                parts = line.split()
                if (len(parts) > 6 and (parts[0][0].isalnum() or parts[0][0].isupper()) and
                   parts[1].isdigit() and ':' in parts[5] and '@' in parts[6]):  # キュー情報の行
                    # 前のキューに関連する受信者を処理
                    flush_recipients(data_list, recent_arrivals_list, current_data, smtp_send_failure_reason, minutes)
                    # 新しいキュー情報の初期化
                    queue_id = parts[0]
                    size = parts[1]
                    arrival_time_str = ' '.join(parts[2:6])
                    sender = parts[6]
                    arrival_datetime = convert_to_datetime(arrival_time_str)
                    current_data = [queue_id, size, arrival_time_str, arrival_datetime, sender]
                    smtp_send_failure_reason = ''  # 新しいキューのためリセット
                elif '@' in line and not line.startswith('('):  # 受信者の行
                    recipient = line.strip()
                    current_data.append((recipient, smtp_send_failure_reason))
                elif line.startswith('('):  # エラー情報の行
                    smtp_send_failure_reason = line

        # 最後のキューに関連する受信者を処理
        flush_recipients(data_list, recent_arrivals_list, current_data, smtp_send_failure_reason, minutes)

        # CSVに書き込み
        write_to_csv(output_file_path, data_list, ['Queue ID', 'Size', 'Arrival Time', 'Arrival DateTime', 'Sender', 'Recipient', 'Failure Reason'])
        write_to_csv(recent_arrivals_file_path, recent_arrivals_list, ['Queue ID', 'Size', 'Arrival Time', 'Arrival DateTime', 'Sender', 'Recipient', 'Failure Reason'])

        logging.info("ファイル処理が正常に完了しました。")
    except Exception as e:
        logging.error(f"{e}, 関数: {inspect.currentframe().f_code.co_name}")

def flush_recipients(data_list, recent_arrivals_list, current_data, smtp_send_failure_reason, minutes):
    for recipient, failure_reason in current_data[5:]:
        complete_data = current_data[:5] + [recipient, failure_reason]
        data_list.append(complete_data)
        if complete_data[3] < datetime.now() - timedelta(minutes=int(minutes)):
            recent_arrivals_list.append(complete_data)
    current_data.clear()  # 現在のキューデータをクリア

def write_to_csv(file_path, data_list, headers):
    """データリストを指定されたCSVファイルに書き込む"""
    try:
        with open(file_path, 'w', newline='', encoding='utf-8') as csvfile:
            writer = csv.writer(csvfile, lineterminator='\n')
            writer.writerow(headers)
            for row in data_list:
                writer.writerow([row[0], row[1], row[2], row[3], row[4], row[5], row[6] if len(row) > 6 else ''])  # Failure Reasonがない場合は空文字を挿入
    except Exception as e:
        logging.error(f"{e}, 関数: {inspect.currentframe().f_code.co_name}")

def main():
    """メイン関数"""
    try:
        setup_logging()
        logging.info("プログラムを開始します")
        input_file_path = sys.argv[1]
        output_file_path = sys.argv[2]
        recent_arrivals_file_path = sys.argv[3]
        minutes = int(sys.argv[4])

        process_file(input_file_path, output_file_path, recent_arrivals_file_path, minutes)
    except Exception as e:
        logging.error(f"{e}, 関数: {inspect.currentframe().f_code.co_name}")

if __name__ == '__main__':
    try:
        main()
    except Exception as e:
        logging.error(f"{e}")
    finally:
        logging.info("プログラムを終了します")
```