feat(syslog): a device through Docker's address is placed by its name, or a device address set on it (#6096)
CI / lint (push) Successful in 2s
CI / unit (push) Successful in 1m32s
CI / integration (push) Successful in 1m42s
CI / publish (push) Successful in 20s

The SNMP poll asks each device's sysName in the same GET and keeps it
(snmp_003_sys_name). A syslog source told apart by name finds its own
address from the SNMP device (reported or given name) or host of that
name, so its lines reach the SNMP device's and host's pages again; the
edit page gains Device address, which always wins (syslog_004).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-19 12:24:58 -04:00
co-authored by Claude Opus 5.5
parent 5ce3dfe69d
commit fcd58224de
14 changed files with 184 additions and 32 deletions
+8 -3
View File
@@ -17,9 +17,14 @@
<input class="input" id="syslog-name" name="name" maxlength="100" value="{{ source.name }}" placeholder="{{ source.hostname_seen or source.address }}" autocomplete="off">
</div>
<div class="field">
<span class="field-label">Address</span>
<span class="mono">{{ source.address }}</span>
<span class="field-hint">Lines are matched to a source by the address they come from.</span>
<span class="field-label">Lines come from</span>
<span class="mono">{{ source.address }}{% if source.sender %} as {{ source.sender }}{% endif %}</span>
<span class="field-hint">{% if source.sender %}Docker's address, through Swarm's ingress: this device is told apart by the name in its lines.{% else %}Lines are matched to a source by the address they come from.{% endif %}</span>
</div>
<div class="field">
<label class="field-label" for="syslog-device-address">Device address</label>
<input class="input mono" id="syslog-device-address" name="device_address" maxlength="255" value="{{ source.device_address }}" placeholder="{{ found if found != source.address else '' }}" autocomplete="off">
<span class="field-hint">The device's own address, which places its lines on its SNMP device's and host's pages.{% if source.sender %} {% if found != source.address %}Found as <span class="mono">{{ found }}</span> from the SNMP device or host named {{ source.sender }}; set it only to override that.{% else %}No SNMP device or host is named {{ source.sender }}, so set it here.{% endif %}{% else %} Leave it empty: it is the address the lines come from.{% endif %}</span>
</div>
<div class="field">
<label class="field-label" for="syslog-host">Show on host</label>
+3 -3
View File
@@ -82,7 +82,7 @@
<tbody>
{% for s in offered %}
<tr>
<td>{% if s.sender %}<span class="mono">{{ s.sender }}</span><div class="field-hint">through Docker's ingress, {{ s.address }}</div>{% else %}<span class="mono">{{ s.address }}</span>{% if s.hostname_seen %}<div class="field-hint">calls itself {{ s.hostname_seen }}</div>{% endif %}{% if s.docker_address %}<div class="tone-warn" style="font-size:0.78rem;">Docker's address, not the device's</div>{% endif %}{% endif %}</td>
<td>{% if s.sender %}<span class="mono">{{ s.sender }}</span><div class="field-hint">{% if s.device_address %}<span class="mono">{{ s.device_address }}</span>, {% endif %}through Docker's ingress</div>{% else %}<span class="mono">{{ s.address }}</span>{% if s.hostname_seen %}<div class="field-hint">calls itself {{ s.hostname_seen }}</div>{% endif %}{% if s.docker_address %}<div class="tone-warn" style="font-size:0.78rem;">Docker's address, not the device's</div>{% endif %}{% endif %}</td>
<td>{{ "{:,}".format(s.lines_total) }}</td>
<td>{{ s.last_seen_at|ago }}</td>
<td class="mono" style="max-width:28rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;" title="{{ s.last_line }}"><span class="{{ tone(s.level) }}">{{ s.severity or "" }}</span> {{ s.last_line or "—" }}</td>
@@ -121,7 +121,7 @@
<tbody>
{% for s in sources %}
<tr>
<td><a href="/plugins/syslog/sources/{{ s.id }}">{{ s.name }}</a><div class="field-hint mono">{{ s.address }}</div>{% if s.sender %}<div class="field-hint">through Docker's ingress</div>{% elif s.docker_address %}<div class="tone-warn" style="font-size:0.78rem;">Docker's address, not the device's</div>{% endif %}</td>
<td><a href="/plugins/syslog/sources/{{ s.id }}">{{ s.name }}</a><div class="field-hint mono">{{ s.device_address or s.address }}</div>{% if s.sender %}<div class="field-hint">through Docker's ingress</div>{% elif s.docker_address %}<div class="tone-warn" style="font-size:0.78rem;">Docker's address, not the device's</div>{% endif %}</td>
<td>{% if s.host %}<a href="/hosts/{{ s.host.id }}">{{ s.host.name }}</a>{% else %}—{% endif %}</td>
<td class="num">{{ "{:,}".format(s.rate or 0) }}</td>
<td>{{ s.last_seen_at|ago }}</td>
@@ -142,7 +142,7 @@
<tbody>
{% for s in ignored %}
<tr>
<td><span class="mono">{{ s.sender or s.address }}</span>{% if s.sender %}<div class="field-hint">through Docker's ingress, {{ s.address }}</div>{% elif s.hostname_seen %}<div class="field-hint">calls itself {{ s.hostname_seen }}</div>{% endif %}</td>
<td><span class="mono">{{ s.sender or s.address }}</span>{% if s.sender %}<div class="field-hint">{% if s.device_address %}<span class="mono">{{ s.device_address }}</span>, {% endif %}through Docker's ingress</div>{% elif s.hostname_seen %}<div class="field-hint">calls itself {{ s.hostname_seen }}</div>{% endif %}</td>
<td>{{ "{:,}".format(s.lines_total) }}</td>
<td>{{ s.last_seen_at|ago }}</td>
{% if admin %}<td><form method="post" action="/plugins/syslog/sources/{{ s.id }}/offer" style="margin:0;">{{ csrf_field() }}<button type="submit" class="btn btn-sm btn-ghost">Offer again</button></form></td>{% endif %}