mweinelt / kea-exporter

Export Kea Metrics in the Prometheus Exposition Format
MIT License
34 stars 17 forks source link

Make interval optional #38

Closed mweinelt closed 8 months ago

mweinelt commented 1 year ago

Query kea only when asked, so the timestamping on the data in Prometheus is correct.

This means dropping interval by default.

Qwiko commented 9 months ago

In the upcoming version of prometheus_client start_http_server returns the objects WSGIServer and thread.

From: https://github.com/prometheus/client_python/commit/b9edc43221101cad593c64d3fe9853760bef135e

def start_wsgi_server(
        port: int,
        addr: str = '0.0.0.0',
        registry: CollectorRegistry = REGISTRY,
        certfile: Optional[str] = None,
        keyfile: Optional[str] = None,
        client_cafile: Optional[str] = None,
        client_capath: Optional[str] = None,
        protocol: int = ssl.PROTOCOL_TLS_SERVER,
        client_auth_required: bool = False,
) -> Tuple[WSGIServer, threading.Thread]:
    """Starts a WSGI server for prometheus metrics as a daemon thread."""

    class TmpServer(ThreadingWSGIServer):
        """Copy of ThreadingWSGIServer to update address_family locally"""

    TmpServer.address_family, addr = _get_best_family(addr, port)
    app = make_wsgi_app(registry)
    httpd = make_server(addr, port, app, TmpServer, handler_class=_SilentHandler)
    if certfile and keyfile:
        context = _get_ssl_ctx(certfile, keyfile, protocol, client_cafile, client_capath, client_auth_required)
        httpd.socket = context.wrap_socket(httpd.socket, server_side=True)
    t = threading.Thread(target=httpd.serve_forever)
    t.daemon = True
    t.start()

    return httpd, t

start_http_server = start_wsgi_server

Taking inspiration from prometheus clients make_wsgi_app function

def make_wsgi_app(registry: CollectorRegistry = REGISTRY, disable_compression: bool = False) -> Callable:
    """Create a WSGI app which serves the metrics from a registry."""

    def prometheus_app(environ, start_response):
        # Prepare parameters
        accept_header = environ.get('HTTP_ACCEPT')
        accept_encoding_header = environ.get('HTTP_ACCEPT_ENCODING')
        params = parse_qs(environ.get('QUERY_STRING', ''))
        if environ['PATH_INFO'] == '/favicon.ico':
            # Serve empty response for browsers
            status = '200 OK'
            headers = [('', '')]
            output = b''
        else:
            # Bake output
            status, headers, output = _bake_output(registry, accept_header, accept_encoding_header, params, disable_compression)
        # Return output
        start_response(status, headers)
        return [output]

    return prometheus_app

We could wrap the make_wsgi_app function with our own that executes exporter.update() before every request instead of updating per interval. Then we should get the latest data every GET request.

from prometheus_client import REGISTRY, make_wsgi_app, start_http_server
...

httpd, _ = start_http_server(port, address)

def local_wsgi_app(registry):
    func = make_wsgi_app(registry, False)
    def app(environ, start_response):
        exporter.update()
        output_array = func(environ, start_response)
        return output_array
    return app

httpd.set_app(local_wsgi_app(REGISTRY))

click.echo(f"Listening on http://{address}:{port}")

while True:
    time.sleep(1)

In my opinion if something like this is implemented interval should be removed completely.

Qwiko commented 8 months ago

Prometheus client_python have now been released to version 0.20.0. https://github.com/prometheus/client_python/tree/v0.20.0.

I can make a pull request with my suggested changes.

mweinelt commented 8 months ago

Closed via #45.