ワンクリックで
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 ページを確認してインストールできます。
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("").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.