129 lines
3.0 KiB
Python
Executable File
129 lines
3.0 KiB
Python
Executable File
# utils/log_viewer.py
|
|
from pathlib import Path
|
|
from django.conf import settings
|
|
from django.contrib.admin.views.decorators import staff_member_required
|
|
from django.http import Http404
|
|
from django.shortcuts import render
|
|
from datetime import datetime
|
|
from django.utils import timezone
|
|
|
|
|
|
def tail(filepath, lines=500, chunk_size=1024):
|
|
with open(filepath, "rb") as f:
|
|
f.seek(0, 2)
|
|
file_size = f.tell()
|
|
|
|
buffer = b""
|
|
pointer = file_size
|
|
|
|
while pointer > 0 and buffer.count(b"\n") < lines:
|
|
read_size = min(chunk_size, pointer)
|
|
pointer -= read_size
|
|
f.seek(pointer)
|
|
buffer = f.read(read_size) + buffer
|
|
|
|
return buffer.decode(errors="ignore").splitlines()[-lines:]
|
|
|
|
|
|
|
|
@staff_member_required
|
|
def log_list(request):
|
|
log_dir = Path(settings.BASE_DIR) / "logs"
|
|
|
|
files = []
|
|
for f in log_dir.glob("*.log"):
|
|
stat = f.stat()
|
|
files.append({
|
|
"name": f.name,
|
|
"size": stat.st_size,
|
|
"mtime": timezone.make_aware(
|
|
datetime.fromtimestamp(stat.st_mtime))
|
|
})
|
|
|
|
return render(request, "logging/logs_list.html", {"files": files})
|
|
|
|
|
|
|
|
|
|
|
|
# @staff_member_required
|
|
# def log_detail(request, filename):
|
|
# log_dir = Path(settings.LOG_VIEWER_DIR)
|
|
# file_path = log_dir / filename
|
|
|
|
# if not file_path.exists():
|
|
# raise Http404
|
|
|
|
# lines = tail(file_path, settings.LOG_VIEWER_MAX_LINES)
|
|
|
|
# return render(
|
|
# request,
|
|
# "logging/log_detail.html",
|
|
# {"filename": filename, "lines": lines},
|
|
# )
|
|
|
|
|
|
import re
|
|
from pathlib import Path
|
|
from django.conf import settings
|
|
from django.shortcuts import render
|
|
from django.http import Http404
|
|
|
|
LOG_PATTERN = re.compile(
|
|
r"""
|
|
^\[
|
|
(?P<time>[\d\-:\s,]+)
|
|
\]\s+\[
|
|
(?P<level>DEBUG|INFO|WARNING|ERROR|CRITICAL)
|
|
\]\s+
|
|
(?P<logger>[^:]+):
|
|
\s+
|
|
(?P<message>.*)
|
|
$
|
|
""",
|
|
re.VERBOSE,
|
|
)
|
|
|
|
LEVEL_ORDER = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
|
|
|
|
|
|
def log_detail(request, filename):
|
|
log_dir = Path(settings.LOG_VIEWER_DIR)
|
|
|
|
file_path = log_dir / filename
|
|
if not file_path.exists() or not file_path.is_file():
|
|
raise Http404("Log file not found")
|
|
|
|
lines = []
|
|
|
|
with file_path.open("r", errors="replace") as f:
|
|
for raw in f:
|
|
raw = raw.rstrip("\n")
|
|
|
|
match = LOG_PATTERN.match(raw)
|
|
if match:
|
|
data = match.groupdict()
|
|
data["raw"] = raw
|
|
data["is_trace"] = False
|
|
else:
|
|
# stack trace / continuation line
|
|
data = {
|
|
"time": "",
|
|
"level": "",
|
|
"logger": "",
|
|
"message": raw,
|
|
"raw": raw,
|
|
"is_trace": True,
|
|
}
|
|
|
|
lines.append(data)
|
|
|
|
return render(
|
|
request,
|
|
"logging/log_detail.html",
|
|
{
|
|
"filename": filename,
|
|
"lines": lines,
|
|
},
|
|
)
|