From daa1626eaf705318013b708954499b0a049088c8 Mon Sep 17 00:00:00 2001 From: Krishna <107162115+k1chik@users.noreply.github.com> Date: Thu, 9 Apr 2026 14:57:24 -0400 Subject: [PATCH 1/5] docs: add API reference for all metric types (#1159) * docs: add API reference for all metric types Adds constructor parameter tables, method documentation, and runnable real-world examples for Counter, Gauge, Histogram, Summary, Info, and Enum. The _index.md quick-pick table now covers all six types. Also fixes labels.md which was missing remove(), remove_by_labels(), and clear() -- the metric pages were already linking to it for those methods. Closes #1021 Signed-off-by: k1chik <107162115+k1chik@users.noreply.github.com> * docs: address review feedback on counter example and table header Revert quick-start counter example to use 'my_failures' (no _total suffix) since the library appends it automatically. Rename 'Value goes' column to 'Behavior' in the metric type overview table for clarity across all six types. Signed-off-by: k1chik <107162115+k1chik@users.noreply.github.com> * docs: rename table column to 'Update model' Signed-off-by: k1chik <107162115+k1chik@users.noreply.github.com> --------- Signed-off-by: k1chik <107162115+k1chik@users.noreply.github.com> --- docs/content/instrumenting/_index.md | 16 ++- docs/content/instrumenting/counter.md | 109 ++++++++++++++++-- docs/content/instrumenting/enum.md | 88 +++++++++++++- docs/content/instrumenting/gauge.md | 146 ++++++++++++++++++++++-- docs/content/instrumenting/histogram.md | 116 +++++++++++++++++-- docs/content/instrumenting/info.md | 75 +++++++++++- docs/content/instrumenting/labels.md | 33 +++++- docs/content/instrumenting/summary.md | 97 +++++++++++++++- 8 files changed, 638 insertions(+), 42 deletions(-) diff --git a/docs/content/instrumenting/_index.md b/docs/content/instrumenting/_index.md index 13bbc6b6..1b013d58 100644 --- a/docs/content/instrumenting/_index.md +++ b/docs/content/instrumenting/_index.md @@ -3,10 +3,20 @@ title: Instrumenting weight: 2 --- -Four types of metric are offered: Counter, Gauge, Summary and Histogram. -See the documentation on [metric types](http://prometheus.io/docs/concepts/metric_types/) +Six metric types are available. Pick based on what your value does: + +| Type | Update model | Use for | +|------|-----------|---------| +| [Counter](counter/) | only up | requests served, errors, bytes sent | +| [Gauge](gauge/) | up and down | queue depth, active connections, memory usage | +| [Histogram](histogram/) | observations in buckets | request latency, request size — when you need quantiles in queries | +| [Summary](summary/) | observations (count + sum) | request latency, request size — when average is enough | +| [Info](info/) | static key-value pairs | build version, environment metadata | +| [Enum](enum/) | one of N states | task state, lifecycle phase | + +See the Prometheus documentation on [metric types](https://prometheus.io/docs/concepts/metric_types/) and [instrumentation best practices](https://prometheus.io/docs/practices/instrumentation/#counter-vs-gauge-summary-vs-histogram) -on how to use them. +for deeper guidance on choosing between Histogram and Summary. ## Disabling `_created` metrics diff --git a/docs/content/instrumenting/counter.md b/docs/content/instrumenting/counter.md index 94618025..4876b612 100644 --- a/docs/content/instrumenting/counter.md +++ b/docs/content/instrumenting/counter.md @@ -3,8 +3,10 @@ title: Counter weight: 1 --- -Counters go up, and reset when the process restarts. +A Counter tracks a value that only ever goes up. Use it for things you count — requests +served, errors raised, bytes sent. When the process restarts, the counter resets to zero. +If your value can go down, use a [Gauge](../gauge/) instead. ```python from prometheus_client import Counter @@ -18,17 +20,110 @@ exposing the time series for counter, a `_total` suffix will be added. This is for compatibility between OpenMetrics and the Prometheus text format, as OpenMetrics requires the `_total` suffix. -There are utilities to count exceptions raised: +## Constructor + +```python +Counter(name, documentation, labelnames=(), namespace='', subsystem='', unit='', registry=REGISTRY) +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `name` | `str` | required | Metric name. A `_total` suffix is appended automatically when exposing the time series. | +| `documentation` | `str` | required | Help text shown in the `/metrics` output and Prometheus UI. | +| `labelnames` | `Iterable[str]` | `()` | Names of labels for this metric. See [Labels](../labels/). | +| `namespace` | `str` | `''` | Optional prefix. | +| `subsystem` | `str` | `''` | Optional middle component. | +| `unit` | `str` | `''` | Optional unit suffix appended to the metric name. | +| `registry` | `CollectorRegistry` | `REGISTRY` | Registry to register with. Pass `None` to skip registration, which is useful in tests where you create metrics without wanting them in the global registry. | + +`namespace`, `subsystem`, and `name` are joined with underscores to form the full metric name: + +```python +# namespace='myapp', subsystem='http', name='requests_total' +# produces: myapp_http_requests_total +Counter('requests_total', 'Total requests', namespace='myapp', subsystem='http') +``` + +## Methods + +### `inc(amount=1, exemplar=None)` + +Increment the counter by the given amount. The amount must be non-negative. + +```python +c.inc() # increment by 1 +c.inc(5) # increment by 5 +c.inc(0.7) # fractional increments are allowed +``` + +To attach trace context to an observation, pass an `exemplar` dict. Exemplars are +only rendered in OpenMetrics format. See [Exemplars](../exemplars/) for details. + +```python +c.inc(exemplar={'trace_id': 'abc123'}) +``` + +### `reset()` + +Reset the counter to zero. Use this when a logical process restarts without +restarting the actual Python process. + +```python +c.reset() +``` + +### `count_exceptions(exception=Exception)` + +Count exceptions raised in a block of code or function. Can be used as a +decorator or context manager. Increments the counter each time an exception +of the given type is raised. ```python @c.count_exceptions() def f(): - pass + pass with c.count_exceptions(): - pass + pass -# Count only one type of exception +# Count only a specific exception type with c.count_exceptions(ValueError): - pass -``` \ No newline at end of file + pass +``` + +## Labels + +See [Labels](../labels/) for how to use `.labels()`, `.remove()`, `.remove_by_labels()`, and `.clear()`. + +## Real-world example + +Tracking HTTP requests by method and status code in a web application: + +```python +from prometheus_client import Counter, start_http_server + +REQUESTS = Counter( + 'requests_total', + 'Total HTTP requests received', + labelnames=['method', 'status'], + namespace='myapp', +) +EXCEPTIONS = Counter( + 'exceptions_total', + 'Total unhandled exceptions', + labelnames=['handler'], + namespace='myapp', +) + +def handle_request(method, handler): + with EXCEPTIONS.labels(handler=handler).count_exceptions(): + # ... process the request ... + status = '200' + REQUESTS.labels(method=method, status=status).inc() + +if __name__ == '__main__': + start_http_server(8000) # exposes metrics at http://localhost:8000/metrics + # ... start your application ... +``` + +This produces time series like `myapp_requests_total{method="GET",status="200"}`. diff --git a/docs/content/instrumenting/enum.md b/docs/content/instrumenting/enum.md index 102091a1..b1e6169a 100644 --- a/docs/content/instrumenting/enum.md +++ b/docs/content/instrumenting/enum.md @@ -3,11 +3,95 @@ title: Enum weight: 6 --- -Enum tracks which of a set of states something is currently in. +Enum tracks which of a fixed set of states something is currently in. Only one state is active at a time. Use it for things like task state machines or lifecycle phases. ```python from prometheus_client import Enum e = Enum('my_task_state', 'Description of enum', states=['starting', 'running', 'stopped']) e.state('running') -``` \ No newline at end of file +``` + +Enum exposes one time series per state: +- `{=""}` — 1 if this is the current state, 0 otherwise + +The first listed state is the default. + +Note: Enum metrics do not work in multiprocess mode. + +## Constructor + +```python +Enum(name, documentation, labelnames=(), namespace='', subsystem='', unit='', registry=REGISTRY, states=[]) +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `name` | `str` | required | Metric name. | +| `documentation` | `str` | required | Help text shown in the `/metrics` output and Prometheus UI. | +| `labelnames` | `Iterable[str]` | `()` | Names of labels for this metric. See [Labels](../labels/). The metric name itself cannot be used as a label name. | +| `namespace` | `str` | `''` | Optional prefix. | +| `subsystem` | `str` | `''` | Optional middle component. | +| `unit` | `str` | `''` | Not supported — raises `ValueError`. Enum metrics cannot have a unit. | +| `registry` | `CollectorRegistry` | `REGISTRY` | Registry to register with. Pass `None` to skip registration, which is useful in tests where you create metrics without wanting them in the global registry. | +| `states` | `List[str]` | required | The complete list of valid states. Must be non-empty. The first entry is the initial state. | + +`namespace`, `subsystem`, and `name` are joined with underscores to form the full metric name: + +```python +# namespace='myapp', subsystem='worker', name='state' +# produces: myapp_worker_state +Enum('state', 'Worker state', states=['idle', 'running', 'error'], namespace='myapp', subsystem='worker') +``` + +## Methods + +### `state(state)` + +Set the current state. The value must be one of the strings passed in the `states` list. Raises `ValueError` if the state is not recognized. + +```python +e.state('running') +e.state('stopped') +``` + +## Labels + +See [Labels](../labels/) for how to use `.labels()`, `.remove()`, `.remove_by_labels()`, and `.clear()`. + +## Real-world example + +Tracking the lifecycle state of a background worker: + +```python +from prometheus_client import Enum, start_http_server + +WORKER_STATE = Enum( + 'worker_state', + 'Current state of the background worker', + states=['idle', 'running', 'error'], + namespace='myapp', +) + +def process_job(): + WORKER_STATE.state('running') + try: + # ... do work ... + pass + except Exception: + WORKER_STATE.state('error') + raise + finally: + WORKER_STATE.state('idle') + +if __name__ == '__main__': + start_http_server(8000) # exposes metrics at http://localhost:8000/metrics + # ... start your application ... +``` + +This produces: +``` +myapp_worker_state{myapp_worker_state="idle"} 0.0 +myapp_worker_state{myapp_worker_state="running"} 1.0 +myapp_worker_state{myapp_worker_state="error"} 0.0 +``` diff --git a/docs/content/instrumenting/gauge.md b/docs/content/instrumenting/gauge.md index 0b1529e9..43168a68 100644 --- a/docs/content/instrumenting/gauge.md +++ b/docs/content/instrumenting/gauge.md @@ -3,7 +3,8 @@ title: Gauge weight: 2 --- -Gauges can go up and down. +A Gauge tracks a value that can go up and down. Use it for things you sample at a +point in time — active connections, queue depth, memory usage, temperature. ```python from prometheus_client import Gauge @@ -13,24 +14,145 @@ g.dec(10) # Decrement by given value g.set(4.2) # Set to a given value ``` -There are utilities for common use cases: +## Constructor ```python -g.set_to_current_time() # Set to current unixtime +Gauge(name, documentation, labelnames=(), namespace='', subsystem='', unit='', registry=REGISTRY, multiprocess_mode='all') +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `name` | `str` | required | Metric name. | +| `documentation` | `str` | required | Help text shown in the `/metrics` output and Prometheus UI. | +| `labelnames` | `Iterable[str]` | `()` | Names of labels for this metric. See [Labels](../labels/). | +| `namespace` | `str` | `''` | Optional prefix. | +| `subsystem` | `str` | `''` | Optional middle component. | +| `unit` | `str` | `''` | Optional unit suffix appended to the metric name. | +| `registry` | `CollectorRegistry` | `REGISTRY` | Registry to register with. Pass `None` to skip registration, which is useful in tests where you create metrics without wanting them in the global registry. | +| `multiprocess_mode` | `str` | `'all'` | How to aggregate this gauge across multiple processes. See [Multiprocess mode](../../multiprocess/). Options: `all`, `liveall`, `min`, `livemin`, `max`, `livemax`, `sum`, `livesum`, `mostrecent`, `livemostrecent`. | + +`namespace`, `subsystem`, and `name` are joined with underscores to form the full metric name: + +```python +# namespace='myapp', subsystem='db', name='connections_active' +# produces: myapp_db_connections_active +Gauge('connections_active', 'Active DB connections', namespace='myapp', subsystem='db') +``` + +## Methods + +### `inc(amount=1)` + +Increment the gauge by the given amount. + +```python +g.inc() # increment by 1 +g.inc(3) # increment by 3 +``` + +Note: raises `RuntimeError` if `multiprocess_mode` is `mostrecent` or `livemostrecent`. + +### `dec(amount=1)` + +Decrement the gauge by the given amount. + +```python +g.dec() # decrement by 1 +g.dec(3) # decrement by 3 +``` + +Note: raises `RuntimeError` if `multiprocess_mode` is `mostrecent` or `livemostrecent`. + +### `set(value)` + +Set the gauge to the given value. + +```python +g.set(42.5) +``` + +### `set_to_current_time()` + +Set the gauge to the current Unix timestamp in seconds. Useful for tracking +when an event last occurred. -# Increment when entered, decrement when exited. +```python +g.set_to_current_time() +``` + +### `track_inprogress()` + +Increment the gauge when a block of code or function is entered, and decrement +it when exited. Can be used as a decorator or context manager. + +```python @g.track_inprogress() -def f(): - pass +def process_job(): + pass with g.track_inprogress(): - pass + pass +``` + +### `time()` + +Set the gauge to the duration in seconds of the most recent execution of a +block of code or function. Unlike `Histogram.time()` and `Summary.time()`, +which accumulate all observations, this overwrites the gauge with the latest +duration each time. Can be used as a decorator or context manager. + +```python +@g.time() +def process(): + pass + +with g.time(): + pass +``` + +### `set_function(f)` + +Bind a callback function that returns the gauge value. The function is called +each time the metric is scraped. All other methods become no-ops after calling +this. + +```python +queue = [] +g.set_function(lambda: len(queue)) ``` -A Gauge can also take its value from a callback: +## Labels + +See [Labels](../labels/) for how to use `.labels()`, `.remove()`, `.remove_by_labels()`, and `.clear()`. + +## Real-world example + +Tracking active database connections and queue depth: ```python -d = Gauge('data_objects', 'Number of objects') -my_dict = {} -d.set_function(lambda: len(my_dict)) -``` \ No newline at end of file +from prometheus_client import Gauge, start_http_server + +ACTIVE_CONNECTIONS = Gauge( + 'connections_active', + 'Number of active database connections', + namespace='myapp', +) +QUEUE_SIZE = Gauge( + 'job_queue_size', + 'Number of jobs waiting in the queue', + namespace='myapp', +) + +job_queue = [] +QUEUE_SIZE.set_function(lambda: len(job_queue)) + +def acquire_connection(): + ACTIVE_CONNECTIONS.inc() + +def release_connection(): + ACTIVE_CONNECTIONS.dec() + +if __name__ == '__main__': + start_http_server(8000) # exposes metrics at http://localhost:8000/metrics + # ... start your application ... +``` diff --git a/docs/content/instrumenting/histogram.md b/docs/content/instrumenting/histogram.md index cb85f183..8975d859 100644 --- a/docs/content/instrumenting/histogram.md +++ b/docs/content/instrumenting/histogram.md @@ -3,8 +3,9 @@ title: Histogram weight: 4 --- -Histograms track the size and number of events in buckets. -This allows for aggregatable calculation of quantiles. +A Histogram samples observations and counts them in configurable buckets. Use it +when you want to track distributions — request latency, response sizes — and need +to calculate quantiles (p50, p95, p99) in your queries. ```python from prometheus_client import Histogram @@ -12,16 +13,113 @@ h = Histogram('request_latency_seconds', 'Description of histogram') h.observe(4.7) # Observe 4.7 (seconds in this case) ``` -The default buckets are intended to cover a typical web/rpc request from milliseconds to seconds. -They can be overridden by passing `buckets` keyword argument to `Histogram`. +A Histogram exposes three time series per metric: +- `_bucket{le=""}` — count of observations with value ≤ le (cumulative) +- `_sum` — sum of all observed values +- `_count` — total number of observations -There are utilities for timing code: +## Constructor + +```python +Histogram(name, documentation, labelnames=(), namespace='', subsystem='', unit='', registry=REGISTRY, buckets=DEFAULT_BUCKETS) +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `name` | `str` | required | Metric name. | +| `documentation` | `str` | required | Help text shown in the `/metrics` output and Prometheus UI. | +| `labelnames` | `Iterable[str]` | `()` | Names of labels for this metric. See [Labels](../labels/). Note: `le` is reserved and cannot be used as a label name. | +| `namespace` | `str` | `''` | Optional prefix. | +| `subsystem` | `str` | `''` | Optional middle component. | +| `unit` | `str` | `''` | Optional unit suffix appended to the metric name. | +| `registry` | `CollectorRegistry` | `REGISTRY` | Registry to register with. Pass `None` to skip registration, which is useful in tests where you create metrics without wanting them in the global registry. | +| `buckets` | `Sequence[float]` | `DEFAULT_BUCKETS` | Upper bounds of the histogram buckets. Must be in ascending order. `+Inf` is always appended automatically. | + +`namespace`, `subsystem`, and `name` are joined with underscores to form the full metric name: + +```python +# namespace='myapp', subsystem='http', name='request_duration_seconds' +# produces: myapp_http_request_duration_seconds +Histogram('request_duration_seconds', 'Latency', namespace='myapp', subsystem='http') +``` + +Default buckets are intended to cover typical web/RPC request latency in seconds and are +accessible as `Histogram.DEFAULT_BUCKETS`: + +``` +.005, .01, .025, .05, .075, .1, .25, .5, .75, 1.0, 2.5, 5.0, 7.5, 10.0, +Inf +``` + +To override with buckets tuned to your workload: + +```python +h = Histogram('request_latency_seconds', 'Latency', buckets=[.1, .5, 1, 2, 5]) +``` + +## Methods + +### `observe(amount, exemplar=None)` + +Record a single observation. The amount is typically positive or zero. + +```python +h.observe(0.43) # observe 430ms +``` + +To attach trace context to an observation, pass an `exemplar` dict. Exemplars are +only rendered in OpenMetrics format. See [Exemplars](../exemplars/) for details. + +```python +h.observe(0.43, exemplar={'trace_id': 'abc123'}) +``` + +### `time()` + +Observe the duration in seconds of a block of code or function and add it to the +histogram. Every call accumulates — unlike `Gauge.time()`, which only keeps the +most recent duration. Can be used as a decorator or context manager. ```python @h.time() -def f(): - pass +def process(): + pass with h.time(): - pass -``` \ No newline at end of file + pass +``` + +## Labels + +See [Labels](../labels/) for how to use `.labels()`, `.remove()`, `.remove_by_labels()`, and `.clear()`. + +## Real-world example + +Tracking HTTP request latency with custom buckets tuned to the workload: + +```python +from prometheus_client import Histogram, start_http_server + +REQUEST_LATENCY = Histogram( + 'request_duration_seconds', + 'HTTP request latency', + labelnames=['method', 'endpoint'], + namespace='myapp', + buckets=[.01, .05, .1, .25, .5, 1, 2.5, 5], +) + +def handle_request(method, endpoint): + with REQUEST_LATENCY.labels(method=method, endpoint=endpoint).time(): + # ... handle the request ... + pass + +if __name__ == '__main__': + start_http_server(8000) # exposes metrics at http://localhost:8000/metrics + # ... start your application ... +``` + +This produces time series like: +``` +myapp_request_duration_seconds_bucket{method="GET",endpoint="/api/users",le="0.1"} 42 +myapp_request_duration_seconds_sum{method="GET",endpoint="/api/users"} 3.7 +myapp_request_duration_seconds_count{method="GET",endpoint="/api/users"} 50 +``` diff --git a/docs/content/instrumenting/info.md b/docs/content/instrumenting/info.md index 6334d92b..6e369de7 100644 --- a/docs/content/instrumenting/info.md +++ b/docs/content/instrumenting/info.md @@ -3,10 +3,83 @@ title: Info weight: 5 --- -Info tracks key-value information, usually about a whole target. +Info tracks key-value pairs that describe a target — build version, configuration, or environment metadata. The values are static: once set, the metric outputs a single time series with all key-value pairs as labels and a constant value of 1. ```python from prometheus_client import Info i = Info('my_build_version', 'Description of info') i.info({'version': '1.2.3', 'buildhost': 'foo@bar'}) ``` + +Info exposes one time series per metric: +- `_info{="", ...}` — always 1; the key-value pairs become labels + +Note: Info metrics do not work in multiprocess mode. + +## Constructor + +```python +Info(name, documentation, labelnames=(), namespace='', subsystem='', unit='', registry=REGISTRY) +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `name` | `str` | required | Metric name. A `_info` suffix is appended automatically when exposing the time series. | +| `documentation` | `str` | required | Help text shown in the `/metrics` output and Prometheus UI. | +| `labelnames` | `Iterable[str]` | `()` | Names of labels for this metric. See [Labels](../labels/). Keys passed to `.info()` must not overlap with these label names. | +| `namespace` | `str` | `''` | Optional prefix. | +| `subsystem` | `str` | `''` | Optional middle component. | +| `unit` | `str` | `''` | Not supported — raises `ValueError`. Info metrics cannot have a unit. | +| `registry` | `CollectorRegistry` | `REGISTRY` | Registry to register with. Pass `None` to skip registration, which is useful in tests where you create metrics without wanting them in the global registry. | + +`namespace`, `subsystem`, and `name` are joined with underscores to form the full metric name: + +```python +# namespace='myapp', subsystem='http', name='build' +# produces: myapp_http_build_info +Info('build', 'Build information', namespace='myapp', subsystem='http') +``` + +## Methods + +### `info(val)` + +Set the key-value pairs for this metric. `val` must be a `dict[str, str]` — both keys and values must be strings. Keys must not overlap with the metric's label names and values cannot be `None`. Calling `info()` again overwrites the previous value. + +```python +i.info({'version': '1.4.2', 'revision': 'abc123', 'branch': 'main'}) +``` + +## Labels + +See [Labels](../labels/) for how to use `.labels()`, `.remove()`, `.remove_by_labels()`, and `.clear()`. + +## Real-world example + +Exposing application build metadata so dashboards can join on version: + +```python +from prometheus_client import Info, start_http_server + +BUILD_INFO = Info( + 'build', + 'Application build information', + namespace='myapp', +) + +BUILD_INFO.info({ + 'version': '1.4.2', + 'revision': 'abc123def456', + 'branch': 'main', + 'build_date': '2024-01-15', +}) + +if __name__ == '__main__': + start_http_server(8000) # exposes metrics at http://localhost:8000/metrics + # ... start your application ... +``` + +This produces: +``` +myapp_build_info{branch="main",build_date="2024-01-15",revision="abc123def456",version="1.4.2"} 1.0 +``` diff --git a/docs/content/instrumenting/labels.md b/docs/content/instrumenting/labels.md index ebf80b56..39ad29c8 100644 --- a/docs/content/instrumenting/labels.md +++ b/docs/content/instrumenting/labels.md @@ -5,8 +5,8 @@ weight: 7 All metrics can have labels, allowing grouping of related time series. -See the best practices on [naming](http://prometheus.io/docs/practices/naming/) -and [labels](http://prometheus.io/docs/practices/instrumentation/#use-labels). +See the best practices on [naming](https://prometheus.io/docs/practices/naming/) +and [labels](https://prometheus.io/docs/practices/instrumentation/#use-labels). Taking a counter as an example: @@ -35,4 +35,33 @@ from prometheus_client import Counter c = Counter('my_requests_total', 'HTTP Failures', ['method', 'endpoint']) c.labels('get', '/') c.labels('post', '/submit') +``` + +## Removing labelsets + +### `remove(*labelvalues)` + +Remove a specific labelset from the metric. Values must be passed in the same +order as `labelnames` were declared. + +```python +c = Counter('my_requests_total', 'HTTP Failures', ['method', 'endpoint']) +c.labels('get', '/').inc() +c.remove('get', '/') +``` + +### `remove_by_labels(labels)` + +Remove all labelsets that partially match the given dict of label names and values. + +```python +c.remove_by_labels({'method': 'get'}) # removes all labelsets where method='get' +``` + +### `clear()` + +Remove all labelsets from the metric at once. + +```python +c.clear() ``` \ No newline at end of file diff --git a/docs/content/instrumenting/summary.md b/docs/content/instrumenting/summary.md index fa407496..55428ecb 100644 --- a/docs/content/instrumenting/summary.md +++ b/docs/content/instrumenting/summary.md @@ -3,7 +3,12 @@ title: Summary weight: 3 --- -Summaries track the size and number of events. +A Summary samples observations and tracks the total count and sum. Use it when +you want to track the size or duration of events and compute averages, but do not +need per-bucket breakdown or quantiles in your Prometheus queries. + +The Python client does not compute quantiles locally. If you need p50/p95/p99, +use a [Histogram](../histogram/) instead. ```python from prometheus_client import Summary @@ -11,15 +16,95 @@ s = Summary('request_latency_seconds', 'Description of summary') s.observe(4.7) # Observe 4.7 (seconds in this case) ``` -There are utilities for timing code: +A Summary exposes two time series per metric: +- `_count` — total number of observations +- `_sum` — sum of all observed values + +## Constructor + +```python +Summary(name, documentation, labelnames=(), namespace='', subsystem='', unit='', registry=REGISTRY) +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `name` | `str` | required | Metric name. | +| `documentation` | `str` | required | Help text shown in the `/metrics` output and Prometheus UI. | +| `labelnames` | `Iterable[str]` | `()` | Names of labels for this metric. See [Labels](../labels/). Note: `quantile` is reserved and cannot be used as a label name. | +| `namespace` | `str` | `''` | Optional prefix. | +| `subsystem` | `str` | `''` | Optional middle component. | +| `unit` | `str` | `''` | Optional unit suffix appended to the metric name. | +| `registry` | `CollectorRegistry` | `REGISTRY` | Registry to register with. Pass `None` to skip registration, which is useful in tests where you create metrics without wanting them in the global registry. | + +`namespace`, `subsystem`, and `name` are joined with underscores to form the full metric name: + +```python +# namespace='myapp', subsystem='worker', name='task_duration_seconds' +# produces: myapp_worker_task_duration_seconds +Summary('task_duration_seconds', 'Task duration', namespace='myapp', subsystem='worker') +``` + +## Methods + +### `observe(amount)` + +Record a single observation. The amount is typically positive or zero. + +```python +s.observe(0.43) # observe 430ms +s.observe(1024) # observe 1024 bytes +``` + +### `time()` + +Observe the duration in seconds of a block of code or function and add it to the +summary. Every call accumulates — unlike `Gauge.time()`, which only keeps the +most recent duration. Can be used as a decorator or context manager. ```python @s.time() -def f(): - pass +def process(): + pass with s.time(): - pass + pass +``` + +## Labels + +See [Labels](../labels/) for how to use `.labels()`, `.remove()`, `.remove_by_labels()`, and `.clear()`. + +## Real-world example + +Tracking the duration of background tasks: + +```python +from prometheus_client import Summary, start_http_server + +TASK_DURATION = Summary( + 'task_duration_seconds', + 'Time spent processing background tasks', + labelnames=['task_type'], + namespace='myapp', +) + +def run_task(task_type, task): + with TASK_DURATION.labels(task_type=task_type).time(): + # ... run the task ... + pass + +if __name__ == '__main__': + start_http_server(8000) # exposes metrics at http://localhost:8000/metrics + # ... start your application ... +``` + +This produces: +``` +myapp_task_duration_seconds_count{task_type="email"} 120 +myapp_task_duration_seconds_sum{task_type="email"} 48.3 ``` -The Python client doesn't store or expose quantile information at this time. \ No newline at end of file +You can compute the average duration in PromQL as: +``` +rate(myapp_task_duration_seconds_sum[5m]) / rate(myapp_task_duration_seconds_count[5m]) +``` From 2cd1738fb880fd0e6aa9a02ec48585128388a6cd Mon Sep 17 00:00:00 2001 From: Chris Marchbanks Date: Thu, 9 Apr 2026 13:50:44 -0600 Subject: [PATCH 2/5] Release 0.25.0 Signed-off-by: Chris Marchbanks --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index ed3ef389..336cfb4f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "prometheus_client" -version = "0.24.1" +version = "0.25.0" description = "Python client for the Prometheus monitoring system." readme = "README.md" license = "Apache-2.0 AND BSD-2-Clause" From 130a4e8a3f80e75122ea9f646b063a6f21db9b42 Mon Sep 17 00:00:00 2001 From: Krishna <107162115+k1chik@users.noreply.github.com> Date: Fri, 17 Apr 2026 11:11:33 -0400 Subject: [PATCH 3/5] docs: add API reference for pushgateway, textfile, and multiprocess (#1162) Closes #1161 Adds parameter tables and formal API reference sections to three pages that previously had examples but no parameter documentation. pushgateway.md: documents push_to_gateway, pushadd_to_gateway, delete_from_gateway, instance_ip_grouping_key, and all four built-in handlers (default, basic_auth, tls_auth, passthrough_redirect). textfile.md: documents write_to_textfile with all four parameters including the previously undocumented escaping and tmpdir, plus atomic write semantics and error behavior. multiprocess/_index.md: documents MultiProcessCollector constructor and mark_process_dead with parameter tables including the previously undocumented path parameter on both. Signed-off-by: k1chik <107162115+k1chik@users.noreply.github.com> --- docs/content/exporting/pushgateway.md | 106 ++++++++++++++++++++++++++ docs/content/exporting/textfile.md | 22 +++++- docs/content/multiprocess/_index.md | 50 ++++++++++++ 3 files changed, 177 insertions(+), 1 deletion(-) diff --git a/docs/content/exporting/pushgateway.md b/docs/content/exporting/pushgateway.md index d9f9a945..6060c0bf 100644 --- a/docs/content/exporting/pushgateway.md +++ b/docs/content/exporting/pushgateway.md @@ -85,3 +85,109 @@ g = Gauge('job_last_success_unixtime', 'Last time a batch job successfully finis g.set_to_current_time() push_to_gateway('localhost:9091', job='batchA', registry=registry, handler=my_auth_handler) ``` + +## API Reference + +### `push_to_gateway(gateway, job, registry, grouping_key=None, timeout=30, handler=default_handler, compression=None)` + +Pushes metrics to the pushgateway, replacing all metrics with the same job and grouping key. +Uses the HTTP `PUT` method. + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `gateway` | `str` | required | URL of the pushgateway. If no scheme is provided, `http://` is assumed. | +| `job` | `str` | required | Value for the `job` label attached to all pushed metrics. | +| `registry` | `Collector` | required | Registry whose metrics are pushed. Typically a `CollectorRegistry` instance. | +| `grouping_key` | `Optional[Dict[str, Any]]` | `None` | Additional labels to identify the group. See the [Pushgateway documentation](https://github.com/prometheus/pushgateway/blob/master/README.md) for details. | +| `timeout` | `Optional[float]` | `30` | Seconds before the request is aborted. Pass `None` for no timeout. | +| `handler` | `Callable` | `default_handler` | Function that performs the HTTP request. See [Handlers](#handlers) below. | +| `compression` | `Optional[str]` | `None` | Compress the payload before sending. Accepts `'gzip'` or `'snappy'`. Snappy requires the [`python-snappy`](https://github.com/andrix/python-snappy) package. | + +### `pushadd_to_gateway(gateway, job, registry, grouping_key=None, timeout=30, handler=default_handler, compression=None)` + +Pushes metrics to the pushgateway, replacing only metrics with the same name, job, and grouping key. +Uses the HTTP `POST` method. + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `gateway` | `str` | required | URL of the pushgateway. | +| `job` | `str` | required | Value for the `job` label attached to all pushed metrics. | +| `registry` | `Optional[Collector]` | required | Registry whose metrics are pushed. Pass `None` to use the default `REGISTRY`. | +| `grouping_key` | `Optional[Dict[str, Any]]` | `None` | Additional labels to identify the group. | +| `timeout` | `Optional[float]` | `30` | Seconds before the request is aborted. Pass `None` for no timeout. | +| `handler` | `Callable` | `default_handler` | Function that performs the HTTP request. | +| `compression` | `Optional[str]` | `None` | Compress the payload. Accepts `'gzip'` or `'snappy'`. | + +### `delete_from_gateway(gateway, job, grouping_key=None, timeout=30, handler=default_handler)` + +Deletes metrics from the pushgateway for the given job and grouping key. +Uses the HTTP `DELETE` method. Has no `registry` or `compression` parameters. + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `gateway` | `str` | required | URL of the pushgateway. | +| `job` | `str` | required | Value for the `job` label identifying the group to delete. | +| `grouping_key` | `Optional[Dict[str, Any]]` | `None` | Additional labels to identify the group. | +| `timeout` | `Optional[float]` | `30` | Seconds before the request is aborted. Pass `None` for no timeout. | +| `handler` | `Callable` | `default_handler` | Function that performs the HTTP request. | + +### `instance_ip_grouping_key()` + +Returns a grouping key dict with the `instance` label set to the IP address of the current host. +Takes no parameters. + +```python +from prometheus_client.exposition import instance_ip_grouping_key + +push_to_gateway('localhost:9091', job='batchA', registry=registry, + grouping_key=instance_ip_grouping_key()) +``` + +## Handlers + +A handler is a callable with the signature: + +```python +def my_handler(url, method, timeout, headers, data): + # url: str — full request URL + # method: str — HTTP method (PUT, POST, DELETE) + # timeout: Optional[float] — seconds before aborting, or None + # headers: List[Tuple[str, str]] — HTTP headers to include + # data: bytes — request body + ... + return callable_that_performs_the_request +``` + +The handler must return a no-argument callable that performs the actual HTTP request and raises +an exception (e.g. `IOError`) on failure. Three built-in handlers are available in +`prometheus_client.exposition`: + +### `default_handler` + +Standard HTTP/HTTPS handler. Used by default in all push functions. + +### `basic_auth_handler(url, method, timeout, headers, data, username=None, password=None)` + +Wraps `default_handler` and adds an HTTP Basic Auth header. + +| Extra parameter | Type | Default | Description | +|----------------|------|---------|-------------| +| `username` | `Optional[str]` | `None` | HTTP Basic Auth username. | +| `password` | `Optional[str]` | `None` | HTTP Basic Auth password. | + +### `tls_auth_handler(url, method, timeout, headers, data, certfile, keyfile, cafile=None, protocol=ssl.PROTOCOL_TLS_CLIENT, insecure_skip_verify=False)` + +Performs the request over HTTPS using TLS client certificate authentication. + +| Extra parameter | Type | Default | Description | +|----------------|------|---------|-------------| +| `certfile` | `str` | required | Path to the client certificate PEM file. | +| `keyfile` | `str` | required | Path to the client private key PEM file. | +| `cafile` | `Optional[str]` | `None` | Path to a CA certificate file for server verification. Uses system defaults if not set. | +| `protocol` | `int` | `ssl.PROTOCOL_TLS_CLIENT` | SSL/TLS protocol version. | +| `insecure_skip_verify` | `bool` | `False` | Skip server certificate verification. Use only in controlled environments. | + +### `passthrough_redirect_handler` + +Like `default_handler` but automatically follows redirects for all HTTP methods, including `PUT` +and `POST`. Use only when you control or trust the source of redirect responses. diff --git a/docs/content/exporting/textfile.md b/docs/content/exporting/textfile.md index 80360e46..cb2571af 100644 --- a/docs/content/exporting/textfile.md +++ b/docs/content/exporting/textfile.md @@ -20,4 +20,24 @@ write_to_textfile('/configured/textfile/path/raid.prom', registry) ``` A separate registry is used, as the default registry may contain other metrics -such as those from the Process Collector. \ No newline at end of file +such as those from the Process Collector. + +## API Reference + +### `write_to_textfile(path, registry, escaping='allow-utf-8', tmpdir=None)` + +Writes metrics from the registry to a file in Prometheus text format. + +The file is written atomically: metrics are first written to a temporary file in the same +directory as `path` (or in `tmpdir` if provided), then renamed into place. This prevents the +Node exporter from reading a partially written file. + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `path` | `str` | required | Destination file path. Must end in `.prom` for the Node exporter textfile collector to process it. | +| `registry` | `Collector` | required | Registry whose metrics are written. | +| `escaping` | `str` | `'allow-utf-8'` | Escaping scheme for metric and label names. Accepted values: `'allow-utf-8'`, `'underscores'`, `'dots'`, `'values'`. | +| `tmpdir` | `Optional[str]` | `None` | Directory for the temporary file used during the atomic write. Defaults to the same directory as `path`. If provided, must be on the same filesystem as `path`. | + +Returns `None`. Raises an exception if the file cannot be written; the temporary file is cleaned +up automatically on failure. \ No newline at end of file diff --git a/docs/content/multiprocess/_index.md b/docs/content/multiprocess/_index.md index 42ea6a67..f7befef8 100644 --- a/docs/content/multiprocess/_index.md +++ b/docs/content/multiprocess/_index.md @@ -96,3 +96,53 @@ from prometheus_client import Gauge # Example gauge IN_PROGRESS = Gauge("inprogress_requests", "help", multiprocess_mode='livesum') ``` + +## API Reference + +### `MultiProcessCollector(registry, path=None)` + +Collector that aggregates metrics written by all processes in the multiprocess directory. + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `registry` | `CollectorRegistry` | required | Registry to register with. Pass a registry created inside the request context to avoid duplicate metrics. | +| `path` | `Optional[str]` | `None` | Path to the directory containing the per-process metric files. Defaults to the `PROMETHEUS_MULTIPROC_DIR` environment variable. | + +Raises `ValueError` if `path` is not set or does not point to an existing directory. + +```python +from prometheus_client import multiprocess, CollectorRegistry + +def app(environ, start_response): + registry = CollectorRegistry(support_collectors_without_names=True) + multiprocess.MultiProcessCollector(registry) + ... +``` + +To use a custom path instead of the environment variable: + +```python +collector = multiprocess.MultiProcessCollector(registry, path='/var/run/prom') +``` + +### `mark_process_dead(pid, path=None)` + +Removes the per-process metric files for a dead process. Call this from your process manager +when a worker exits to prevent stale `live*` gauge values from accumulating. + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `pid` | `int` | required | PID of the process that has exited. | +| `path` | `Optional[str]` | `None` | Path to the multiprocess directory. Defaults to the `PROMETHEUS_MULTIPROC_DIR` environment variable. | + +Returns `None`. Only removes files for `live*` gauge modes (e.g. `livesum`, `liveall`); files +for non-live modes are left in place so their last values remain visible until the directory is +wiped on restart. + +```python +# Gunicorn config +from prometheus_client import multiprocess + +def child_exit(server, worker): + multiprocess.mark_process_dead(worker.pid) +``` From e75a74f7a9000f414ddacefd6797d560d4bb6731 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Vokr=C3=A1=C4=8Dko?= Date: Fri, 24 Apr 2026 23:07:17 +0200 Subject: [PATCH 4/5] Expose measured duration on Timer context manager (#1166) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Assigning the .time() context manager (with ... as t) now yields a Timer whose .duration attribute holds the observed value in seconds after the block exits. This lets callers reuse the measurement (logging, further metrics) without calling default_timer() a second time. Signed-off-by: Lukáš Vokráčko --- docs/content/instrumenting/gauge.md | 4 ++++ docs/content/instrumenting/histogram.md | 4 ++++ docs/content/instrumenting/summary.md | 4 ++++ prometheus_client/context_managers.py | 5 +++-- tests/test_core.py | 8 ++++++++ 5 files changed, 23 insertions(+), 2 deletions(-) diff --git a/docs/content/instrumenting/gauge.md b/docs/content/instrumenting/gauge.md index 43168a68..62294944 100644 --- a/docs/content/instrumenting/gauge.md +++ b/docs/content/instrumenting/gauge.md @@ -108,6 +108,10 @@ def process(): with g.time(): pass + +with g.time() as t: + pass +print(t.duration) # observed time in seconds. ``` ### `set_function(f)` diff --git a/docs/content/instrumenting/histogram.md b/docs/content/instrumenting/histogram.md index 8975d859..fa0ffe1a 100644 --- a/docs/content/instrumenting/histogram.md +++ b/docs/content/instrumenting/histogram.md @@ -86,6 +86,10 @@ def process(): with h.time(): pass + +with h.time() as t: + pass +print(t.duration) # observed time in seconds. ``` ## Labels diff --git a/docs/content/instrumenting/summary.md b/docs/content/instrumenting/summary.md index 55428ecb..714dfd2f 100644 --- a/docs/content/instrumenting/summary.md +++ b/docs/content/instrumenting/summary.md @@ -68,6 +68,10 @@ def process(): with s.time(): pass + +with s.time() as t: + pass +print(t.duration) # observed time in seconds. ``` ## Labels diff --git a/prometheus_client/context_managers.py b/prometheus_client/context_managers.py index 3988ec22..3e8d7ced 100644 --- a/prometheus_client/context_managers.py +++ b/prometheus_client/context_managers.py @@ -55,6 +55,7 @@ class Timer: def __init__(self, metric, callback_name): self._metric = metric self._callback_name = callback_name + self.duration = None def _new_timer(self): return self.__class__(self._metric, self._callback_name) @@ -65,9 +66,9 @@ def __enter__(self): def __exit__(self, typ, value, traceback): # Time can go backwards. - duration = max(default_timer() - self._start, 0) + self.duration = max(default_timer() - self._start, 0) callback = getattr(self._metric, self._callback_name) - callback(duration) + callback(self.duration) def labels(self, *args, **kw): self._metric = self._metric.labels(*args, **kw) diff --git a/tests/test_core.py b/tests/test_core.py index 66492c6f..cdf32bfa 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -378,6 +378,14 @@ def test_block_decorator_with_label(self): metric.labels('foo') self.assertEqual(1, value('s_with_labels_count', {'label1': 'foo'})) + def test_timer_duration_exposed(self): + with self.summary.time() as t: + time.sleep(0.01) + self.assertIsNotNone(t.duration) + self.assertGreater(t.duration, 0) + recorded_sum = self.registry.get_sample_value('s_sum') + self.assertEqual(t.duration, recorded_sum) + def test_timer_not_observable(self): s = Summary('test', 'help', labelnames=('label',), registry=self.registry) From 482656c8c07b78668adb5f3171dfe71ccc2396b2 Mon Sep 17 00:00:00 2001 From: Krishna <107162115+k1chik@users.noreply.github.com> Date: Fri, 24 Apr 2026 17:10:21 -0400 Subject: [PATCH 5/5] docs: add API reference for CollectorRegistry and custom collector classes (#1169) Closes #1163 collector/custom.md: Collector protocol section (collect/describe), value vs labels mutual exclusivity note, full constructor and add_metric tables for GaugeMetricFamily, CounterMetricFamily, SummaryMetricFamily, HistogramMetricFamily, and InfoMetricFamily, plus a runnable real-world example. collector/_index.md: constructor parameter tables for ProcessCollector, PlatformCollector, and GCCollector, with exported metrics listed for each. registry/_index.md (new): CollectorRegistry constructor and all public methods (register, unregister, collect, restricted_registry, get_sample_value, set_target_info, get_target_info), the global REGISTRY instance, and examples for isolated registry usage and registry=None. All code examples verified by running them in Python. Signed-off-by: k1chik <107162115+k1chik@users.noreply.github.com> --- docs/content/collector/_index.md | 77 +++++++++- docs/content/collector/custom.md | 250 ++++++++++++++++++++++++++++++- docs/content/registry/_index.md | 141 +++++++++++++++++ 3 files changed, 464 insertions(+), 4 deletions(-) create mode 100644 docs/content/registry/_index.md diff --git a/docs/content/collector/_index.md b/docs/content/collector/_index.md index 957c8ba9..85c6f12f 100644 --- a/docs/content/collector/_index.md +++ b/docs/content/collector/_index.md @@ -18,8 +18,8 @@ ProcessCollector(namespace='mydaemon', pid=lambda: open('/var/run/daemon.pid').r # Platform Collector The client also automatically exports some metadata about Python. If using Jython, -metadata about the JVM in use is also included. This information is available as -labels on the `python_info` metric. The value of the metric is 1, since it is the +metadata about the JVM in use is also included. This information is available as +labels on the `python_info` metric. The value of the metric is 1, since it is the labels that carry information. # Disabling Default Collector metrics @@ -33,4 +33,75 @@ import prometheus_client prometheus_client.REGISTRY.unregister(prometheus_client.GC_COLLECTOR) prometheus_client.REGISTRY.unregister(prometheus_client.PLATFORM_COLLECTOR) prometheus_client.REGISTRY.unregister(prometheus_client.PROCESS_COLLECTOR) -``` \ No newline at end of file +``` + +## API Reference + +### ProcessCollector + +```python +ProcessCollector(namespace='', pid=lambda: 'self', proc='/proc', registry=REGISTRY) +``` + +Collects process metrics from `/proc`. Only available on Linux. + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `namespace` | `str` | `''` | Prefix added to all metric names, e.g. `'mydaemon'` produces `mydaemon_process_cpu_seconds_total`. | +| `pid` | `Callable[[], int or str]` | `lambda: 'self'` | Callable that returns the PID to monitor. `'self'` monitors the current process. | +| `proc` | `str` | `'/proc'` | Path to the proc filesystem. Useful for testing or containerised environments with a non-standard mount point. | +| `registry` | `CollectorRegistry` | `REGISTRY` | Registry to register with. Pass `None` to skip registration. | + +Metrics exported: + +| Metric | Description | +|--------|-------------| +| `process_cpu_seconds_total` | Total user and system CPU time in seconds. | +| `process_virtual_memory_bytes` | Virtual memory size in bytes. | +| `process_resident_memory_bytes` | Resident memory size in bytes. | +| `process_start_time_seconds` | Start time since Unix epoch in seconds. | +| `process_open_fds` | Number of open file descriptors. | +| `process_max_fds` | Maximum number of open file descriptors. | + +The module-level `PROCESS_COLLECTOR` is the default instance registered with `REGISTRY`. + +### PlatformCollector + +```python +PlatformCollector(registry=REGISTRY, platform=None) +``` + +Exports Python runtime metadata as a `python_info` gauge metric with labels. + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `registry` | `CollectorRegistry` | `REGISTRY` | Registry to register with. Pass `None` to skip registration. | +| `platform` | module | `None` | Override the `platform` module. Intended for testing. | + +Labels on `python_info`: `version`, `implementation`, `major`, `minor`, `patchlevel`. +On Jython, additional labels are added: `jvm_version`, `jvm_release`, `jvm_vendor`, `jvm_name`. + +The module-level `PLATFORM_COLLECTOR` is the default instance registered with `REGISTRY`. + +### GCCollector + +```python +GCCollector(registry=REGISTRY) +``` + +Exports Python garbage collector statistics. Only active on CPython (skipped silently on +other implementations). + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `registry` | `CollectorRegistry` | `REGISTRY` | Registry to register with. | + +Metrics exported: + +| Metric | Description | +|--------|-------------| +| `python_gc_objects_collected_total` | Objects collected during GC, by generation. | +| `python_gc_objects_uncollectable_total` | Uncollectable objects found during GC, by generation. | +| `python_gc_collections_total` | Number of times each generation was collected. | + +The module-level `GC_COLLECTOR` is the default instance registered with `REGISTRY`. diff --git a/docs/content/collector/custom.md b/docs/content/collector/custom.md index bc6a021c..62c0180a 100644 --- a/docs/content/collector/custom.md +++ b/docs/content/collector/custom.md @@ -35,4 +35,252 @@ not implemented and the CollectorRegistry was created with `auto_describe=True` (which is the case for the default registry) then `collect` will be called at registration time instead of `describe`. If this could cause problems, either implement a proper `describe`, or if that's not practical have `describe` -return an empty list. \ No newline at end of file +return an empty list. + +## Collector protocol + +A collector is any object that implements a `collect` method. Optionally it +can also implement `describe`. + +### `collect()` + +Returns an iterable of metric family objects (`GaugeMetricFamily`, +`CounterMetricFamily`, etc.). Called every time the registry is scraped. + +### `describe()` + +Returns an iterable of metric family objects used only to determine the metric +names the collector produces. Samples on the returned objects are ignored. If +not implemented and the registry has `auto_describe=True`, `collect` is called +at registration time instead. + +## value vs labels + +Every metric family constructor accepts either inline data or `labels`, but not +both. The inline data parameter name varies by type: `value` for Gauge, Counter, +and Info; `count_value`/`sum_value` for Summary; `buckets` for Histogram. + +- Pass inline data to emit a single unlabelled metric directly from the constructor. +- Pass `labels` (a sequence of label names) and then call `add_metric` one or + more times to emit labelled metrics. + +```python +# single unlabelled value +GaugeMetricFamily('my_gauge', 'Help text', value=7) + +# labelled metrics via add_metric +g = GaugeMetricFamily('my_gauge', 'Help text', labels=['region']) +g.add_metric(['us-east-1'], 3) +g.add_metric(['eu-west-1'], 5) +``` + +## API Reference + +### GaugeMetricFamily + +```python +GaugeMetricFamily(name, documentation, value=None, labels=None, unit='') +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `name` | `str` | required | Metric name. | +| `documentation` | `str` | required | Help text shown in the `/metrics` output. | +| `value` | `float` | `None` | Emit a single unlabelled sample with this value. Mutually exclusive with `labels`. | +| `labels` | `Sequence[str]` | `None` | Label names. Use with `add_metric`. Mutually exclusive with `value`. | +| `unit` | `str` | `''` | Optional unit suffix appended to the metric name. | + +#### `add_metric(labels, value, timestamp=None)` + +Add a labelled sample to the metric family. + +| Parameter | Type | Description | +|-----------|------|-------------| +| `labels` | `Sequence[str]` | Label values in the same order as the `labels` constructor argument. | +| `value` | `float` | The gauge value. | +| `timestamp` | `float` or `Timestamp` | Optional Unix timestamp for the sample. | + +```python +g = GaugeMetricFamily('temperature_celsius', 'Temperature by location', labels=['location']) +g.add_metric(['living_room'], 21.5) +g.add_metric(['basement'], 18.0) +yield g +``` + +### CounterMetricFamily + +```python +CounterMetricFamily(name, documentation, value=None, labels=None, created=None, unit='', exemplar=None) +``` + +If `name` ends with `_total`, the suffix is stripped automatically so the +metric is stored without it and the `_total` suffix is added on exposition. + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `name` | `str` | required | Metric name. A trailing `_total` is stripped and re-added on exposition. | +| `documentation` | `str` | required | Help text. | +| `value` | `float` | `None` | Emit a single unlabelled sample. Mutually exclusive with `labels`. | +| `labels` | `Sequence[str]` | `None` | Label names. Use with `add_metric`. Mutually exclusive with `value`. | +| `created` | `float` | `None` | Unix timestamp the counter was created at. Only used when `value` is set. | +| `unit` | `str` | `''` | Optional unit suffix. | +| `exemplar` | `Exemplar` | `None` | Exemplar for the single-value form. Only used when `value` is set. | + +#### `add_metric(labels, value, created=None, timestamp=None, exemplar=None)` + +| Parameter | Type | Description | +|-----------|------|-------------| +| `labels` | `Sequence[str]` | Label values. | +| `value` | `float` | The counter value. | +| `created` | `float` | Optional Unix timestamp the counter was created at. | +| `timestamp` | `float` or `Timestamp` | Optional Unix timestamp for the sample. | +| `exemplar` | `Exemplar` | Optional exemplar. See [Exemplars](../../instrumenting/exemplars/). | + +```python +c = CounterMetricFamily('http_requests_total', 'HTTP requests by status', labels=['status']) +c.add_metric(['200'], 1200) +c.add_metric(['404'], 43) +c.add_metric(['500'], 7) +yield c +``` + +### SummaryMetricFamily + +```python +SummaryMetricFamily(name, documentation, count_value=None, sum_value=None, labels=None, unit='') +``` + +`count_value` and `sum_value` must always be provided together or not at all. + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `name` | `str` | required | Metric name. | +| `documentation` | `str` | required | Help text. | +| `count_value` | `int` | `None` | Observation count for a single unlabelled metric. Must be paired with `sum_value`. | +| `sum_value` | `float` | `None` | Observation sum for a single unlabelled metric. Must be paired with `count_value`. | +| `labels` | `Sequence[str]` | `None` | Label names. Use with `add_metric`. Mutually exclusive with `count_value`/`sum_value`. | +| `unit` | `str` | `''` | Optional unit suffix. | + +#### `add_metric(labels, count_value, sum_value, timestamp=None)` + +| Parameter | Type | Description | +|-----------|------|-------------| +| `labels` | `Sequence[str]` | Label values. | +| `count_value` | `int` | The number of observations. | +| `sum_value` | `float` | The sum of all observed values. | +| `timestamp` | `float` or `Timestamp` | Optional Unix timestamp for the sample. | + +```python +s = SummaryMetricFamily('rpc_duration_seconds', 'RPC duration', labels=['method']) +s.add_metric(['get'], count_value=1000, sum_value=53.2) +s.add_metric(['put'], count_value=400, sum_value=28.7) +yield s +``` + +### HistogramMetricFamily + +```python +HistogramMetricFamily(name, documentation, buckets=None, sum_value=None, labels=None, unit='') +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `name` | `str` | required | Metric name. | +| `documentation` | `str` | required | Help text. | +| `buckets` | `Sequence` | `None` | Bucket data for a single unlabelled metric. Each entry is a `(le, value)` pair or `(le, value, exemplar)` triple. Must include a `+Inf` bucket. Mutually exclusive with `labels`. | +| `sum_value` | `float` | `None` | Observation sum. Cannot be set without `buckets`. Omitted for histograms with negative buckets. | +| `labels` | `Sequence[str]` | `None` | Label names. Use with `add_metric`. Mutually exclusive with `buckets`. | +| `unit` | `str` | `''` | Optional unit suffix. | + +#### `add_metric(labels, buckets, sum_value, timestamp=None)` + +| Parameter | Type | Description | +|-----------|------|-------------| +| `labels` | `Sequence[str]` | Label values. | +| `buckets` | `Sequence` | Bucket data. Each entry is a `(le, value)` pair or `(le, value, exemplar)` triple. Must be sorted and include `+Inf`. | +| `sum_value` | `float` or `None` | The sum of all observed values. Pass `None` for histograms with negative buckets. | +| `timestamp` | `float` or `Timestamp` | Optional Unix timestamp. | + +```python +h = HistogramMetricFamily('request_size_bytes', 'Request sizes', labels=['handler']) +h.add_metric( + ['api'], + buckets=[('100', 5), ('1000', 42), ('+Inf', 50)], + sum_value=18350.0, +) +yield h +``` + +### InfoMetricFamily + +```python +InfoMetricFamily(name, documentation, value=None, labels=None) +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `name` | `str` | required | Metric name. The `_info` suffix is added automatically on exposition. | +| `documentation` | `str` | required | Help text. | +| `value` | `Dict[str, str]` | `None` | Key-value label pairs for a single unlabelled info metric. Mutually exclusive with `labels`. | +| `labels` | `Sequence[str]` | `None` | Label names for the outer grouping. Use with `add_metric`. Mutually exclusive with `value`. | + +#### `add_metric(labels, value, timestamp=None)` + +| Parameter | Type | Description | +|-----------|------|-------------| +| `labels` | `Sequence[str]` | Outer label values (from the `labels` constructor argument). | +| `value` | `Dict[str, str]` | Key-value label pairs that form the info payload. | +| `timestamp` | `float` or `Timestamp` | Optional Unix timestamp. | + +```python +# single unlabelled info metric +yield InfoMetricFamily('build', 'Build metadata', value={'version': '1.2.3', 'commit': 'abc123'}) + +# labelled: one info metric per service +i = InfoMetricFamily('service_build', 'Per-service build info', labels=['service']) +i.add_metric(['auth'], {'version': '2.0.1', 'commit': 'def456'}) +i.add_metric(['api'], {'version': '1.9.0', 'commit': 'ghi789'}) +yield i +``` + +## Real-world example + +Proxying metrics from an external source: + +```python +from prometheus_client.core import CounterMetricFamily, GaugeMetricFamily, REGISTRY +from prometheus_client.registry import Collector +from prometheus_client import start_http_server + +# Simulated external data source +_QUEUE_STATS = { + 'orders': {'depth': 14, 'processed': 9821}, + 'notifications': {'depth': 3, 'processed': 45210}, +} + +class QueueCollector(Collector): + def collect(self): + depth = GaugeMetricFamily( + 'queue_depth', + 'Current number of messages waiting in the queue', + labels=['queue'], + ) + processed = CounterMetricFamily( + 'queue_messages_processed_total', + 'Total messages processed from the queue', + labels=['queue'], + ) + for name, stats in _QUEUE_STATS.items(): + depth.add_metric([name], stats['depth']) + processed.add_metric([name], stats['processed']) + yield depth + yield processed + +REGISTRY.register(QueueCollector()) + +if __name__ == '__main__': + start_http_server(8000) + import time + while True: + time.sleep(1) +``` diff --git a/docs/content/registry/_index.md b/docs/content/registry/_index.md new file mode 100644 index 00000000..0d554535 --- /dev/null +++ b/docs/content/registry/_index.md @@ -0,0 +1,141 @@ +--- +title: Registry +weight: 8 +--- + +A `CollectorRegistry` holds all the collectors whose metrics are exposed when +the registry is scraped. The global default registry is `REGISTRY`, which all +metric constructors register with automatically unless told otherwise. + +```python +from prometheus_client import REGISTRY, CollectorRegistry + +# Use the default global registry +from prometheus_client import Counter +c = Counter('my_counter', 'A counter') # registered with REGISTRY automatically + +# Create an isolated registry, e.g. for testing +r = CollectorRegistry() +c2 = Counter('my_counter', 'A counter', registry=r) +``` + +## Constructor + +```python +CollectorRegistry(auto_describe=False, target_info=None, support_collectors_without_names=False) +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `auto_describe` | `bool` | `False` | If `True`, calls `collect()` on a collector at registration time if the collector does not implement `describe()`. Used to detect duplicate metric names. The default `REGISTRY` is created with `auto_describe=True`. | +| `target_info` | `Dict[str, str]` | `None` | Key-value labels to attach as a `target_info` metric. Equivalent to calling `set_target_info` after construction. | +| `support_collectors_without_names` | `bool` | `False` | If `True`, allows registering collectors that produce no named metrics (i.e. whose `describe()` returns an empty list). | + +## Methods + +### `register(collector)` + +Register a collector with this registry. Raises `ValueError` if any of the +metric names the collector produces are already registered. + +```python +from prometheus_client.registry import Collector + +class MyCollector(Collector): + def collect(self): + ... + +REGISTRY.register(MyCollector()) +``` + +### `unregister(collector)` + +Remove a previously registered collector. + +```python +from prometheus_client import GC_COLLECTOR +REGISTRY.unregister(GC_COLLECTOR) +``` + +### `collect()` + +Yield all metrics from every registered collector. Also yields the +`target_info` metric if one has been set. + +```python +for metric in REGISTRY.collect(): + print(metric.name, metric.type) +``` + +### `restricted_registry(names)` + +Return a view of this registry that only exposes the named metrics. Useful +for partial scrapes. See [Restricted registry](../restricted-registry/) for +usage with `generate_latest` and the built-in HTTP server. + +```python +from prometheus_client import generate_latest + +subset = REGISTRY.restricted_registry(['python_info', 'process_cpu_seconds_total']) +output = generate_latest(subset) +``` + +### `get_sample_value(name, labels=None)` + +Return the current value of a single sample, or `None` if not found. Intended +for use in unit tests; not efficient for production use. + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `name` | `str` | required | Full sample name including any suffix (e.g. `'my_counter_total'`). | +| `labels` | `Dict[str, str]` | `{}` | Label key-value pairs to match. An empty dict matches an unlabelled sample. | + +```python +from prometheus_client import Counter, CollectorRegistry + +r = CollectorRegistry() +c = Counter('requests_total', 'Total requests', registry=r) +c.inc(3) + +assert r.get_sample_value('requests_total') == 3.0 +``` + +### `set_target_info(labels)` + +Set or replace the target metadata labels exposed as a `target_info` metric. +Pass `None` to remove the target info metric. + +```python +REGISTRY.set_target_info({'env': 'production', 'region': 'us-east-1'}) +``` + +### `get_target_info()` + +Return the current target info labels as a `Dict[str, str]`, or `None` if not set. + +```python +info = REGISTRY.get_target_info() +``` + +## The global REGISTRY + +`REGISTRY` is the module-level default instance, created as: + +```python +REGISTRY = CollectorRegistry(auto_describe=True) +``` + +All metric constructors (`Counter`, `Gauge`, etc.) register with `REGISTRY` +by default. Pass `registry=None` to skip registration, or pass a different +`CollectorRegistry` instance to use a custom registry. + +```python +from prometheus_client import Counter, CollectorRegistry + +# skip global registration — useful in tests +c = Counter('my_counter', 'A counter', registry=None) + +# register with a custom registry +r = CollectorRegistry() +c2 = Counter('my_counter', 'A counter', registry=r) +```