Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Prometheus and Grafana integration

The hotpath-prometheus feature exposes every profiling subsystem as Prometheus metrics on a dedicated GET /metrics endpoint. Point a Prometheus scraper at it and build Grafana dashboards on top of any of the performance signals measured by the library.

Configure prometheus metrics endpoint

Add hotpath-prometheus feature forwarding:

[dependencies]
hotpath = "0.25"

[features]
hotpath = ["hotpath/hotpath"]
hotpath-prometheus = ["hotpath/hotpath-prometheus"]
cargo run --features='hotpath,hotpath-prometheus'

The exporter starts automatically with the profiler on 127.0.0.1:6772 (customizable with HOTPATH_PROMETHEUS_PORT, HOTPATH_PROMETHEUS_HOST). Verify it with:

curl http://127.0.0.1:6772/metrics

Prometheus scrape config

Minimal prometheus.yml:

global:
  scrape_interval: 5s

scrape_configs:
  - job_name: hotpath
    scrape_native_histograms: true
    static_configs:
      - targets: ["127.0.0.1:6772"]

With scrape_native_histograms: true Prometheus negotiates the protobuf format and ingests high-resolution native histograms. Without it, the exporter serves the text format with coarse classic buckets.

The two representations are queried differently. A native histogram is a single series under the bare metric name, e.g. hotpath_function_duration_seconds. A classic histogram is a set of float series with the _bucket, _sum and _count suffixes. When Prometheus ingests the native part, it drops the classic part, so the suffixed series do not exist. Add always_scrape_classic_histograms: true to the scrape config to store both.

Authentication

Set HOTPATH_PROMETHEUS_AUTH_TOKEN and every request must carry it in the Authorization header, either bare or Bearer-prefixed. This matches Prometheus’ authorization scrape config:

global:
  scrape_interval: 5s

scrape_configs:
  - job_name: hotpath
    scrape_native_histograms: true
    authorization:
      credentials: your-secret-token
    static_configs:
      - targets: ["127.0.0.1:6772"]

The token travels in plaintext: it guards against other local processes and accidental exposure, not a substitute for TLS.

Grafana

Add Prometheus as a data source and query the metrics with PromQL. The histogram queries below assume native histograms (the scrape_native_histograms: true config above); the classic equivalents follow.

# p99 function duration
histogram_quantile(0.99, sum by (function) (rate(hotpath_function_duration_seconds[1m])))

# Average function duration
histogram_sum(rate(hotpath_function_duration_seconds[1m])) / histogram_count(rate(hotpath_function_duration_seconds[1m]))

# Calls per second per function
rate(hotpath_function_calls_total[1m])

# Allocation rate per function
rate(hotpath_function_alloc_bytes_total[1m])

# SQL queries per served request, per route
rate(hotpath_server_sql_calls_total[5m]) / rate(hotpath_server_scoped_requests_total[5m])

# I/O throughput that stays correct under time sampling
rate(hotpath_io_sampled_bytes_total[1m]) / histogram_sum(rate(hotpath_io_op_seconds[1m]))

# Average future poll duration
rate(hotpath_future_poll_seconds_total[1m]) / rate(hotpath_future_sampled_polls_total[1m])

Durations are exported in seconds. Counters are cumulative since profiling started, so use rate() / increase() in queries.

Classic histogram queries

Without scrape_native_histograms (or with always_scrape_classic_histograms: true) histograms are stored as _bucket, _sum and _count series. Quantiles need the le label and the _bucket suffix, and sums and counts are plain series instead of histogram_sum() / histogram_count():

# p99 function duration
histogram_quantile(0.99, sum by (function, le) (rate(hotpath_function_duration_seconds_bucket[1m])))

# Average function duration
rate(hotpath_function_duration_seconds_sum[1m]) / rate(hotpath_function_duration_seconds_count[1m])

# I/O throughput that stays correct under time sampling
rate(hotpath_io_sampled_bytes_total[1m]) / rate(hotpath_io_op_seconds_sum[1m])

Classic buckets are coarse, log-spaced 1-3 steps, so quantiles from them are rough estimates. Prefer native histograms when accuracy matters.

Time sampling

With time sampling enabled, *_total call counters still count every call, while duration histograms only contain the sampled ones. Their count (histogram_count(), or the classic _count series) is the number of timed calls, so averages derived from sum / count stay correct.

Available metrics

Families with no data are omitted from the scrape, e.g. lock metrics appear only when a mutex! or rw_lock! wrapper has been used.

Process

MetricTypeLabelsDescription
hotpath_build_infogaugehotpath_version, rustc_version, profile, osAlways 1, build metadata lives in the labels
hotpath_uptime_secondsgaugeSeconds since profiling started

hotpath_build_info describes the binary that produced the scrape:

