Log files source: follow the logs of Traefik, nginx and other programs
Files source (source_type=file), declared in Settings > Log sources > Log files (/data/files.json, 50 at most): name (= app), path as mounted in the container (file or glob, rotated copies .1/.gz left out, 200 files per glob), format, optional host, switch. - formats: plain text (level detected, leading timestamp read and removed), JSON lines (msg/time/level keys, numeric pino levels, other keys as fields, nested ones dotted), logfmt, access log CLF (nginx, Apache, Traefik CLF with router/backend/duration), Traefik access log in JSON (summary "GET host/path 200 · 12.5 ms · router · client"), regular expression with named groups time/level/msg/host/app/status; 5xx = err, 4xx = warning; fields named like LogStream's own renamed field_<name> - indented lines (text) and non-matching lines (regex) added to the previous entry, which waits 2 s for them - inode + offset per file in /data/files-state.json, rotation (end of .1 read first), copytruncate, file renamed within the glob; last FILES_BACKFILL (1h) of dated lines read when turned on, new files read from their start - status per file (files followed, entries read, not found / permission) UI: rows with switch, name, path, format, host (regex field when needed), status line; green edge, tint and FILE badge in the list, "Files" in the source filter, HTTP fields first in the details, FR/EN. docker-compose, .env.example and README (EN/FR): mounts, formats, FILES_BACKFILL. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
e87186e476
commit
087d9e7b30
14 files changed
+1934
-9
No files matched your search
+31
-2
@@ -24,6 +24,7 @@
|
||||
--chip-l: 38%; /* lightness of project-colored text (labels, Docker apps) */
|
||||
--src-audit: #7c4dbd; /* audit events (auditd) */
|
||||
--src-ls: #1f9aa3; /* LogStream internal logs */
|
||||
--src-file: #5f8a1f; /* log files (Settings > Sources > Log files) */
|
||||
--src-tint: 6%; /* background tint of their rows */
|
||||
|
||||
--sev-crit: #be123c;
|
||||
@@ -75,6 +76,7 @@
|
||||
--chip-l: 70%;
|
||||
--src-audit: #a983e6;
|
||||
--src-ls: #3cc3cc;
|
||||
--src-file: #9ccc54;
|
||||
--src-tint: 9%;
|
||||
--sev-crit: #fb7185;
|
||||
--sev-err: #f87171;
|
||||
@@ -110,6 +112,8 @@
|
||||
--chip-l: 70%;
|
||||
--src-audit: #a983e6;
|
||||
--src-ls: #3cc3cc;
|
||||
--src-file: #9ccc54;
|
||||
--src-file: #9ccc54;
|
||||
--src-tint: 9%;
|
||||
--sev-crit: #fb7185;
|
||||
--sev-err: #f87171;
|
||||
@@ -424,8 +428,9 @@ body.col-resizing, body.col-resizing * { cursor: col-resize !important; user-sel
|
||||
one), light tint, badge before the app name. */
|
||||
.row.src-audit { --src: var(--src-audit); }
|
||||
.row.src-logstream { --src: var(--src-ls); }
|
||||
.row.src-audit, .row.src-logstream { border-left-color: var(--src); background: color-mix(in srgb, var(--src) var(--src-tint), transparent); }
|
||||
.row.src-audit:hover, .row.src-logstream:hover { background: color-mix(in srgb, var(--src) 13%, var(--row-hover)); }
|
||||
.row.src-file { --src: var(--src-file); }
|
||||
.row.src-audit, .row.src-logstream, .row.src-file { border-left-color: var(--src); background: color-mix(in srgb, var(--src) var(--src-tint), transparent); }
|
||||
.row.src-audit:hover, .row.src-logstream:hover, .row.src-file:hover { background: color-mix(in srgb, var(--src) 13%, var(--row-hover)); }
|
||||
.src-badge {
|
||||
display: inline-block; margin-right: 5px; padding: 0 4px; border-radius: 4px; vertical-align: 1px;
|
||||
font: 700 9.5px/1.45 var(--sans); letter-spacing: .04em;
|
||||
@@ -751,11 +756,35 @@ input.switch:focus-visible { outline: 2px solid var(--accent); outline-offset: 2
|
||||
.tag-row .icon-btn svg { width: 15px; height: 15px; }
|
||||
.opt input { display: none; }
|
||||
.opt:has(input:checked) { color: var(--accent); border-color: var(--accent); background: var(--accent-soft); }
|
||||
/* Log files (Settings > Sources) */
|
||||
.file-list { display: flex; flex-direction: column; gap: 4px; }
|
||||
.file-row {
|
||||
display: grid; align-items: center; gap: 6px;
|
||||
grid-template-columns: auto minmax(7rem, 1fr) minmax(10rem, 2fr) minmax(9rem, 1.4fr) minmax(6rem, .8fr) 26px;
|
||||
padding: 6px 6px 6px 10px; border: 1px solid var(--border); border-radius: 8px; background: var(--panel);
|
||||
}
|
||||
.file-row.off > input[type="text"], .file-row.off > select { opacity: .6; }
|
||||
.file-row input[type="text"] {
|
||||
height: 28px; padding: 0 8px; min-width: 0;
|
||||
border: 1px solid var(--border); border-radius: 6px; background: var(--panel); font-size: 12.5px; outline: 0;
|
||||
}
|
||||
.file-row input.mono { font-family: var(--mono); }
|
||||
.file-row input[type="text"]:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
|
||||
.file-row select.field { height: 28px; }
|
||||
.file-row .icon-btn { width: 26px; height: 26px; border-radius: 6px; }
|
||||
.file-row .icon-btn svg { width: 15px; height: 15px; }
|
||||
.file-row .fr-regex, .file-row .fr-status { grid-column: 2 / -1; }
|
||||
.file-row .fr-regex[hidden] { display: none; }
|
||||
.file-row .fr-status { margin: 0 !important; font-size: 12px; }
|
||||
.file-toolbar { margin-top: 10px; }
|
||||
.tag-row .err { grid-column: 1 / -1; color: var(--sev-err); font-size: 12px; }
|
||||
.tag-row .err:empty { display: none; }
|
||||
|
||||
/* ---------- Mobile ---------- */
|
||||
@media (max-width: 760px) {
|
||||
.file-row { grid-template-columns: auto minmax(0, 1fr) 26px; }
|
||||
.file-row [data-del] { grid-column: 3; grid-row: 1; }
|
||||
.file-row > [data-f="path"], .file-row > [data-f="format"], .file-row > [data-f="host"] { grid-column: 2 / -1; }
|
||||
body { font-size: 13.5px; }
|
||||
.topbar { flex-wrap: wrap; padding: 8px 16px; gap: 8px 10px; }
|
||||
.search { order: 3; flex-basis: 100%; max-width: none; }
|
||||
|
||||
Reference in new issue
Block a user