0.5.5 — 2026-09-15¶
The web console gains a probe editor and a settings page, so an agent is
configured from the browser; enabling TLS no longer requires a certificate;
the licence agreement is published and linked from the console, the installer
and the CLI; the agent ships as a container image; agent config check reads
the values, not only their shape; uninstall cleans the machine even without a
service unit; a host with a saturated inotify quota keeps monitoring; OTLP
outputs can push to a base path and report failures per signal; a probe's logs
can be routed to one output; a Pro probe reads the console log stream of an
Azure Container App; and four breaking changes on tags, channel names and
metric timestamps, listed at the end.
Features¶
-
Enabling TLS no longer requires supplying a certificate.
tls.enabled: truewith nothing else now produces a self-signed pair on first start, next to the configuration at<config directory>/certs/, readable only by the service user. The log says the certificate is self-signed and how to replace it, rather than letting a browser warning be the first news of it. The pair is generated once: replacing those two files with your own keeps them, and a restart or an upgrade will not undo your certificate.Naming a
cert_filethat does not exist is a different case and generates nothing. The agent refuses to start the listener and names the file and the directory it looked in, because a path you wrote and that is not there is more likely a typo than an invitation, and writing a certificate over it would hide the mistake. -
The licence that covers the paid probes is published, and the agent says where to read it. The SenHub Agent licence agreement is now a page rather than a file to request: the French text, which is the only binding version, and an English courtesy translation carrying the same article numbering so a cross-reference resolves in either language, each with a printable PDF. The installer names it on Windows, the install command prints it on Linux, and the console's Settings page links to it from the activation field, so an operator can read what activating a token places under contract before doing it. Annex 1, the third-party component list, is generated from the build's dependency graph and describes what actually ships rather than what the dependency file declares.
-
The agent ships as a container image.
ghcr.io/senhub-io/senhub-agentstarts from one required variable and one mount: an export token, and a volume on the state directory. An entrypoint writes a configuration from the environment when none is mounted and checks it before the agent starts, so a container whose variables produce a file the agent would refuse stops with the reason instead of restarting in a loop. Probes come through three doors:SENHUB_PROBEScarrying the YAML aprobes.dfile would hold, a mountedprobes.d, or a whole mounted configuration directory. Credentials are never written to a file; the fragments carry${env:...}references the agent resolves at every start. Linux amd64 and arm64, running as an unprivileged user, with a health check on the console.Identity is the part worth reading twice. A container has no machine-id, and
host.idis read from it, so without one the underlying library falls back to the kernel boot identifier and every container arrives as a new host: measured on a real deployment, three runs of the same image, three hosts in the graph. The agent key has the same shape of problem for the other identity, since it lives inagent.yaml. Both are now resolved at startup and kept in the state volume, so one volume is one host and one agent, andSENHUB_HOST_IDsettles the first where the deployment already knows it. Nothing is baked into the image: an identity that belongs to an image belongs to all of its instances at once. -
The web console puts what you need to start in front, and shows where the data goes. The menu now follows the operator's path: Overview, Probes, Outputs, Settings, Docs. The probe editor opens on a block "Required to start" (name, enabled, and the parameters the probe does nothing useful without: host, user and password for a database, target, version and community for SNMP), with a sticky action bar and every optional group folded behind a summary of what is set. Errors are anchored on their field, a stored secret is shown as a state with a Replace button, the type is chosen in a filterable grid, and a fresh install offers four starters instead of an empty table. The new Outputs page lists every output with its state (listening, exporting, idle, failing with the reason, disabled) and who last read each endpoint; push outputs are created and edited from a form generated from their schema, and an OTLP output is tested step by step (name resolution, TCP, TLS, one real export) before it is saved. The Sensor Builder became the Sensor URLs tab of the HTTP output; the old address redirects there. The Overview replaces the dashboard with a getting-started list, compact probe and output summaries, one agent card, a licence card without the list of sixty names, and the recent events of the agent (a probe that started failing, an output that could not start, a save from the console). The Docs entry opens the documentation on agent.senhub.io. Scripts get the same through
/api/{key}/catalog/outputs,/api/{key}/config/outputsand/api/{key}/info/events.A few details an operator will meet. An output can be created disabled: its file is written as
.disabledfrom the start. A file understrategies.dthe agent cannot read is listed as failing with its error instead of being hidden. An edit that does not mentionenabledleaves a disabled output disabled. A stored secret is shown as Stored and never sent back by the form, inside a block or a list of blocks (the users of an SNMP v3 device) as well; Replace, then an empty field, drops it. The probe catalogue tells a type that needs a licence from one that does not run on this operating system. -
Every probe type says what it reads. All 66 probe types of the agent now carry a parameter schema: the keys the probe reads, their kind and default, which ones it cannot start without, which ones are secrets. The console's editor is generated from it, so every type is added and edited from the Probes page with the same form, Validate reports a key the probe never reads or a value of the wrong shape before anything is written, and the catalogue lists every type with its tier and where it runs.
-
Push OTLP straight to a backend that serves it under a base path.
endpointis ahost:portpair and cannot carry a path, which ruled out backends exposing OTLP under a prefix. The newurl_path_prefix(OTLP/HTTP only) is prepended to the standard signal paths, so/api/v2/otlpsends to/api/v2/otlp/v1/metricsand its siblings. Setting it withprotocol: grpcis refused at config load rather than silently ignored. Documented with a Dynatrace example. -
Close idle OTLP/HTTP connections before the ingress does. A sparse signal (logs, typically) can leave its connection idle long enough for a load balancer to close it, and the agent then pays a failed request discovering it. The new
idle_conn_timeout, set below your ingress idle timeout, makes the agent close first and reconnect cleanly. Unset keeps the previous behaviour (the Go default of 90 seconds), so nothing changes until you configure it. -
Route a probe's logs to a specific output. A probe that produces logs sent them to every output that reads them; the
endpoints-style filtering that governs metrics did not apply. The new per-probelog_strategiescloses that:log_strategies: ["otlp"]sends that probe's records to OTLP and nowhere else. Omitting it keeps today's behaviour, so nothing changes until you configure it. Only outputs that can actually consume logs are accepted —agent config checkrejects anything else instead of letting the records go nowhere. -
Say who owns what a probe observes, and which application it belongs to. Governance (owner, criticality, location, lifecycle, labels) could only be stated for the host, in the OTLP output, or per device inside
snmp_poll. A database on a host had the host's governance, and so did the one next to it that belongs to another application. Every probe entry now takes an optionalgovernanceblock with the same vocabulary; it is stamped on the entities, metrics and logs of that probe only. The labelapplicationnames the application chain an instance takes part in, so one filter finds everything in the chain across hosts and probe types.agent config checkreports an unknown key or a value outside a closed set, and the Probes page edits the block below the parameters. The host's location (site, datacenter, rack, room) now also descends to what runs on the host, a database or a service reached on the loopback address; owner, criticality and labels never do. -
Read the console log stream of an Azure Container App (Pro). The new
azure_container_appsprobe reads stdout and stderr of every replica of an application through Azure Resource Manager, the same streamaz containerapp logs show --followreads, without a volume, a sidecar or any change to the container. Lines ride the log rail likefiletaillines, with the same parsers and multiline folding, and carry the application, revision, replica and container they came from. Replicas that appear and disappear are followed; the probe reports its open streams and reconnections so a broken stream shows in PRTG even when the logs go elsewhere.min_severityandexcludedrop the lines nobody reads before they leave the agent, and a counter shows what was left out. The probe also reports the reads left in the subscription's Azure Resource Manager budget (senhub.azure_container_apps.arm.reads_remaining), so a scan interval that eats into the quota is visible.
Fixes¶
-
A metric point is stamped with the moment it was measured, not the moment it left. A series in the store is re-published on every push cycle until it is evicted, and every copy carried the export time, so an old measurement was presented as a current one for up to a full
staleness_ttlwindow. An operator saw it on a target removed from anhttp_checkprobe: the series came back from the checkpoint after a restart and stayed red at 0, with fresh timestamps, on something nobody was querying. The datapoint now carries the time the probe observed it; a value the agent computes at export time, such as its own self-metrics, keeps the export time. Eviction is unchanged and still removes the series after the window. This has a consequence for probes that run less often than the push cycle, described under Breaking Changes. (#812) -
A container without a mounted volume now says so. The entrypoint has always warned when the state directory is not a mount, because without one the host identity, the agent key and the log bookmarks go with the container. Under Docker the warning could not fire: the image declared
VOLUMEon that directory, so Docker created an anonymous volume, the mount check was satisfied and nothing was said — while the volume was orphaned bydocker rmand the next run started as a new host anyway. The directive is gone. A named volume behaves as before and keeps its identity across adocker rm; a run without one is told what it is losing. -
A configured minimum TLS version now reaches the listener.
min_tls_versionwas parsed, defaulted, logged at startup and returned by the configuration API, and never applied:ServeTLSran on a server whoseTLSConfigwas nil. An operator who required TLS 1.3 was served by a socket that still accepted 1.2, with the agent's own log line telling them 1.3 was in force. A security control that is announced and not applied is worse than one that is absent, because it is the one nobody re-checks. -
A missing certificate no longer reports a listening HTTPS server. Enabling TLS without usable certificate files logged "HTTPS server listening", naming the configured minimum version, and only then discovered the file was absent. The port then refused every connection while the service still reported active: the console, the PRTG endpoint and the Nagios endpoint were gone with nothing on screen saying so. The default made it easy to reach, since
./certs/agent-cert.pemresolves against the unit's working directory while an install writes its certificates beside the configuration — the producer and the consumer never agreed on the path, and the log printed the relative form, which reads as the configuration directory. Both files are now checked before anything is announced, and the failure names the absolute paths. -
The console URL printed after an install carries the key. It said
http://localhost:8080/web/{agentkey}/dashboard, with the placeholder left in, two lines after generating the key. It is the last line an operator reads and the only one telling them where to go, and it could not be opened. -
A clean first install no longer opens with two warnings. Sealing inline secrets and migrating an inline licence both read the configuration. On a first install there is none yet — it is written a few lines later — so both failed and both warned, naming a missing file. Neither has anything to do when the file is absent, so neither runs.
-
uninstallsays how much it removed. It reported "removed 0 files and 3 directories" after deleting a tree holdingagent.yaml,license.jwt, the sealed secret store and the fragment directories: the counter reported the top-level file list whileRemoveAlltook everything under the directories without counting any of it. On a destructive command the summary is the one line an operator reads to confirm what happened, and "0 files" on a tree holding a licence token reads like nothing was removed. -
The Overview no longer scrolls sideways on a phone. Its probe table is wider than a 390px screen and pushed the whole document to 589px. Wrapping the table was not enough: the grid track holding the card would not shrink past it, since a bare
1frtrack isminmax(auto, 1fr)and its automatic minimum is the width of what it holds. The card grids now useminmax(0, 1fr)and the table scrolls inside its card. -
One spelling for "license", everywhere it is read. The Settings page, the Overview, the probe picker and the probe editor disagreed within a single session, and the API supplied part of it: the catalogue returned "requires a licence" as the reason string the picker renders verbatim. The Windows installer disagreed with itself too, on the Options page description, the licence-file label, and the file picker's title and filter.
-
A probe whose target is down is no longer shown as healthy. A database probe pointed at a host that refuses the connection was listed as running, counted under "Running", and left the Overview saying "0 failing", while the agent logged the refused ping and emitted
db.up = 0. Health was right: a refused connection is a successful collection reporting a down target. What was missing is the other half, and the probes already publish it, since 49 types emit asenhub.<area>.upseries. Such a probe now has its own state, "target down", naming the series it quotes, sorted to the top of the list and counted where an operator looks for what needs them. -
Deletions ask in the page. They used the browser's own dialog, the one interaction that left the console's style: unstyleable, blocking, and silently disabled for good once a viewer ticks Chrome's "prevent this page from creating more dialogs". Escape cancels, Enter confirms, and focus starts on Cancel.
-
The probe editor no longer contradicts itself. Validate reported the same missing field twice, once in the form's words and once in the schema's, so a blank PostgreSQL form announced seven problems for four empty fields. And nothing cleared as the operator typed: with all four filled, the page said "Ready to save" in green while still showing the red banner, the four field errors and a stale problem count. A verdict answers the values it was run on, so any edit now drops it.
-
A probe or output created disabled is no longer announced as started. Both said "the agent starts it on its own", in green, directly above a row reading "disabled".
-
The address bar names the tab on screen. Arriving on the HTTP output's Settings tab left the address reading
#urls, so a copied link or a plain reload landed on the other tab. The licence file control also drew its labels in the browser's language, putting French text in the middle of an English console; it is now the console's own button. -
A poller can no longer chart a measurement the agent never reported. The pull endpoints sent no cache directive at all. A response with neither a validator nor an expiry lets a client apply its own heuristic freshness, so PRTG, Nagios, a Prometheus scraper or any proxy between them was free to reuse an earlier answer, and nothing on either side would say so. Every route the HTTP output serves now says
no-store. The console's own assets keep revalidating, where caching is safe and useful. -
The sensor URL preview follows the URL instead of waiting for a click. Every choice on the Sensor URLs tab (poller, probe, tag filter, whether channel names carry the resource) changes what the poller would receive, so after any of them the panel below showed the answer to a URL no longer on screen, while reading as the current one. It now refreshes on its own, debounced so a tag filter picked value by value sends one request; the button remains as an explicit re-read and is named Refresh. The view also opens on the raw response rather than the channel table: it is what the poller actually receives, and the only view that shows a field the table does not carry. The table is one click away, and whichever is picked is the one that comes back next time.
-
A container host no longer drowns the agent's own log. On a machine that runs containers, veth interfaces appear and disappear constantly. The network probe enumerates the counters, then looks each interface up by name; between the two, a veth can be gone. That race is the normal state of a container host, not a failure, and it was reported straight to standard output with no level, no structured field and no respect for the configured verbosity. Measured on a fleet host with 26 containers and 23 container creations in 24 hours: 42 of the agent's 87 journal lines were this one message, 48 per cent of its output, all of a single kind. Two machines with 6 and 7 containers but little churn produced none, which matches the mechanism: it is the birth and death of the interfaces that produces the race, not their number. The vanish case is now a Debug line through the logger, any other failure to read an interface stays a Warn, and both carry the interface as a structured field.
-
The
depends_onwarning no longer tells root to run as root. An agent that sees outbound sockets and can name none of their owners said so and advised running as root — while its ownrunning_asfield readroot. The privilege answer is only half of it: mapping a socket to its owner reads/proc/<pid>/fd, and unreadable is one reason, absent is another. A container sharing another's network namespace sees that namespace's whole socket table while its/procholds only its own processes, so the owners do not exist in its view and no privilege conjures them. Reproduced on a host, agent as root, every socket unattributable. The message now tells each case what is actually true, and both still name the switch to turn the rail off. -
An omitted
cache:block is no longer a warning. It was reported on every call — not once at load — so an accepted default produced a recurring WARN, which is how operators learn to read warnings as noise. The default is now silent, and a configured retention is announced once, again only if a reload changes it. -
A list parameter written without its brackets is no longer read as nothing.
signals: metrics, logs, tracesis valid YAML and a string, not a list. The OTLP receiver discarded it and started on its default — metrics only — with nothing in the log to say the line had been ignored, while the sender gotUNIMPLEMENTEDfor the two signals it thought it had enabled. That reads like a missing feature; it was an option nobody had asked for. A value that cannot be read as a list is now refused at startup, naming the shape to write. Absent, or an explicitly empty list, still means metrics only.The same silence existed on every list parameter the probes read —
paths,channels,levels,sources,services,brokers,mib_paths,allowed_cidrs. They now report a value they cannot read instead of falling back without a word. -
The agent no longer refuses to run when it cannot watch its configuration. The watch exists so an edit is picked up without a restart. It can fail for reasons that have nothing to do with your file: on Linux, inotify has a per-user quota of 128 instances by default, and a host running k3s can hold most of it. The agent treated that as a fatal start, systemd restarted it five times, gave up, and the host stopped being monitored — because a convenience could not start.
It now starts, collects and exports normally, and says what it lost:
Surface What you see Log one WARN naming the cause and the consequence agent statusa Configuration NOT watched block with the reason Metrics senhub.agent.config.watch.disabled{reason}at 1, no series when the watch runsIn that state a configuration change needs an agent restart to apply. A configuration that cannot be loaded — a malformed file, a broken substitution — still fails the start, as before: that one is yours to fix, and systemd is right to report it.
-
The
dockerandswarmprobes now work on Windows. Docker Engine and Docker Desktop expose their API over a named pipe there, while the probes only ever dialled a Unix socket — so a Windows host running containers collected nothing at all. The default address is now the one the platform actually uses, andsocket_pathaccepts a pipe (npipe://./pipe/docker_engine). -
agent config checknow reads the values, not only the shape. It verified that a file parsed, that a probe type existed and that a strategy resolved, and never asked whether the values would be accepted — sopriority: 99was reported[OK]and the probe then refused to start, and a strategy key the agent rejects at construction passed the same way. The check now runs each probe's own validation (the probe is built and dropped: nothing is started, no socket or file is opened) and each strategy's validator.Three new lines you may see on a file that used to pass:
What Reported as A value the probe refuses ( priority: 99)[ERROR], and the check exits non-zeroA value the probe cannot read ( priority: "high")[WARN]— the probe starts on its default, which is not what the file saysA key the strategy never reads ( insecure: true)[ERROR], or a[WARN]naming the current spelling when there is oneIf you run
config checkin a pipeline, a file with one of these now fails it. That is the point — the agent was already behaving this way, silently. -
A parameter the agent cannot read no longer disappears without a word. The typed readers answered "absent" and "present but unreadable" identically, so every probe treated a mistyped value the way it treats a missing one: it used its default.
priority: "pas-un-nombre"started a journal reader on priority 7 and said nothing. The agent now warns at startup, naming the parameter, the value written and what was expected. -
uninstallcleans the machine even when there is no service unit to remove. The file cleanup was gated on the service removal succeeding, and the failure was swallowed: on a machine whose unit had already been removed by hand, or where an install never completed,uninstall --yesprinted nothing, removed nothing and exited 0 — leaving the sealed secret store, which holds the agent key and every credential the agent sealed. The removal is now reported when it fails, the files go either way, and the exit code says so. -
Uninstalling now removes the sealed secret store.
uninstallsaid "Cleanup completed" while leavingprobes.d/,strategies.d/and the secret store on disk — the latter holding the agent key and every credential the agent had sealed (database, BMC, API tokens). The whole installed configuration directory is now removed, and the confirmation says so before you answer, because it cannot be undone. A configuration you pointed at with--config-pathoutside the install directory is still treated narrowly: only that file is removed, since its neighbours are not ours to delete. -
A second agent on the same machine no longer destroys the first one's logs. Two agents resolved the same log file and each rotated it independently; whichever rotated first truncated the other's file, and its history was gone. A normal install keeps writing
senhubagent.logexactly as before — nothing to change in your log collection. A second instance, started with a different configuration file, now writes to its own. -
A registry URL with a path no longer breaks updates silently.
auto_update.urlis the base the agent appends to, and it appends two different things —/releases/…for the version list,/download/…for the artifacts. A value that already carried one of those paths made every derived URL double it, so the agent found nothing, changed nothing, and stayed on its installed version withauto_update.enabled: truestill in the file. The agent now corrects every shape anyone writes (/releases,/download, a pastedreleases.jsonURL, a trailing slash), on every code path rather than only when the configuration is read, andagent config checkreports the value with the one to write instead. The correction is also logged once rather than on every collection cycle. -
The OTLP log queue no longer fills with records that cannot be delivered. When the receiver refuses a batch for what it contains — a malformed payload, an attribute type it does not accept — resending it gets the same answer. Those records used to be written to the on-disk queue anyway, replayed at every restart and every recovery, refused again, and written back; they occupied space the queue then took from records that a retry would have delivered. They are now discarded once and counted, and the queue keeps doing what it is for: riding out an outage.
-
A batch the collector partly refuses is no longer treated as a batch it never received. OTLP lets a consumer accept a batch and keep only part of it, saying what it refused and why. The agent read that answer as a failed export: the delivered batch went to the on-disk log queue and was replayed at every restart and recovery, re-sending records the collector already had, while the refused ones were refused again and nothing counted them. The rejection is now separated from a real failure — the records the consumer refused are counted in the new
senhub.agent.export.rejected{strategy,signal}and the reason it gave is logged (once per minute per reason, not once per batch), while the batch stays out of the queue. -
MySQL and PostgreSQL answer to their documented parameters again. Both pages described the parameters of the paid probes they replaced when those probes moved to the free tier. An operator following them set
sslmode: requireon PostgreSQL and got a plaintext connection; setexpose_top_tableson MySQL and got nothing. Nothing warned, because a parameter a probe does not read is simply not read.
The old names work again — expose_per_database, expose_top_tables,
database, sslmode, sslrootcert, timeout — and the options that
had no equivalent at all are back: a query timeout on both probes, a
private CA and a verification switch on MySQL (tls now takes a block,
not just true), and a configurable replication lag threshold. The
pages list what the probes actually read, and agent config check
names the current spelling for a renamed parameter.
Three PostgreSQL parameters are not coming back: expose_per_database,
expose_top_tables and bloat_top_n asked for per-database and
per-table breakdowns this probe does not produce. agent config check
reports them as errors rather than letting them sit in a file looking
like they do something.
-
A lagging MySQL replica is no longer reported as healthy.
senhub.db.replication.healthchecked that both replica threads were running and stopped there, so a replica hours behind its source — stale reads, useless for failover — reported 1. Lag is part of the composite now, as the reference always said it was, abovemax_replication_lag_seconds(default 300, the threshold the PostgreSQL probe has always applied). Set it to0for a deliberately delayed replica. If you alert on this metric, a replica between your own tolerance and 300 seconds will change state at upgrade. -
Options that shipped without documentation now have some. A test reads the configuration keys the agent parses and fails the build when one appears nowhere in this guide, which found sixteen: the OTLP memory limit and durability settings, the metric cache cap, the push storages' own parameters, and per-probe options on
smart,nvidia,activemqandsnmp_poll. Each was a shipped feature nobody could find. -
A dead listener is now reported as unhealthy. The syslog, event and OTLP receiver probes wait for data to arrive rather than polling for it, so they had nothing to derive health from and reported healthy on every cycle — including cycles where their socket had been closed for hours because the port was taken at startup or the server stopped with an error on its own goroutine. They now report the state of the listener itself, so
senhub-agent statusandsenhub_agent_probes_healthytell you when one has stopped receiving. -
A probe can no longer ship untagged data by omission.
probe_nameandprobe_typeare now added centrally to every datapoint that does not already carry them. Previously each probe added them itself, and a probe that forgot produced series nothing could tell apart — and whose cache entries collided with another instance's. -
An intake outage can no longer grow the event backlog until the agent dies. The cloud metrics and PRTG outputs already capped what they hold when a destination is unreachable; the event output did not, and every failed send appended the whole batch to a list nothing trimmed. It is now bounded like the others: past the cap the oldest events go first, and the loss is counted rather than silent.
-
A rejected batch is no longer resent forever. When the intake refuses a batch of events for what it contains — a malformed payload, an unprocessable body — resending the same bytes gets the same answer. The agent now recognises that class, drops the batch once, and moves on, instead of pinning it at the head of the retry backlog where it blocked everything queued behind it and burned a round-trip (plus two seconds of retry sleep) on every cycle.
-
A failing output is now visible before it starts losing data. Until the backlog reaches its cap an output that cannot deliver sheds nothing, so no counter moved and the only trace was a log line. The new
senhub.agent.export.send.failed{strategy,reason}counts failed delivery attempts per output, withreasonsaying whether the batch was kept (transport) or dropped (validation,configuration). -
Stopping the agent no longer races itself. Every service — the configuration loader, the outputs, the probe pool, the auto-updater — now shares one cancellation, and each gets its own shutdown allowance instead of competing for a single five-second budget the first slow drain could consume entirely. In practice: an output that takes its time flushing its last batch no longer costs the probe pool its chance to close connections cleanly. The whole stop stays bounded (twenty seconds at worst) so systemd and the Windows service manager never escalate to a kill.
-
A configuration reload no longer leaves work behind. Probes and outputs recreated by a reload used to be started without any link to the agent's own shutdown, and relied entirely on being stopped individually; one that was not left a goroutine running for the life of the process. Long-lived agents whose configuration is edited regularly were the ones that accumulated them. A start/reload/stop cycle is now covered by a test that fails on a single leaked goroutine.
-
OTLP export failures are now visible per signal. A failing logs pipeline (for example a receiver rejecting every batch) previously moved no counter: the failure only surfaced as dead-letter queue churn. Failed exports now increment
senhub.agent.otlp.export.errorsfor every signal, the logs path logs a throttled warning naming the error, andsenhub-agent status --otlp, the web dashboard JSON (/info/otlp,export_errors_by_signal) and the exported metric carry the per-signal breakdown. refresh-unitno longer drops directives you added to the unit. A directive added inline tosenhub-agent.service(anEnvironmentFile=carrying an ingest token, anEnvironment=proxy setting) was silently removed when the unit was refreshed from the packaged template, which could leave an output starting without its credentials. Directives the template does not manage are now carried over into the refreshed unit, under a comment saying where they came from. Hardening directives,ExecStart,User=andGroup=are still decided by the refresh.- A configured output that fails to start is now visible. Until now
the only trace was one error line at boot: the agent kept running with
its remaining outputs, so a broken one could go unnoticed for as long as
nobody read the journal. A strategy that is configured but not running
is reported by
senhub-agent status, and exported assenhub.agent.strategy.failed{strategy,reason}(no series once every output runs). Fixing the configuration clears it on the next reload, without restarting the agent.
Breaking Changes¶
-
linux_logsandwindows_eventlogno longer puthost.nameon a log record. Both probes read a local journal, and both copied the hostname the source spells into a record-levelhost.name— the short kernel name on Linux, the uppercase computer name on Windows. The resource already carries the canonical FQDN, the same value the metrics use, so every record held that key twice with two values and which one survived was decided by the consumer. On a reporting tenant that split 22 machines into 35 identities, and a dashboard filtered on the FQDN showed a quarter of the lines with nothing saying so. The per-record value is kept, namespaced, the waysysloghas always emitted a remote sender's hostname.Before (record attribute) After (record attribute) host.name=edge-01(linux_logs)systemd.hostname=edge-01host.name=DASH483(windows_eventlog)winlog.computer=DASH483Migration: a query filtering log records on the short or uppercase name must move to the resource-level
host.name(the FQDN), which is also what joins logs to metrics. A query that needs the spelling the source used readssystemd.hostnameorwinlog.computer. Thesyslogprobe is untouched: itssyslog.hostnamegenuinely names another machine. -
senhub.agent.otlp.export.errorsgained asignalattribute. The unlabeled series is replaced by one series per failing signal; the former total is the sum oversignal. A signal that never failed emits no series (counter semantics: absence means zero).Before After senhub.agent.otlp.export.errors(no attributes)senhub.agent.otlp.export.errors{signal="metrics"\|"logs"\|"traces"}Migration: replace
senhub_agent_otlp_export_errors_totalin dashboards and alert rules bysum(senhub_agent_otlp_export_errors_total)(any-signal alerting) or pivot on the newsignallabel. Theexport_errors_totalfield of/info/otlpis unchanged. -
A series measured less often than the push cycle now yields one sample per measurement, not one per push. Since a point carries the time it was measured (see Fixes, #812), the copies re-published between two runs of a probe carry the same timestamp and a time-series database keeps one of them. Before, a probe running every 30 minutes produced a fresh sample every 30 seconds; now it produces one every 30 minutes. This changes what a consumer sees for
exec,os_updatesand any probe whoseintervalis longer than the push cycle: an instant query, or an alert rule with a short lookback, finds no sample once the last one is older than the database's staleness window (five minutes for Prometheus and VictoriaMetrics), and an alert on such a series resolves while the condition still holds.Migration: a rule or panel on a series measured every N minutes must look back at least N minutes. In PromQL, replace
m == 1bylast_over_time(m[1h]) == 1, with a range longer than the probe's interval. Series measured at or above the push cadence, which is the general case, are unaffected.A later version re-publishes a value as current for as long as its probe still vouches for it, so that a low-cadence gauge behaves as before without the timestamp of an orphan series ever running ahead of its last measurement (#890).
-
PRTG push channels are now named like pull channels. The same measurement reached PRTG under two different names depending on how it travelled: the agent pushed
cpu_core_usagewhile a PRTG pull showedCPU Core 0 Usage. Both now resolve the display name from the probe's definition, so one device has one channel set.Before (push) After (push and pull) cpu_core_usageCPU Core 0 Usagemem_used_percentMemory Used %PRTG treats a renamed channel as a new one, so a sensor fed by the push strategy will start fresh channels and lose the history attached to the old names. Plan the upgrade of push-fed sensors accordingly. Pull-fed sensors (the HTTP Data Advanced ones) are unaffected: their names do not change.
A probe that sets an explicit
prtg_metric_idstill names its own channel — that override is unchanged.