Initial solution: add parse_log.py

This commit is contained in:
2026-06-10 09:57:13 +00:00
parent 0cc43ea669
commit af08b07407
+85
View File
@@ -0,0 +1,85 @@
"""CLI tool to parse raw log lines into typed ApiEvent objects using LangChain structured output.
Usage:
python parse_log.py [--log FILE]
If --log is omitted, a builtin example log is used.
"""
from __future__ import annotations
import argparse
import sys
from pathlib import Path
from typing import Iterable, List
from langchain_core.output_parsers import PydanticOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from models import ApiEvent
# Example log with mixed OK and error events
EXAMPLE_LOG = """
GET /api/users 200 123ms
POST /api/login 404 Not Found
GET /api/data 200 456ms
GET /api/health 500 Internal Server Error
"""
# Prompt template that instructs LLM to output a single ApiEvent in JSON
PROMPT = ChatPromptTemplate.from_messages(
[
("system", "You are a helpful assistant that parses a single log line into a structured event.")
]
)
# Create a parser for the ApiEvent union
parser = PydanticOutputParser(pydantic_object=ApiEvent)
# LLM model replace with your own key or use a local model
llm = ChatOpenAI(temperature=0, model="gpt-4o-mini")
def parse_line(line: str) -> ApiEvent:
"""Parse a single log line using the LLM and the structured output parser."""
# Build a prompt that includes the line and asks for JSON
prompt = PROMPT.format_messages(user=line)
# Get raw response
raw = llm.invoke(prompt)
# Parse into ApiEvent
return parser.parse(raw.content)
def parse_log(lines: Iterable[str]) -> List[ApiEvent]:
return [parse_line(line) for line in lines if line.strip()]
def main() -> None:
parser_cli = argparse.ArgumentParser(description="Parse raw log into typed events")
parser_cli.add_argument("--log", type=Path, help="Path to a log file; if omitted, example log is used")
args = parser_cli.parse_args()
if args.log and args.log.exists():
raw_lines = args.log.read_text().splitlines()
else:
raw_lines = EXAMPLE_LOG.strip().splitlines()
events = parse_log(raw_lines)
# Print each event as JSON
for ev in events:
print(ev.model_dump_json(indent=2))
# Simple table
print("\nParsed events table:\n")
print("{:<6} {:<20} {:<6} {}".format("kind", "path", "status", "details"))
for ev in events:
if ev.kind == "ok":
print(f"{ev.kind:<6} {ev.path:<20} {ev.status:<6} duration={ev.duration_ms}ms")
else:
print(f"{ev.kind:<6} {ev.path:<20} {ev.status:<6} error={ev.error_message}")
if __name__ == "__main__":
main()