Skip to content

Architecture

transceiver-exporter is a single Go binary with no background workers, no caching layer, and no persistent state between scrapes. Everything happens synchronously, once, per HTTP request to the metrics path.

Request path

  1. HTTP request arrives at -web.telemetry-path (default /metrics).
  2. handleMetricsRequest (in main.go) builds a fresh prometheus.Registry and a fresh TransceiverCollector for this request only, parameterized by the current flag values (include/exclude lists and regexes, the interface-features toggle, and the dBm power-unit toggle). Nothing is reused or cached across requests.
  3. The registry's Collect invokes the collector, which:
  4. Enumerates interfaces via net.Interfaces(), applying the loopback-always-excluded rule and the configured include/exclude filters (see Configuration).
  5. Opens one ethtool handle (ethtool.NewEthtool(), from wobcom/go-ethtool) for the whole scrape.
  6. For each surviving interface, calls tool.NewInterface(name, true), which pulls both the driver info (ETHTOOL_GDRVINFO-equivalent) and the module EEPROM (get_module_info/get_module_eeprom) over SIOCETHTOOL ioctls.
  7. Converts what comes back into Prometheus gauges and writes them to the metrics channel.
  8. promhttp.HandlerFor serializes the registry's collected metrics into the Prometheus text exposition format and writes the HTTP response.

Because everything is per-request, a flag like -collector.optical-power-in-dbm or an interface filter takes effect on the very next scrape — there is nothing to restart or invalidate.

Failure isolation

Collection failures are handled at two levels, both non-fatal to the HTTP response:

  • Handle-level failure (opening ethtool itself fails — see Permissions) aborts the whole pass for that scrape; the response is still 200 OK, just empty of transceiver metrics.
  • Interface-level failure (one interface's NewInterface call errors) is logged and that interface is skipped; every other interface in the same scrape is unaffected.

Errors are carried on a dedicated Go channel (errs) back to the collector wrapper, which logs each one at ERROR level while metrics collection for the rest of the scrape continues on a separate goroutine — see the Collect method on transceiverCollectorWrapper in main.go.

Why per-scrape, not a polling loop

There is no interval, no ticker, and no in-memory metric cache: each scrape does the ioctl work fresh. This keeps the implementation simple and the data always current as of the scrape instant, at the cost of the ioctl work happening on the Prometheus scrape's own request latency rather than being amortized in the background. In practice this is cheap — the ioctl calls are local kernel calls with no network round trip — but it does mean a very short scrape timeout on a host with many interfaces could, in principle, be tighter than on an exporter that serves from a pre-populated cache.

Package layout

PackageRole
main (repo root)Flag parsing, HTTP server, wiring the collector into a promhttp handler.
transceiver-collectorThe prometheus.Collector implementation: interface enumeration/filtering, EEPROM-to-metric translation, and the mW→dBm conversion.
wobcom/go-ethtool (external dependency)The actual ethtool ioctl calls and EEPROM parsing this exporter is built on.