make prometheus_client work with TCPServer to collect metrics

22 views Asked by At

I have this tcpserver.py

class Server(TCPServer):
    allow_reuse_address = True
    # The constant would be better initialized by a systemd module
    SYSTEMD_FIRST_SOCKET_FD = 3

    def __init__(self, server_address, handler_cls, bind_and_activate=True):
        self.handlers = set()
        # Invoke base but omit bind/listen steps (performed by systemd activation!)
        try:
            TCPServer.__init__(self, server_address, handler_cls, bind_and_activate)
        except TypeError:
            TCPServer.__init__(self, server_address, handler_cls)

        # Override socket
        self.socket = socket.fromfd(
            self.SYSTEMD_FIRST_SOCKET_FD, self.address_family, self.socket_type)

        if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'):
            flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
            flags |= fcntl.FD_CLOEXEC
            fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags)

    def server_bind(self):
        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.socket.bind(self.server_address)

    def server_close(self):
        TCPServer.server_close(self)
        _LOG.debug("Shutting down server.")
        for handler in self.handlers.copy():
            self.shutdown_request(handler.request)


def main():
    """ Starts TCPServer.
    """
    logging.basicConfig(level=logging.DEBUG)
    server = ("", int(TCP_PORT))
    # Create a TCP Server instance
    server = Server(server, TCPServerRequestHandler)
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        sys.exit(0)

then I made request_handler.py:

from prometheus_client import start_http_server, Gauge, Counter, Histogram


# Define Prometheus metrics
client_connections = Gauge('tcp_server_client_connections', 'Number of client connections')
client_names = Gauge('tcp_server_client_names', 'Names of connected clients', labelnames=['client'])


class TCPServerRequestHandler(RequestHandler):

    def handle(self):
        """Receives data from client.
        """
        start_time = time.time()
        msg = self.request.recv(1024).strip()
        if self.client_address and not msg:
            _LOG.error("No Data revieved from Client: {}".format(self.client_address[0]))
            return
        client_address = self.client_address[0]

        decoded_msg = msg.decode('utf-8')
        client_connections.inc()
        client_names.labels(client=client_address).set(1)

        try:
            data = ast.literal_eval(decoded_msg)
        except ValueError:
            data = decoded_msg

        if data and isinstance(data, dict):
            _LOG.debug("Data received is a dict.")
            # TODO: Action to perform.
            _type = 'dictionary'
        elif isinstance(data, str):
            _LOG.debug("str Data recieved.")
            _type = 'string'
        else:
            _LOG.error("No data recieved.")
        time_taken = time.time() - start_time
        connection_duration.observe(time_taken)
        self.request.sendall(f"Acknowledged data received of type {_type} and took {time_taken} to process".encode())

def main():
    # Start Prometheus HTTP server on port 8000
    start_http_server(8035)

and just calling the main function above the Prometheus http server doesn't start. So I went back to this three step demo https://github.com/prometheus/client_python/blob/master/docs/content/getting-started/three-step-demo.md

I spotted that after it starts the HTTP server: start_http_server(8000) it uses while loop and call the function inside that while loop! which I tested and works as given in the example given in the three step demo.

So my question is: does that mean I would have to call the

server = Server(server, TCPServerRequestHandler)
server.serve_forever()

inside the while block of tcpserver.py

I was hoping to keep the exporter and tcpserver separate!

0

There are 0 answers