The user-facing half (rule 27). Per-container log viewer + admin controls, so container logs are something the operator can actually touch. Viewer (plugins/docker): - /container/<host>/<name>/logs full page + /logs/lines HTMX fragment polled every 5s (near-live follow); stream filter (all/stdout/stderr) + case-insensitive text search; newest-first so the latest lines survive the poll's scroll reset; empty/loading states; a "View logs" link from the container detail page. Jinja auto-escapes log content (no markup injection). Settings (Thresholds & Retention tab): - global on/off toggle, comma-separated exclude list, retention days + max MB per container — DB-backed, no restart. The toggle + exclude are enforced authoritatively at ingest (_persist_logs drops disabled/excluded lines), since the push model has no channel to tell an agent to stop. Tests: route-defined smoke; template-parse covers the new templates; integration test for the ingest-time toggle + exclude enforcement. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CAGR73DUowdVFVvYzLXC5C
23 lines
1.1 KiB
HTML
23 lines
1.1 KiB
HTML
{# docker/_container_logs_lines.html — log-line fragment, HTMX-polled (m79).
|
|
{{ l.line }} is auto-escaped by Jinja, so log content can't inject markup. #}
|
|
{% if lines %}
|
|
{% for l in lines %}
|
|
<div style="display:flex;gap:0.6rem;white-space:pre-wrap;word-break:break-word;">
|
|
<span style="color:var(--text-dim);flex-shrink:0;" title="{{ l.ts }}">{{ l.ts.strftime("%m-%d %H:%M:%S") }}</span>
|
|
<span style="flex-shrink:0;width:3.2rem;color:{% if l.stream == 'stderr' %}var(--red){% else %}var(--text-muted){% endif %};">{{ l.stream }}</span>
|
|
<span style="flex:1;min-width:0;{% if l.stream == 'stderr' %}color:var(--text);{% endif %}">{{ l.line }}</span>
|
|
</div>
|
|
{% endfor %}
|
|
{% else %}
|
|
<div style="color:var(--text-muted);padding:1rem 0.25rem;">
|
|
{% if filtered %}
|
|
No lines match the current filter.
|
|
{% else %}
|
|
No logs collected yet for <code>{{ name }}</code>. Lines appear within a few
|
|
seconds of the container writing to stdout/stderr — unless it's on the log
|
|
exclude list or log collection is turned off in
|
|
<a href="/settings/thresholds/">Settings → Thresholds & Retention</a>.
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|