Spaces:
Running
Running
Upload logs_route.py
Browse files- logs_route.py +106 -0
logs_route.py
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Independent logs page for VNEWS Space.
|
| 2 |
+
Serves /logs (HTML) and /logs.txt (raw) so build/runtime errors are visible
|
| 3 |
+
even when the Hugging Face build-logs tab is stuck/unavailable.
|
| 4 |
+
Mounted from _run.py.
|
| 5 |
+
"""
|
| 6 |
+
import os
|
| 7 |
+
import time
|
| 8 |
+
import json
|
| 9 |
+
import subprocess
|
| 10 |
+
from fastapi import Request
|
| 11 |
+
from fastapi.responses import HTMLResponse, PlainTextResponse
|
| 12 |
+
|
| 13 |
+
try:
|
| 14 |
+
from app_v2_entry import app
|
| 15 |
+
except Exception:
|
| 16 |
+
from main import app
|
| 17 |
+
|
| 18 |
+
BUILD_DONE = "/app/.build_done"
|
| 19 |
+
DATA_DIR = '/data' if os.path.isdir('/data') else os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data')
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def _collect():
|
| 23 |
+
lines = []
|
| 24 |
+
lines.append("=== VNEWS LOGS ===")
|
| 25 |
+
lines.append("generated: " + time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()))
|
| 26 |
+
lines.append("")
|
| 27 |
+
# Build marker
|
| 28 |
+
if os.path.exists(BUILD_DONE):
|
| 29 |
+
lines.append("[BUILD] .build_done exists -> container started OK")
|
| 30 |
+
try:
|
| 31 |
+
lines.append("[BUILD] built at: " + open(BUILD_DONE).read().strip())
|
| 32 |
+
except Exception:
|
| 33 |
+
pass
|
| 34 |
+
else:
|
| 35 |
+
lines.append("[BUILD] WARNING: .build_done MISSING -> uvicorn started before build finished?")
|
| 36 |
+
lines.append("")
|
| 37 |
+
|
| 38 |
+
# Space status from HF runtime file
|
| 39 |
+
try:
|
| 40 |
+
import json as _j
|
| 41 |
+
mj = os.path.join(os.path.dirname(os.path.abspath(__file__)), '.huggingface', 'main.json')
|
| 42 |
+
if os.path.exists(mj):
|
| 43 |
+
lines.append("[RUNTIME] .huggingface/main.json present")
|
| 44 |
+
else:
|
| 45 |
+
lines.append("[RUNTIME] .huggingface/main.json NOT found")
|
| 46 |
+
except Exception as e:
|
| 47 |
+
lines.append("[RUNTIME] error: " + str(e))
|
| 48 |
+
lines.append("")
|
| 49 |
+
|
| 50 |
+
# Data dir contents
|
| 51 |
+
lines.append("[DATA] dir=" + DATA_DIR)
|
| 52 |
+
try:
|
| 53 |
+
if os.path.isdir(DATA_DIR):
|
| 54 |
+
for f in sorted(os.listdir(DATA_DIR)):
|
| 55 |
+
p = os.path.join(DATA_DIR, f)
|
| 56 |
+
lines.append(" - %s (%d bytes)" % (f, os.path.getsize(p)))
|
| 57 |
+
else:
|
| 58 |
+
lines.append(" (data dir missing)")
|
| 59 |
+
except Exception as e:
|
| 60 |
+
lines.append(" error: " + str(e))
|
| 61 |
+
lines.append("")
|
| 62 |
+
|
| 63 |
+
# Recent container logs (stdout) if captured
|
| 64 |
+
log_paths = ["/tmp/vnews_stdout.log", os.path.join(DATA_DIR, "app.log")]
|
| 65 |
+
for lp in log_paths:
|
| 66 |
+
if os.path.exists(lp):
|
| 67 |
+
lines.append("[STDOUT] tail of " + lp + ":")
|
| 68 |
+
try:
|
| 69 |
+
with open(lp, "r", errors="replace") as fh:
|
| 70 |
+
tail = fh.read().splitlines()[-50:]
|
| 71 |
+
for l in tail:
|
| 72 |
+
lines.append(" " + l)
|
| 73 |
+
except Exception as e:
|
| 74 |
+
lines.append(" read error: " + str(e))
|
| 75 |
+
lines.append("")
|
| 76 |
+
|
| 77 |
+
# Environment hints
|
| 78 |
+
lines.append("[ENV] HF_SPACE: " + os.environ.get("HF_SPACE", "?"))
|
| 79 |
+
lines.append("[ENV] SPACE_ID: " + os.environ.get("SPACE_ID", "?"))
|
| 80 |
+
lines.append("[ENV] CUDA/CPU: " + ("gpu" if os.environ.get("CUDA_VISIBLE_DEVICES") else "cpu"))
|
| 81 |
+
lines.append("")
|
| 82 |
+
lines.append("=== END ===")
|
| 83 |
+
return "\n".join(lines)
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
@app.get("/logs")
|
| 87 |
+
def logs_page(request: Request):
|
| 88 |
+
txt = _collect()
|
| 89 |
+
html = (
|
| 90 |
+
"<!DOCTYPE html><html lang='vi'><head><meta charset='utf-8'>"
|
| 91 |
+
"<meta name='viewport' content='width=device-width,initial-scale=1'>"
|
| 92 |
+
"<title>VNEWS Logs</title>"
|
| 93 |
+
"<style>body{background:#0d1117;color:#c9d1d9;font-family:monospace;padding:16px}"
|
| 94 |
+
"pre{white-space:pre-wrap;word-break:break-word;font-size:13px;line-height:1.5}"
|
| 95 |
+
"a{color:#58a6ff}</style></head><body>"
|
| 96 |
+
"<h2>VNEWS — Build & Runtime Logs</h2>"
|
| 97 |
+
"<p><a href='/logs.txt'>📄 raw text</a> · refresh để cập nhật</p>"
|
| 98 |
+
"<pre>" + txt.replace("&", "&").replace("<", "<").replace(">", ">") + "</pre>"
|
| 99 |
+
"</body></html>"
|
| 100 |
+
)
|
| 101 |
+
return HTMLResponse(html)
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
@app.get("/logs.txt")
|
| 105 |
+
def logs_raw(request: Request):
|
| 106 |
+
return PlainTextResponse(_collect())
|