#!/bin/env python3 import argparse import re import sqlite3 from typing import Any REGEX = re.compile( r"^" r"{address space usage: (?P-?\d+) bytes/(?P\w+)} " r"{rss usage: (?P-?\d+) bytes/(?P\w+)} " r"\[pid: (?P\d+)\|app: -\|req: -/-\] (?P[0-9.]+) \(-\) " r"{(?P\d+) vars in (?P\d+) bytes} " r"\[(?P[0-9A-Za-z: ]+)\] (?P\w+) (?P.*?) => " r"generated (?P\d+) bytes in (?P\d+) msecs " r"\((- http://hasty.ai)?(?P[A-Z0-9/.]+) (?P\d+)\) " r"(?P\d+) headers in (?P\d+) bytes " r"\((?P\d+) switches on core (?P\d+)\) " r"(?P.*?)" r"( (?Phttps?://[^ ]*?))?( -)?( http(://|%3A%2F%2F)hasty\.ai)?" r"$" ) def save_logs(logs: list[dict[str, Any]]) -> None: columns = sorted(REGEX.groupindex, key=lambda col: REGEX.groupindex[col]) conn = sqlite3.Connection("logs.db") cur = conn.cursor() cur.execute(f"CREATE TABLE IF NOT EXISTS logs({', '.join(columns)})") params = ", ".join(["?"] * len(columns)) cur.executemany( f"INSERT INTO logs VALUES ({params})", [[log[col] for col in columns] for log in logs], ) conn.commit() conn.close() def read_logs(path: str) -> list[dict[str, Any]]: with open(path, errors="replace") as fp: lines = fp.readlines() parsed = [ (line, REGEX.match(line.strip())) for line in lines if line.startswith("{address space usage") ] for line, match in parsed: if not match: print("failed to parse:", line.strip()) return [match.groupdict() for _, match in parsed if match] def main(): parser = argparse.ArgumentParser() parser.add_argument("logfile", default="uwsgi.log") args = parser.parse_args() save_logs(read_logs(args.logfile)) if __name__ == "__main__": main()