Skip to main content

Translates prometheus_client metrics into OpenAPI specification.

Project description

Generate OpenAPI components schema from prometheus_client metrics.

Install

pip install promclient_to_openapi

Usage

import json

from fastapi import FastAPI
from fastapi.openapi.utils import get_openapi
from promclient_to_openapi import prometheus_client_to_openapi
from prometheus_client import REGISTRY


app = FastAPI()
metrics_schema = prometheus_client_to_openapi(metrics=REGISTRY)


def custom_openapi():
    """Modify default OpenAPI spec for metrics to be documented."""

    if app.openapi_schema:
        return app.openapi_schema

    openapi_schema = get_openapi(title="Customized OpenAPI", version="0.1.0", routes=app.routes)
    openapi_schema["components"] = {"schemas": metrics_schema}

    app.openapi_schema = openapi_schema
    return app.openapi_schema

app.openapi = custom_openapi

See more about OpenAPI customization for FastAPI on the docs.

Or you can provide your list of metrics objects (Gauge, Counter, Info, etc) instead of invoking full metrics generation and parsing and also customize description, root property name and describe labels:

m1 = Gauge(name="test_metric_foo", documentation="Test metric", labelnames=("metric",))
m1.labels(("1",)).set(value=1)

m2 = Gauge(name="test_metric_bar", documentation="Test metric", labelnames=("metric",))
m2.labels(("2",)).set(value=2)

labels_descriptions: dict[str, str] = {"Test metric": {"metric": "Test label"}}

metrics_schema = prometheus_client_to_openapi(
    metrics=(m1, m2),
    describe_labels=labels_descriptions,
    description="Customized description",
    roperty_name="MyCoolMetrics"
)

For more advanced usage see below.

First example will generate default valid OpenAPI schema extended with default prometheus_client metrics definitions:

openapi: 3.1.0
info:
  title: Customized OpenAPI
  version: 0.1.0
paths: {}
components:
  schemas:
    PrometheusClientMetrics:
      properties:
        python_gc_objects_collected:
          $ref: '#/components/schemas/PythonGcObjectsCollected'
        python_gc_objects_uncollectable:
          $ref: '#/components/schemas/PythonGcObjectsUncollectable'
        python_gc_collections:
          $ref: '#/components/schemas/PythonGcCollections'
        python_info:
          $ref: '#/components/schemas/PythonInfo'
        process_virtual_memory_bytes:
          $ref: '#/components/schemas/ProcessVirtualMemoryBytes'
        process_resident_memory_bytes:
          $ref: '#/components/schemas/ProcessResidentMemoryBytes'
        process_start_time_seconds:
          $ref: '#/components/schemas/ProcessStartTimeSeconds'
        process_cpu_seconds:
          $ref: '#/components/schemas/ProcessCpuSeconds'
        process_open_fds:
          $ref: '#/components/schemas/ProcessOpenFds'
        process_max_fds:
          $ref: '#/components/schemas/ProcessMaxFds'
      type: object
      title: PrometheusClientMetrics
      description: Prometheus-compatible metrics
    PythonGcObjectsCollected:
      properties:
        generation:
          type: string
          title: Generation
      type: object
      title: PythonGcObjectsCollected
      description: Objects collected during gc
    PythonGcObjectsUncollectable:
      properties:
        generation:
          type: string
          title: Generation
      type: object
      title: PythonGcObjectsUncollectable
      description: Uncollectable objects found during GC
    PythonGcCollections:
      properties:
        generation:
          type: string
          title: Generation
      type: object
      title: PythonGcCollections
      description: Number of times this generation was collected
    PythonInfo:
      type: object
      title: PythonInfo
      description: Python platform information
    ProcessVirtualMemoryBytes:
      properties: {}
      type: object
      title: ProcessVirtualMemoryBytes
      description: Virtual memory size in bytes.
    ProcessResidentMemoryBytes:
      properties: {}
      type: object
      title: ProcessResidentMemoryBytes
      description: Resident memory size in bytes.
    ProcessStartTimeSeconds:
      properties: {}
      type: object
      title: ProcessStartTimeSeconds
      description: Start time of the process since unix epoch in seconds.
    ProcessCpuSeconds:
      properties: {}
      type: object
      title: ProcessCpuSeconds
      description: Total user and system CPU time spent in seconds.
    ProcessOpenFds:
      properties: {}
      type: object
      title: ProcessOpenFds
      description: Number of open file descriptors.
    ProcessMaxFds:
      properties: {}
      type: object
      title: ProcessMaxFds
      description: Maximum number of open file descriptors.

