0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

postqueue-pをcsvにする方法

Last updated at Posted at 2024-02-25
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("プログラムを終了します")
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?