Implementations of Prometheus Client

This commit is contained in:
Silversthorn 2023-09-03 16:04:28 +02:00
parent 57dc58099b
commit eb6d9560f8
7 changed files with 116 additions and 1 deletions

View File

@ -3,11 +3,14 @@ import logging
import datetime
from datetime import timedelta
from prometheus_client import Gauge
from app.classes.models.servers import Servers, HelperServers
from app.classes.shared.helpers import Helpers
from app.classes.shared.main_models import DatabaseShortcuts
from app.classes.shared.migration import MigrationManager
try:
from peewee import (
SqliteDatabase,
@ -29,6 +32,13 @@ logger = logging.getLogger(__name__)
peewee_logger = logging.getLogger("peewee")
peewee_logger.setLevel(logging.INFO)
# REGISTRY Entries for Server Stats functions
ONLINE_PLAYERS = Gauge(
name="online_players",
documentation="The number of players online for a server",
labelnames=["server_id"],
)
# **********************************************************************************
# Servers Stats Class
@ -157,6 +167,8 @@ class HelperServerStats:
self.database.connect(reuse_if_open=True)
server_id = server_stats.get("id", 0)
ONLINE_PLAYERS.labels(f"{self.server_id}").set(server_stats.get("online"))
if server_id == 0:
logger.warning("Stats saving failed with error: Server unknown (id = 0)")
return

View File

@ -0,0 +1,50 @@
import logging
import typing as t
from prometheus_client import REGISTRY, CollectorRegistry
from prometheus_client.exposition import _bake_output
from prometheus_client.exposition import parse_qs, urlparse
from app.classes.web.base_api_handler import BaseApiHandler
logger = logging.getLogger(__name__)
class BaseMetricsHandler(BaseApiHandler):
"""HTTP handler that gives metrics from ``REGISTRY``."""
registry: CollectorRegistry = REGISTRY
def get_registry(self) -> None:
# Prepare parameters
registry = self.registry
accept_header = self.request.headers.get("Accept")
accept_encoding_header = self.request.headers.get("Accept-Encoding")
params = parse_qs(urlparse(self.request.path).query)
# Bake output
status, headers, output = _bake_output(
registry, accept_header, accept_encoding_header, params, False
)
# Return output
self.finish_metrics(int(status.split(" ", maxsplit=1)[0]), headers, output)
@classmethod
def factory(cls, registry: CollectorRegistry) -> type:
"""Returns a dynamic MetricsHandler class tied
to the passed registry.
"""
# This implementation relies on MetricsHandler.registry
# (defined above and defaulted to REGISTRY).
# As we have unicode_literals, we need to create a str()
# object for type().
cls_name = str(cls.__name__)
MyMetricsHandler = type(cls_name, (cls, object), {"registry": registry})
return MyMetricsHandler
def finish_metrics(self, status: int, headers, data: t.Dict[str, t.Any]):
self.set_status(status)
self.set_header("Content-Type", "text/plain")
for header in headers:
self.set_header(*header)
self.finish(data)

View File

@ -0,0 +1,19 @@
from prometheus_client import Info
from app.classes.web.metrics_handler import BaseMetricsHandler
CRAFTY_INFO = Info("Crafty_Controller", "Infos of this Crafty Instance")
# Decorate function with metric.
class ApiOpenMetricsIndexHandler(BaseMetricsHandler):
def get(self):
auth_data = self.authenticate_user()
if not auth_data:
return
version = f"{self.helper.get_version().get('major')}.{self.helper.get_version().get('minor')}.{self.helper.get_version().get('sub')}"
CRAFTY_INFO.info(
{"version": version, "docker": f"{self.helper.is_env_docker()}"}
)
self.get_registry()

View File

@ -0,0 +1,18 @@
from app.classes.web.routes.metrics.index import ApiOpenMetricsIndexHandler
from app.classes.web.routes.metrics.servers import ApiOpenMetricsServersHandler
def metrics_handlers(handler_args):
return [
# OpenMetrics routes
(
r"/metrics?",
ApiOpenMetricsIndexHandler,
handler_args,
),
(
r"/metrics/servers/(0-9)+?",
ApiOpenMetricsServersHandler,
handler_args,
),
]

View File

@ -0,0 +1,12 @@
from prometheus_client import Histogram
from app.classes.web.metrics_handler import BaseMetricsHandler
# Decorate function with metric.
class ApiOpenMetricsServersHandler(BaseMetricsHandler):
def get(self):
auth_data = self.authenticate_user()
if not auth_data:
return
self.get_registry()

View File

@ -20,6 +20,7 @@ from app.classes.web.public_handler import PublicHandler
from app.classes.web.panel_handler import PanelHandler
from app.classes.web.default_handler import DefaultHandler
from app.classes.web.routes.api.api_handlers import api_handlers
from app.classes.web.routes.metrics.metrics_handlers import metrics_handlers
from app.classes.web.server_handler import ServerHandler
from app.classes.web.ajax_handler import AjaxHandler
from app.classes.web.api_handler import (
@ -169,6 +170,8 @@ class Webserver:
(r"/api/v1/users/delete_user", DeleteUser, handler_args),
# API Routes V2
*api_handlers(handler_args),
# API Routes OpenMetrics
*metrics_handlers(handler_args),
# Using this one at the end
# to catch all the other requests to Public Handler
(r"/(.*)", PublicHandler, handler_args),

View File

@ -9,7 +9,7 @@ cryptography==41.0.1
libgravatar==1.0.0
peewee==3.13
pexpect==4.8
psutil==5.9
psutil==5.9.5
pyOpenSSL==23.2.0
pyjwt==2.4.0
PyYAML==6.0.1
@ -19,3 +19,4 @@ tornado==6.3.2
tzlocal==4.0
jsonschema==4.5.1
orjson==3.8.12
prometheus-client==0.17.1