feat(syslog): devices through Swarm's ingress told apart by name; the send-to address is a setting (#6096)
CI / lint (push) Successful in 2s
CI / unit (push) Failing after 1m30s
CI / integration (push) Successful in 1m38s
CI / publish (push) Skipped

Through ingress every device arrives from Docker's own address, so a
source there is keyed by the hostname its lines carry (new column
sender, unique with address). The set-up help no longer guesses the
address from the web hostname: it shows the plugin's send_to setting,
covers ingress and host mode, ASUSTOR ADM, and both syslog formats.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-19 12:05:28 -04:00
co-authored by Claude Opus 5.5
parent d2926b6663
commit 0a647bbb82
11 changed files with 193 additions and 53 deletions
+8 -4
View File
@@ -23,7 +23,7 @@ from .network import on_own_network
from .parse import SEVERITIES, facility_name, severity_name
from .patterns import (PRESETS, PRESETS_BY_KEY, SOURCE_MODULE, WINDOWS, PatternError, key_for, parse_form,
resource_of)
from .receiver import LISTENER, MAX_SENDERS, settings
from .receiver import LISTENER, MAX_SENDERS, send_to, settings
from .service import (AROUND_LINES, AROUND_SEVERITY, MARK_SEVERITY, MAX_MARKS, PANEL_LINES, PANEL_SPAN,
SEVERITY_CHOICES, apps_of, filtered, line_filter, lines_between, place_for, rates, shown_on,
source_name)
@@ -55,7 +55,7 @@ def listener_view(port: int, udp: bool, tcp: bool) -> dict:
def source_view(s: SyslogSource, hosts: dict, rate: int | None = None) -> dict:
host = hosts.get(s.host_id) if s.host_id else None
return {"id": s.id, "address": s.address, "state": s.state, "rate": rate,
return {"id": s.id, "address": s.address, "sender": s.sender, "state": s.state, "rate": rate,
"docker_address": on_own_network(s.address),
"name": s.name or s.hostname_seen or s.address,
"hostname_seen": s.hostname_seen, "host": host,
@@ -66,7 +66,11 @@ def source_view(s: SyslogSource, hosts: dict, rate: int | None = None) -> dict:
def suggested_host(source: SyslogSource, hosts: list) -> str | None:
"""The host whose address is the sender's, if one is."""
"""The host whose address is the sender's, if one is; for a device that
reaches Steward through Docker's address, the host of the name it gives."""
if source.sender:
name = source.sender.lower()
return next((h.id for h in hosts if name in (h.name.lower(), (h.address or "").lower())), None)
return next((h.id for h in hosts if h.address == source.address), None)
@@ -91,7 +95,7 @@ async def index():
return await render_template("syslog/index.html", listener=listener,
sources=views["accepted"], offered=views["offered"],
ignored=views["ignored"], hosts=hosts,
server=request.host.rsplit(":", 1)[0] if request.host.count(":") == 1 else request.host,
server=send_to(current_app),
now=datetime.now(timezone.utc))