Instrumenting What You Can't Change
Half of Harborline will never call prometheus_client. Postgres, Redis, and nginx are other people's code — the bookings team can configure them but not edit them, and a client library is useless without somewhere to put it. The ecosystem's answer is the exporter: a sidecar process that speaks the target's native protocol, asks it what it knows, and republishes the answers as a /metrics endpoint.
Exporters cover most of the gap, not all of it. This topic adds the two fallbacks for when even an exporter falls short — probing from outside, and mining logs — and closes with the decision ladder that keeps a team from picking the wrong rung. By the end, every Harborline component has a rung and a port.
The Exporter Pattern
postgres_exporter on :9187 logs into db-01 and turns the pg_stat_* views into metrics. redis_exporter on :9121 runs INFO against cache-01. nginx-prometheus-exporter on :9113 reads web's stub_status page. Each becomes one more scrape job in obs-01's Prometheus, indistinguishable from a natively instrumented service once the samples land.
The nginx case shows the fidelity ceiling. stub_status is seven numbers — active connections, accepts, handled, total requests, and three connection states — so per-route latency from open-source nginx is not on this rung at any price. An exporter can only republish what its target chooses to expose; it translates, it does not add.
The Native Exception
RabbitMQ needs no sidecar at all: it has shipped its own rabbitmq_prometheus plugin since 3.8, serving :15692, so mq-01 is covered by a single rabbitmq-plugins enable rabbitmq_prometheus. The standing rule is to check for built-in Prometheus support before shopping for an exporter — more infrastructure grows it every year, and a first-party endpoint beats a third-party translation of the same numbers.
Blackbox Probing
blackbox_exporter on :9115 tests from outside: an HTTP probe against https://harborline.example/ asserting a 200 and a TLS certificate that is not near expiry, a TCP probe against db-01:5432. It sees zero internal state and exactly what a customer sees — which makes it the one rung that catches "every service reports healthy, site unreachable."
# prometheus.yml — probe the public site via blackbox_exporter
- job_name: "blackbox-http"
metrics_path: /probe
params: {module: [http_2xx]}
static_configs: [{targets: ["https://harborline.example/"]}]
relabel_configs:
- {source_labels: [__address__], target_label: __param_target}
- {source_labels: [__param_target], target_label: instance}
- {target_label: __address__, replacement: "obs-01:9115"}
The relabeling dance is mandatory. The listed target must not be scraped directly — the exporter must be scraped about it — so the config moves the URL into the target parameter, copies it into the instance label so graphs name the site rather than the prober, and points the actual scrape address at obs-01:9115. Skip any step and the job quietly probes nothing while reporting success.
Logs into Metrics, the Last Rung
When a system exposes nothing and accepts no probe worth having, its log lines can be counted: Loki metric queries promoted into recorded metrics through the ruler, or mtail and grok_exporter tailing files directly. It works, and it is coupled to log format — one upstream release that rewords a line silently zeroes the metric while everything else reports healthy. This rung ships with a ticket to climb off it.
The Decision Ladder
Native instrumentation, then official exporter, then blackbox probe, then log parsing — in that order. Each step down trades interior visibility for exterior observation, and a stable contract for fragile scraping: a client library reads the program's own state, an exporter reads what the target admits, a probe reads only the surface, and a log parser reads whatever a formatting decision left behind.
Placing Harborline on the ladder takes one paragraph. The Python services — bookings, search, payments, notifier — sit on rung one with prometheus_client. Postgres, Redis, and nginx sit on rung two behind their exporters, while mq-01 joins rung one courtesy of its built-in plugin. The public checkout URL gets a rung-three blackbox probe, and nothing, yet, lives on rung four — a state worth defending.
bookings, search, mq-01's plugin→Native instrumentationpg_stat_*, INFO, stub_status→Official exporter- Treating redis_exporter as a substitute for instrumenting the Redis client — the exporter sees
cache-01's server-side view, and anything that constrains a client from its own side would be invisible from the server; topic 23's pool gauges cover what no exporter can, a distinction that will matter later in the book. - Running postgres_exporter as a superuser because the first attempt hit a permissions error — a monitoring credential with full write access to the bookings database; the
pg_monitorrole exists for exactly this and needs nothing more. - Wiring blackbox_exporter without the relabeling step — every probe's
instancelabel is the exporter itself, targets never receive traffic, andprobe_successstays 1 while the real endpoint could be down for a week. - Alerting on the exporter's
upand calling the database monitored —upsays the exporter answered; the exporter's own connection metric (pg_up,redis_up) says whether the database did, and the two disagree during every database outage. - Parsing nginx access logs for request counts that stub_status already serves — a fragile regex pipeline maintained forever to produce a number available one rung up for free.
- Check for native Prometheus support before deploying any exporter — RabbitMQ's built-in plugin on :15692 replaces a whole sidecar with one enable command.
- Grant every exporter a least-privilege monitoring role —
pg_monitorfor postgres_exporter — and treat a monitoring credential that can write data as a finding, not a shortcut. - Probe every public endpoint with blackbox_exporter and alert on
probe_successandprobe_ssl_earliest_cert_expiry— certificate expiry is the most preventable outage in the industry. - Mark every log-derived metric as temporary, with a ticket to move the number up the ladder — rung four is a tourniquet, not a treatment.
Knowledge Check
redis_exporter is scraping cache-01 and all its metrics look healthy. What can it still not see?
- Redis memory usage, which only the INFO command exposes
- Client-side state, like a full connection pool inside
bookings - Command throughput, because the exporter samples too slowly to count operations
- Whether the Redis server is reachable at all
During a Postgres outage, up{job="postgres"} stays 1. Why?
- Prometheus caches the last successful scrape result for the staleness window
uptracks the exporter, and the exporter is still running- The exporter fails over to a standby replica automatically and keeps reporting
- A scrape must fail three consecutive times before up transitions to 0
What does blackbox probing catch that no interior instrumentation can?
- Slow database queries inside db-01
- Connection pool saturation inside bookings before it turns into latency
- The site being unreachable while every service reports healthy
- Which route is producing the most 5xx responses
In the blackbox scrape job, what does the relabeling actually do?
- Sends the scrape to the exporter while passing the real target as a parameter
- Filters out noisy probe metrics before they reach the TSDB
- Sets a slower probe schedule so external endpoints are not hammered by constant probes
- Chooses whether the probe speaks HTTP or TCP for each target
A team parses an application's log lines to produce a request-rate metric. What is the characteristic failure?
- The parsing pipeline consumes more CPU than the application itself under peak load
- Log-derived metrics always explode cardinality
- An upstream release rewords the line and the metric silently drops to zero
- Loki's ruler cannot turn log queries into recorded metrics
You got correct