hotpath_build_info{hotpath_version="0.24.0",rustc_version="1.97.0",profile="release",os="linux-x86_64"} 1
  • hotpath_version / rustc_version - crate and compiler versions the binary was built with.
  • profile - the Cargo profile the binary was built with: debug, release, or a custom profile’s name such as profiling.
  • os - OS-ARCH pair of the running process, e.g. linux-x86_64 or macos-aarch64.

Functions

Requires #[hotpath::measure] / #[hotpath::measure_all] instrumentation.

MetricTypeLabelsDescription
hotpath_function_calls_totalcounterfunctionTotal calls, including calls skipped by time sampling
hotpath_function_duration_secondshistogramfunctionDuration of sampled calls

With the hotpath-alloc feature:

MetricTypeLabelsDescription
hotpath_function_alloc_bytes_totalcounterfunctionTotal bytes allocated
hotpath_function_alloc_count_totalcounterfunctionTotal allocations
hotpath_function_alloc_byteshistogramfunctionBytes allocated per call
hotpath_function_alloc_counthistogramfunctionAllocations per call

The two per-call histograms are omitted for async entries whose measurements carry no per-call totals.

SQL queries

Requires a SQL tracing integration. Series are keyed by normalized query text; queries longer than HOTPATH_MAX_LOG_LEN are truncated with a hash suffix so distinct queries never collapse into one series.

MetricTypeLabelsDescription
hotpath_sql_queries_totalcounterquery, source, routeTotal executions
hotpath_sql_duration_secondshistogramquery, source, routeQuery duration

source is the innermost instrumented caller, route the axum route handling the request (see route scoping). Aggregate with sum by (query) for the per-query view.

HTTP client requests

Requires hotpath::http!(client).

MetricTypeLabelsDescription
hotpath_http_requests_totalcounterendpoint, source, routeTotal outbound requests per normalized endpoint
hotpath_http_errors_totalcounterendpoint, source, routeTransport errors plus responses with status >= 400
hotpath_http_duration_secondshistogramendpoint, source, routeRequest duration

HTTP server (axum)

Requires hotpath::axum!(router).

MetricTypeLabelsDescription
hotpath_server_requests_totalcounterrouteTotal requests per matched route template
hotpath_server_responses_totalcounterroute, classResponses with a 4xx or 5xx status
hotpath_server_duration_secondshistogramrouteDuration until the response head is produced
hotpath_server_scoped_requests_totalcounterrouteCompleted requests that carried a route scope
hotpath_server_sql_calls_totalcounterrouteSQL queries issued by route-scoped requests
hotpath_server_http_calls_totalcounterrouteOutbound HTTP requests issued by route-scoped requests

Divide the SQL and HTTP call counters by hotpath_server_scoped_requests_total for per-request rates.

Locks

Requires hotpath::mutex! / hotpath::rw_lock! wrappers. Call-site labels: source is the file:line:column of the wrapper macro, label the user-provided label (empty when unset), iter the instantiation index for call sites that create several instances.

MetricTypeLabelsDescription
hotpath_mutex_acquisitions_totalcountersource, label, iterTotal acquisitions, including those skipped by time sampling
hotpath_mutex_wait_secondshistogramsource, label, iterTime spent waiting to acquire
hotpath_mutex_acquire_secondshistogramsource, label, iterTime the lock was held
hotpath_rwlock_acquisitions_totalcountersource, label, iter, opTotal acquisitions per side (op = read / write)
hotpath_rwlock_wait_secondshistogramsource, label, iter, opTime spent waiting to acquire, per side
hotpath_rwlock_acquire_secondshistogramsource, label, iter, opTime the lock was held, per side

Channels

Requires hotpath::channel! wrappers. type is the channel kind (bounded[N], unbounded, oneshot), payload the message type name.

MetricTypeLabelsDescription
hotpath_channel_sent_totalcountersource, label, iter, type, payloadMessages sent
hotpath_channel_received_totalcountersource, label, iter, type, payloadMessages received
hotpath_channel_instances_created_totalcountersource, label, iter, type, payloadInstances created at this call site since start
hotpath_channel_instances_closed_totalcountersource, label, iter, type, payloadInstances that have closed
hotpath_channel_queue_sizegaugesource, label, iter, type, payloadMessages sent but not yet received
hotpath_channel_max_queue_sizegaugesource, label, iter, type, payloadSince-start high-water mark of the queue size
hotpath_channel_proc_secondshistogramsource, label, iter, type, payloadDelay between send and sampled receive (wrap mode only)

Streams

Requires hotpath::stream! wrappers.

MetricTypeLabelsDescription
hotpath_stream_items_totalcountersource, label, iter, payloadItems yielded
hotpath_stream_instances_created_totalcountersource, label, iter, payloadInstances created at this call site since start
hotpath_stream_instances_closed_totalcountersource, label, iter, payloadInstances that have closed

Subtract the closed counter from the created one for the number of instances currently alive at a call site.

Futures

Requires #[hotpath::measure(future = true)] on the async function. source is the function path.

