48 lines
1.4 KiB
Python
48 lines
1.4 KiB
Python
"""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))
|