{"id":4697,"library":"py-grpc-prometheus","title":"Python gRPC Prometheus Interceptors","description":"py-grpc-prometheus is an instrumentation library that provides Prometheus metrics for gRPC services in Python. It offers client and server interceptors to expose standard gRPC metrics, aiming for parity with similar libraries in Java and Go. The current version is 0.8.0, released in February 2024, with releases occurring on an 'as-needed' basis.","status":"active","version":"0.8.0","language":"en","source_language":"en","source_url":"https://github.com/lchenn/py-grpc-prometheus","tags":["grpc","prometheus","metrics","monitoring","interceptor"],"install":[{"cmd":"pip install py-grpc-prometheus","lang":"bash","label":"Install latest version"}],"dependencies":[{"reason":"Core gRPC library for Python.","package":"grpcio","optional":false},{"reason":"Used to expose Prometheus metrics.","package":"prometheus-client","optional":false},{"reason":"Build and distribution utility.","package":"setuptools","optional":false}],"imports":[{"symbol":"PromServerInterceptor","correct":"from py_grpc_prometheus.prometheus_server_interceptor import PromServerInterceptor"},{"symbol":"PromClientInterceptor","correct":"from py_grpc_prometheus.prometheus_client_interceptor import PromClientInterceptor"},{"note":"While functional, direct import for `start_http_server` is cleaner.","wrong":"import prometheus_client; prometheus_client.start_http_server(8000)","symbol":"start_http_server","correct":"from prometheus_client import start_http_server"}],"quickstart":{"code":"import grpc\nfrom concurrent import futures\nfrom prometheus_client import start_http_server\nfrom py_grpc_prometheus.prometheus_server_interceptor import PromServerInterceptor\nfrom py_grpc_prometheus.prometheus_client_interceptor import PromClientInterceptor\n\n# --- Example gRPC Service (replace with your actual service) ---\n# In a real application, you would generate this from a .proto file\nclass GreeterServicer(grpc.ServerInterceptor):\n    def SayHello(self, request, context):\n        print(f\"Server received: {request.name}\")\n        return greeter_pb2.HelloReply(message=f\"Hello, {request.name}!\")\n\n    def intercept_service(self, continuation, handler_call_details):\n        # Simple example of a custom interceptor that just passes through\n        print(f\"Custom server interceptor: {handler_call_details.method}\")\n        return continuation(handler_call_details)\n\n# To make this example runnable, we'll mock proto definitions\nclass greeter_pb2:\n    class HelloRequest:\n        def __init__(self, name=''):\n            self.name = name\n    class HelloReply:\n        def __init__(self, message=''):\n            self.message = message\n\n\n# --- Server Setup ---\ndef serve():\n    # Start Prometheus HTTP server to expose metrics\n    start_http_server(8000)\n    print(\"Prometheus metrics exposed on port 8000\")\n\n    # Initialize Prometheus server interceptor\n    prom_server_interceptor = PromServerInterceptor(enable_handling_time_histogram=True)\n\n    # Create gRPC server with interceptor(s)\n    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10), \n                         interceptors=(prom_server_interceptor, GreeterServicer()))\n\n    # Add your actual service to the server\n    # In a real app, this would be `greeter_pb2_grpc.add_GreeterServicer_to_server`\n    # For this example, we'll just bind the port for a placeholder\n    server.add_insecure_port('[::]:50051')\n    print(\"gRPC server started on port 50051\")\n    server.start()\n    server.wait_for_termination()\n\n# --- Client Setup ---\ndef run_client():\n    # Initialize Prometheus client interceptor\n    prom_client_interceptor = PromClientInterceptor(enable_client_handling_time_histogram=True)\n\n    # Create gRPC channel with interceptor\n    channel = grpc.intercept_channel(\n        grpc.insecure_channel('localhost:50051'),\n        prom_client_interceptor\n    )\n    # In a real app, this would be `greeter_pb2_grpc.GreeterStub(channel)`\n    stub = GreeterServicer() # Using the mock servicer for simplicity\n\n    print(\"Client sending request...\")\n    try:\n        response = stub.SayHello(greeter_pb2.HelloRequest(name='World'), context=None)\n        print(f\"Client received: {response.message}\")\n    except grpc.RpcError as e:\n        print(f\"Client received RPC error: {e.code()} - {e.details()}\")\n    print(\"Client finished.\")\n\nif __name__ == '__main__':\n    # This part requires a proper gRPC setup (proto files, generated code)\n    # For a truly runnable quickstart without proto generation, this is illustrative.\n    # To run this, you would typically run the server in one terminal and client in another.\n    # For demonstration, we'll just show the components.\n    \n    # As this cannot be fully 'runnable' without proto generation, \n    # consider this an illustrative quickstart. \n    # In a real scenario, you'd run `serve()` in one process and `run_client()` in another.\n    print(\"Quickstart shows client and server setup. Requires gRPC proto generation for full functionality.\")\n    print(\"To test metrics, run `serve()` in a separate process, then `run_client()`.\")\n    # Example of how you'd typically start them:\n    # import multiprocessing\n    # server_process = multiprocessing.Process(target=serve)\n    # server_process.start()\n    # time.sleep(2) # Give server time to start\n    # run_client()\n    # server_process.terminate()\n    # server_process.join()\n\n","lang":"python","description":"This quickstart demonstrates how to set up both gRPC server and client interceptors with `py-grpc-prometheus` to expose metrics. It includes starting a Prometheus HTTP server and enabling histogram metrics for latency tracking. Note: for a fully functional gRPC application, you would replace the placeholder `greeter_pb2` and `GreeterServicer` with generated code from your `.proto` definitions."},"warnings":[{"fix":"For AsyncIO gRPC services, use `pip install py-async-grpc-prometheus` and import `PromAsyncServerInterceptor` or `PromAsyncClientInterceptor` from that library.","message":"The core `PromServerInterceptor` and `PromClientInterceptor` are not compatible with Python gRPC's AsyncIO implementation. For AsyncIO support, you need to use the `py-async-grpc-prometheus` library instead.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Enable histograms during interceptor initialization by setting parameters like `enable_handling_time_histogram=True`, `enable_client_stream_receive_time_histogram=True`, or `enable_client_stream_send_time_histogram=True` for server/client interceptors.","message":"Prometheus histograms for gRPC call handling times (`grpc_server_handling_seconds`, `grpc_client_handling_seconds`, etc.) are disabled by default to prevent high cardinality issues. Latency metrics will not be collected unless explicitly enabled.","severity":"gotcha","affected_versions":"All versions"},{"fix":"This is expected behavior. Send at least one gRPC request to your instrumented service to see the metrics populate.","message":"When `prometheus_client.start_http_server()` is initialized, the `grpc_*` metrics will initially appear commented out (with descriptions) on the metrics endpoint. They will only start showing actual values after the gRPC application has processed its first calls.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Upgrade to version 0.8.0 or newer to ensure accurate reporting of RPCs that are terminated by `context.abort()` or `context.abort_with_status()`. Review your error rate dashboards if upgrading from older versions.","message":"Version 0.8.0 introduced explicit counting for `context.abort()` and `context.abort_with_status()` calls. In earlier versions, these gRPC cancellations might not have been correctly or consistently reflected in `grpc_server_handled_total` metrics, potentially undercounting errors or cancellations.","severity":"breaking","affected_versions":"<0.8.0"},{"fix":"Consider upgrading to a newer version (e.g., 0.7.0 or 0.8.0) or downgrading to a previous stable version if this issue affects your deployment. Monitor the GitHub issues for a specific fix for 0.6.0.","message":"Users of version 0.6.0 may encounter a `ValueError: Duplicated timeseries in CollectorRegistry`. This is a known open issue that can lead to metric collection failures.","severity":"gotcha","affected_versions":"0.6.0"},{"fix":"Upgrade to version 0.5.0 or newer to benefit from fixes related to exception handling within interceptors, ensuring more reliable and accurate error metrics.","message":"Prior to versions 0.4.0 and 0.5.0, there were issues with exception error code handling and allowing interceptor exceptions with configuration. This could lead to inconsistent or incorrect metric reporting for gRPC calls that result in exceptions.","severity":"gotcha","affected_versions":"<0.5.0"}],"env_vars":null,"last_verified":"2026-04-12T00:00:00.000Z","next_check":"2026-07-11T00:00:00.000Z"}