MetricTypeLabelsDescription
hotpath_future_polls_totalcountersource, labelTotal polls, including polls skipped by time sampling
hotpath_future_sampled_polls_totalcountersource, labelTimed polls, the denominator for the average poll duration
hotpath_future_poll_seconds_totalcountersource, labelTime spent in timed polls
hotpath_future_poll_alloc_bytes_totalcountersource, labelBytes allocated during polls (requires hotpath-alloc)
hotpath_future_poll_allocs_totalcountersource, labelAllocations during polls (requires hotpath-alloc)

I/O

Requires hotpath::io! wrappers. type is the wrapped type name, op one of read, write, flush, shutdown. Op kinds a wrapper never touched are not exported.

MetricTypeLabelsDescription
hotpath_io_ops_totalcountersource, label, iter, type, opTotal operations, including ops skipped by time sampling
hotpath_io_bytes_totalcountersource, label, iter, type, opTotal bytes transferred
hotpath_io_sampled_bytes_totalcountersource, label, iter, type, opBytes transferred by timed operations
hotpath_io_errors_totalcountersource, label, iter, type, opOperations that returned an error
hotpath_io_op_secondshistogramsource, label, iter, type, opDuration of sampled operations

Threads

Requires the threads feature. Per-thread series cover live threads only; a thread’s series goes stale after it exits, while its allocations stay in the process-level totals.

MetricTypeLabelsDescription
hotpath_threadsgaugeThreads in the most recent monitor sample
hotpath_rss_bytesgaugeResident set size of the process
hotpath_thread_cpu_percentgaugename, tidCPU usage over the last monitor interval
hotpath_thread_cpu_percent_maxgaugename, tidSince-start peak CPU usage
hotpath_thread_cpu_percent_avggaugename, tidLifetime average CPU usage
hotpath_thread_cpu_seconds_totalcountername, tid, modeCPU time per thread (mode = user / sys)
hotpath_thread_alloc_bytes_totalcountername, tidBytes allocated per thread (requires hotpath-alloc)
hotpath_thread_dealloc_bytes_totalcountername, tidBytes deallocated per thread (requires hotpath-alloc)
hotpath_alloc_bytes_totalcounterBytes allocated by the process, exited threads included (requires hotpath-alloc)
hotpath_dealloc_bytes_totalcounterBytes deallocated by the process, exited threads included (requires hotpath-alloc)

Tokio runtime

Requires the tokio feature and hotpath::tokio_runtime!(). Some series are only available when Tokio’s unstable (RUSTFLAGS="--cfg tokio_unstable") metrics are enabled.

MetricTypeLabelsDescription
hotpath_tokio_workersgaugeWorker threads
hotpath_tokio_alive_tasksgaugeTasks currently alive
hotpath_tokio_global_queue_depthgaugeTasks waiting in the global injection queue
hotpath_tokio_blocking_threadsgaugeThreads in the blocking pool
hotpath_tokio_idle_blocking_threadsgaugeIdle threads in the blocking pool
hotpath_tokio_blocking_queue_depthgaugeTasks waiting for the blocking pool
hotpath_tokio_spawned_tasks_totalcounterTasks spawned since start
hotpath_tokio_remote_schedules_totalcounterTasks scheduled from outside the runtime
hotpath_tokio_io_fd_registered_totalcounterFile descriptors registered with the io driver
hotpath_tokio_io_fd_deregistered_totalcounterFile descriptors deregistered from the io driver
hotpath_tokio_io_ready_events_totalcounterReadiness events delivered by the io driver
hotpath_tokio_worker_parks_totalcounterworkerTimes each worker parked
hotpath_tokio_worker_busy_seconds_totalcounterworkerTime each worker spent executing tasks
hotpath_tokio_worker_polls_totalcounterworkerTasks polled by each worker
hotpath_tokio_worker_steals_totalcounterworkerTasks stolen from other workers’ queues
hotpath_tokio_worker_local_queue_depthgaugeworkerTasks waiting in each worker’s local queue

Gauges

Requires hotpath::gauge! entries.

MetricTypeLabelsDescription
hotpath_gaugegaugekeyCurrent value
hotpath_gauge_mingaugekeySince-start minimum
hotpath_gauge_maxgaugekeySince-start maximum
hotpath_gauge_updates_totalcounterkeyUpdates applied

Configuration

Environment VariableDefaultDescription
HOTPATH_PROMETHEUS_PORT6772Port the exporter listens on
HOTPATH_PROMETHEUS_HOST127.0.0.1Bind address; set to 0.0.0.0 when a Prometheus container must reach the exporter through the Docker bridge
HOTPATH_PROMETHEUS_AUTH_TOKEN-Optional token required in the Authorization header, bare or Bearer-prefixed

Example:

HOTPATH_PROMETHEUS_PORT=9100 HOTPATH_PROMETHEUS_AUTH_TOKEN=secret123 \
cargo run --features='hotpath,hotpath-prometheus'