"""Output formatting for investigation results."""
from datetime import datetime
try:
from rich.console import Console
from rich.panel import Panel
from rich.text import Text
from rich.markdown import Markdown
RICH_AVAILABLE = True
except ImportError:
RICH_AVAILABLE = False
from ..models import Investigation, CausalChain, Severity
class OutputFormatter:
"""Format investigation results for display."""
def __init__(self, format: str = "rich"):
self.format = format
if RICH_AVAILABLE:
self.console = Console()
def format_investigation(self, investigation: Investigation) -> str:
"""Format a complete investigation."""
if self.format == "json":
return self._format_json(investigation)
elif self.format == "text":
return self._format_text(investigation)
else: # rich
return self._format_rich(investigation)
def print_investigation(self, investigation: Investigation):
"""Print investigation results."""
if RICH_AVAILABLE and self.format == "rich":
self._print_rich(investigation)
else:
print(self.format_investigation(investigation))
def _print_rich(self, investigation: Investigation):
"""Print rich-formatted investigation."""
console = self.console
# Header
console.print("\n[bold cyan]⚙ Debug Oracle Investigation[/bold cyan]\n")
console.print(f" [dim]Query:[/dim] {investigation.query}")
console.print(f" [dim]Time:[/dim] {investigation.timestamp.strftime('%Y-%m-%d %H:%M:%S UTC')}")
console.print(f" [dim]Log entries analyzed:[/dim] {len(investigation.logs)}")
console.print(f" [dim]Metrics snapshots:[/dim] {len(investigation.metrics)}")
console.print(f" [dim]Git changes:[/dim] {len(investigation.git_changes)}")
console.print()
# Analysis
if investigation.raw_response:
console.print(Panel(
Markdown(investigation.raw_response),
title="[bold yellow]Causal Analysis[/bold yellow]",
border_style="blue",
))
# Causal chains
if investigation.causal_chains:
console.print("\n[bold green]Causal Chains:[/bold green]")
for i, chain in enumerate(investigation.causal_chains, 1):
console.print(f"\n [bold]Chain {i}:[/bold]")
for step in chain.chain:
console.print(f" → {step}")
console.print(f" [dim]Confidence: {chain.confidence:.0%}[/dim]")
console.print()
def _format_rich(self, investigation: Investigation) -> str:
"""Return markdown for rich rendering."""
return investigation.raw_response or "No analysis available."
def _format_text(self, investigation: Investigation) -> str:
"""Format as plain text."""
lines = [
"=" * 60,
"Debug Oracle Investigation",
"=" * 60,
"",
f"Query: {investigation.query}",
f"Time: {investigation.timestamp.strftime('%Y-%m-%d %H:%M:%S UTC')}",
f"Log entries: {len(investigation.logs)}",
f"Metrics: {len(investigation.metrics)}",
f"Git changes: {len(investigation.git_changes)}",
"",
"-" * 60,
"Causal Analysis",
"-" * 60,
"",
investigation.raw_response or "No analysis available.",
"",
]
if investigation.causal_chains:
lines.append("-" * 60)
lines.append("Causal Chains")
lines.append("-" * 60)
for i, chain in enumerate(investigation.causal_chains, 1):
lines.append(f"\nChain {i} (confidence: {chain.confidence:.0%}):")
for step in chain.chain:
lines.append(f" → {step}")
lines.append("")
return "\n".join(lines)
def _format_json(self, investigation: Investigation) -> str:
"""Format as JSON."""
import json
return json.dumps({
"query": investigation.query,
"timestamp": investigation.timestamp.isoformat(),
"log_entries": len(investigation.logs),
"metrics": len(investigation.metrics),
"git_changes": len(investigation.git_changes),
"causal_chains": [
{
"symptom": chain.symptom,
"root_cause": chain.root_cause,
"chain": chain.chain,
"confidence": chain.confidence,
"evidence": chain.evidence,
"recommendation": chain.recommendation,
}
for chain in investigation.causal_chains
],
"analysis": investigation.raw_response,
}, indent=2)
def print_severity_summary(self, logs: list) -> None:
"""Print a quick summary of log severity counts."""
from collections import Counter
severity_counts = Counter(l.severity for l in logs)
if RICH_AVAILABLE:
console = self.console
console.print("\n[bold]Log Summary:[/bold]")
for severity in [Severity.CRITICAL, Severity.ERROR, Severity.WARNING, Severity.INFO]:
count = severity_counts.get(severity, 0)
if count > 0:
color = {"critical": "red", "error": "yellow", "warning": "blue", "info": "green"}.get(severity.value, "white")
console.print(f" [{color}]{severity.value.upper()}[/]: {count}")
else:
print("\nLog Summary:")
for severity in [Severity.CRITICAL, Severity.ERROR, Severity.WARNING, Severity.INFO]:
count = severity_counts.get(severity, 0)
if count > 0:
print(f" {severity.value.upper()}: {count}")
print()