"""Log parser abstraction (Chapter 07).""" from __future__ import annotations from dataclasses import dataclass from datetime import datetime from typing import Protocol, TYPE_CHECKING if TYPE_CHECKING: from app.models.log_file import LogFile @dataclass class ParsedEntry: timestamp: datetime ip: str method: str path: str status_code: int bytes_sent: int referrer: str | None user_agent: str | None class LogParser(Protocol): def parse_line(self, line: str) -> ParsedEntry | None: ... def get_parser_for(log_file: "LogFile") -> LogParser: """Build the right LogParser for a given upload's format_string. ASSUMPTION (flagged): a blank format_string — which the upload form allows — falls back to Combined, since Chapter 07 states no default for that case. Anything else is compiled as-is; a real custom LiteSpeed format is never silently replaced by a preset. """ # Imported lazily to avoid a circular import (apache.py imports # ParsedEntry from this module). from app.services.log_parser.apache import FormatCompiledParser, combined_parser, common_parser from app.services.log_parser.format_compiler import compile_format fmt = (log_file.format_string or "").strip() key = fmt.lower() if key in ("", "combined"): return combined_parser() if key == "common": return common_parser() return FormatCompiledParser(compile_format(fmt))