#!/usr/bin/env python3 """ Perforce Server Log Analyzer (Python 3) — fast, streaming, no DB, single output file Updates: - ONLY outputs: .txt (default: logrpt.txt) - Wider text columns (client) + safe clipping so alignment stays intact. - Adds section: "LONGEST WRITE LOCK HOLD TIMES (BY COMMAND)". - REMOVED section + calculation: "LONGEST LOCK HOLD TIMES (BY USER)" (per user aggregation). - Fixes heap TypeError by adding a monotonic sequence tiebreaker (Python 3 heap compares tuples). """ from __future__ import annotations import argparse import datetime as dt import gzip import lzma import re import sys import time from dataclasses import dataclass, field from heapq import heappush, heappushpop, nlargest from itertools import count from pathlib import Path from typing import Dict, List, Optional, Tuple TOPK = 25 UTC = dt.timezone.utc # Wider report columns W_USER = 24 W_CLIENT = 60 W_PID = 8 W_CMD = 24 W_DATE = 20 W_METRIC = 16 # right-aligned numeric # Global monotonically increasing counter used as a heap tiebreaker _HEAP_SEQ = count(1) # ---------------------------- # Time helpers (timezone-aware UTC) # ---------------------------- def utc_iso(ts: int) -> str: if not ts: return "" return dt.datetime.fromtimestamp(ts, tz=UTC).strftime("%Y-%m-%d %H:%M:%S") # ---------------------------- # Text formatting helpers # ---------------------------- def _clip_left(s: str, width: int) -> str: """Keep leftmost chars; add ellipsis if clipped.""" if width <= 0: return "" if len(s) <= width: return s if width <= 1: return s[:width] return s[: width - 1] + "…" def _clip_right(s: str, width: int) -> str: """Keep rightmost chars; add ellipsis if clipped. Useful for long client names.""" if width <= 0: return "" if len(s) <= width: return s if width <= 1: return s[-width:] return "…" + s[-(width - 1):] def fmt_row(user: str, client: str, pid: int, cmd: str, date_s: str, metric: float, metric_fmt: str) -> str: u = _clip_left(str(user), W_USER) c = _clip_right(str(client), W_CLIENT) d = _clip_left(str(date_s), W_DATE) cm = _clip_left(str(cmd), W_CMD) return ( f"{u:>{W_USER}} " f"{c:<{W_CLIENT}} " f"{pid:>{W_PID}d} " f"{cm:<{W_CMD}} " f"{d:<{W_DATE}} " f"{metric:{metric_fmt}}" "\n" ) # ---------------------------- # Regexes / parsing helpers # ---------------------------- FIRST_LINE_RE = re.compile(r"^(?P\S+)\s+(?P