一键导入
logging-monitoring
Guide to the real-time logging system, LogViewer component, and system monitoring.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Guide to the real-time logging system, LogViewer component, and system monitoring.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
Guide for server management, system APIs, backups, and maintenance operations.
Guide for developing features in the Vite + React Router frontend.
Guide to Docklift's automated release pipeline using semantic-release.
Guide for setting up, running, and developing the Docklift project.
Guide for setting up and managing Docklift's GitHub App integration.
Coolify/Dokploy-style managed databases with Dokku-style app linking.
基于 SOC 职业分类
| name | Logging & Monitoring |
| description | Guide to the real-time logging system, LogViewer component, and system monitoring. |
Docklift streams logs for both platform containers and user apps over Server-Sent Events (SSE).
Docker Container → stdout/stderr → Dockerode → SSE → EventSource → LogViewer UI
backend/src/routes/system.ts → GET /api/system/logs/:servicebackend/src/services/docker.ts → streamContainerLogs()backend/src/routes/deployments.ts → SSE during build + docker compose upfrontend/src/components/LogViewer.tsx — single source of truth for renderingfrontend/src/components/SystemLogsPanel.tsx — SSE wrapper for one servicefrontend/src/pages/Logs.tsx — tabbed UI for platform containersfrontend/src/pages/ProjectDetail.tsx → ContainerLogsPanel — uses LogViewerThese are the only valid system services. They are defined once, in LOG_SERVICE_CONTAINERS
(backend/src/routes/system.ts), and must match docker-compose.yml:
| Service key | Container | UI label | Role |
|---|---|---|---|
backend | docklift-backend | Backend | API server |
frontend | docklift-frontend | Frontend | Dashboard SPA |
nginx | docklift-nginx | Dashboard Gateway | Serves the panel on :8080 |
proxy | docklift-nginx-proxy | Public Proxy | Public :80/:443 for app domains |
certbot | docklift-certbot | Certbot | Certificate issuance + renewal |
Naming matters. There are two nginx containers and calling both "nginx" confuses everyone.
docklift-nginxis the internal dashboard gateway;docklift-nginx-proxyis the public edge that terminates TLS for user domains. The UI uses the role names above, not the image name.
There is NO
docklift-dbordocklift-redis. SQLite is a file inside the backend container.
frontend/src/components/LogViewer.tsx, used by both system and project logs.
| Prop | Type | Default | Description |
|---|---|---|---|
logs | string[] | — | Raw log lines from SSE |
connected | boolean | — | Whether the SSE connection is alive |
title | string | — | Header title (role name) |
subtitle | string | — | Monospace subtitle (real container name) |
onClear | () => void | — | Clear log buffer |
onDownload | () => void | auto | Custom download handler |
height | string | "h-[600px]" | Tailwind height class |
downloadFilename | string | "logs.txt" | Download filename |
2026-Feb-05 12:06:06.047error, fatal, panic, fail · Amber: warnsuccess, ready, loaded, ✓ · Blue: info, starting, 🚀\n (not "")Use direct scrollTop assignment inside requestAnimationFrame — not scrollIntoView():
const el = scrollRef.current;
if (el) requestAnimationFrame(() => { el.scrollTop = el.scrollHeight; });
scrollIntoView() scrolls the page as well as the log pane, which fights the sidebar shell layout,
and a smooth animation never lands at the bottom while lines are still streaming — every append
restarts it and it stalls part-way down.
const es = new EventSource(`/api/system/logs/backend?tail=500&token=${sseToken}`);
es.onmessage = (event) => {
const data = JSON.parse(event.data); // { type: 'log', message: '...' }
setLogs(prev => [...prev, data.message]);
};
SSE needs a short-lived token from POST /api/auth/sse-token, never the session JWT — see the
authentication skill.
{ "type": "log", "message": "Prisma schema loaded from prisma/schema.prisma" }
{ "type": "error", "message": "Container not found" }
{ "type": "status", "message": "Container is not running" }
{ "type": "end", "message": "Stream closed" }
SystemLogsPanel retries with backoff when the stream drops. Reset the retry counter only when a
type: 'log' frame arrives — not in onopen and not on status/error frames.
A missing or stopped container still "opens" the stream successfully and immediately emits an error
frame. Resetting the counter there produces a tight reconnect loop that hammers the API forever.
Repeated status lines are also deduplicated (appendStatus) so a stopped container does not fill the
buffer with the same sentence.
SystemLogsPanel must receive key={service} so switching tabs fully unmounts and remounts it.
Without the key, the previous service's lines stay in state and appear interleaved.
<SystemLogsPanel
key={activeService}
service={activeService}
label="Public Proxy"
container="docklift-nginx-proxy"
isActive
/>
tail defaults to 200 (?tail= up to the UI's 500 for initial load)timestamps: true in streamContainerLogs)docker-compose.yml runs certbot renew without --quiet and echoes a timestamped
[certbot] … checking renewals line each cycle. With --quiet, a healthy renewal loop produces no
output at all, which is indistinguishable from a broken container in the Logs UI.
LOG_SERVICE_CONTAINERS, or the
container is genuinely absent. Check the backoff reset rule above before assuming an API bug.key prop on SystemLogsPanel.scrollIntoView().logs.join("\n"), not logs.join("").