Real advanced FastAPI example:

# For openapi_tags field, application should be instanciated normally, not using
# fastapi.openapi.utils.get_openapi() function.
app = FastAPI(
    title=__prog__,
    summary=summary,
    description=__doc__,
    version=f"{__version__} {__status__}",
    contact={"name": __author__, "email": __email__},
    openapi_tags=[
        {"name": "Internal", "description": "Internal endpoints."},
    ],
    docs_url=None,
    redoc_url=None,
)

# Genearate metrics schema and save default method with another name.
metrics_schema = prometheus_client_to_openapi(metrics=REGISTRY)
app.default_openapi = app.openapi


# This way custom schema (for metrics) is prepended to OpenAPI specification.
def custom_openapi() -> dict[str, Any]:
    """Modify default OpenAPI spec for metrics to be documented."""

    # This should be called only once, obviously.
    if app.openapi_schema:
        return app.openapi_schema

    openapi_schema = app.default_openapi()
    openapi_schema["components"]["schemas"].update(metrics_schema)

    # FastAPI inserts "type" parameter here so ReDoc cannot handle custom schema
    # reference.
    del openapi_schema["paths"]["/metrics"]["get"]["responses"]["200"]["content"]["application/openmetrics-text"]["schema"]["type"]

    # Also remove default 422 Validation Error from documentation since there is
    # no actual validation of Accept header, just some kind of negotiation to
    # support "Accept: application/openmetrics-text".
    del openapi_schema["paths"]["/metrics"]["get"]["responses"]["422"]

    app.openapi_schema = openapi_schema
    return openapi_schema


# Now replace default .openapi() method with custom.
app.openapi = custom_openapi

And handler:

class MetricsResponse(Response):
    """Prometheus-compatible metrics response."""

    media_type = "application/openmetrics-text"

    def render(self, content: Any) -> bytes:
        return generate_latest()


def serve_metrics(
    accept: Annotated[
        str | None,
        Header(
            description="Accept HTTP header for content-type negotiation with Prometheus server.",
            examples=["application/openmetrics-text", "text/plain"],
        ),
    ] = None,
) -> MetricsResponse:
    """Serve application metrics."""

    if accept is not None and "application/openmetrics-text" in accept.lower():
        return MetricsResponse(media_type="application/openmetrics-text")

    return MetricsResponse(media_type="text/plain")


router.add_api_route(
    path="/metrics",
    endpoint=serve_metrics,
    name="Metrics",
    description="Get application metrics in Prometheus-compatible format.",
    response_class=MetricsResponse,
    responses={
        status.HTTP_200_OK: {
            "content": {
                "application/openmetrics-text": {
                    "schema": {
                        "$ref": "#/components/schemas/PrometheusClientMetrics",
                    },
                },
                "text/plain": {
                    "schema": {
                        "$ref": "#/components/schemas/PrometheusClientMetrics",
                    },
                },
            },
        },
    },
)

Changelog:

  • 1.1.1: (05.11.2025): added FastAPI helpers and Apt/Pip Info metrics helpers.
  • 1.0.2: (18.10.2025): redo label descriptions, commented code.
  • 1.0.1: (27.09.2025): remove required field completely.

TODO

  • Add aioprometheus support.
  • Document helper functions and rewrite README.md.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

promclient_to_openapi-1.1.1.tar.gz (12.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

promclient_to_openapi-1.1.1-py3-none-any.whl (11.2 kB view details)

Uploaded Python 3

File details

Details for the file promclient_to_openapi-1.1.1.tar.gz.

File metadata

File hashes

Hashes for promclient_to_openapi-1.1.1.tar.gz
Algorithm Hash digest
SHA256 5bf169ac66cdf7e5e85565bb58b93e0d43f18b69273bd07b3a789e48980845d2
MD5 0145df57db97f9df48d940f84bfb661c
BLAKE2b-256 08c51db5f9e1ed4ca1a2542317678ff19fc2bc927b33fe4241436e17dfee71fe

See more details on using hashes here.

File details

Details for the file promclient_to_openapi-1.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for promclient_to_openapi-1.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 560ce3942e46e58226cce76c154173d7be356cb9d70dcdcd0b24b3f1918e0eb0
MD5 70f31bb3cc8c334964c0dcc84520fb98
BLAKE2b-256 85a7cee88196bb555a9a9e2968acbe536ad9fb359718b25aced35b0e9194b000

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page