mirror of
https://git.ianrenton.com/ian/spothole.git
synced 2026-09-20 22:37:44 +00:00
Extract webserver metrics into a separate class to avoid passing it into every API call. Change the display to requests per hour rather than just last request time. Add SSE and telnet client connected count.
This commit is contained in:
+36
-37
@@ -33,6 +33,7 @@ from webserver.handlers.manifesthandler import ManifestHandler
|
||||
from webserver.handlers.metrics import PrometheusMetricsHandler
|
||||
from webserver.handlers.pagetemplate import PageTemplateHandler
|
||||
from webserver.sse_broadcaster import SSEBroadcaster
|
||||
from webserver.webserver_metrics import WebServerMetrics
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -52,19 +53,19 @@ class WebServer:
|
||||
self._port = WEB_SERVER_PORT
|
||||
self._api_only_mode = API_ONLY_MODE
|
||||
self._shutdown_event = asyncio.Event()
|
||||
self.web_server_metrics = {
|
||||
"last_page_access_time": None,
|
||||
"last_api_access_time": None,
|
||||
"page_access_counter": 0,
|
||||
"api_access_counter": 0,
|
||||
"status": "Starting",
|
||||
}
|
||||
self.web_server_metrics = WebServerMetrics()
|
||||
|
||||
def setup(self):
|
||||
# Listen for new spots and alerts being added to the cache, so we can notify SSE clients immediately
|
||||
DATA_STORE.spots.add_listener(self._spot_broadcaster.publish)
|
||||
DATA_STORE.alerts.add_listener(self._alert_broadcaster.publish)
|
||||
|
||||
@property
|
||||
def sse_client_count(self) -> int:
|
||||
"""Number of connected SSE clients, across both the spots and alerts streams."""
|
||||
|
||||
return self._spot_broadcaster.client_count + self._alert_broadcaster.client_count
|
||||
|
||||
def start(self):
|
||||
"""Start the web server"""
|
||||
|
||||
@@ -82,62 +83,57 @@ class WebServer:
|
||||
self._spot_broadcaster.bind_to_web_server_loop()
|
||||
self._alert_broadcaster.bind_to_web_server_loop()
|
||||
|
||||
# Prepare a list of common arguments that are passed in to every API & page handler. This is just a basic thing
|
||||
# to avoid copy-pasting the same thing to every route declaration below.
|
||||
handler_opts = {"web_server_metrics": self.web_server_metrics}
|
||||
|
||||
# API endpoints are always enabled
|
||||
api_routes = [
|
||||
(
|
||||
r"/api/v2/spots",
|
||||
APISpotsHandler,
|
||||
{"spots": self._data_store.spots, **handler_opts},
|
||||
{"spots": self._data_store.spots},
|
||||
),
|
||||
(
|
||||
r"/api/v2/alerts",
|
||||
APIAlertsHandler,
|
||||
{"alerts": self._data_store.alerts, **handler_opts},
|
||||
{"alerts": self._data_store.alerts},
|
||||
),
|
||||
(
|
||||
r"/api/v2/spots/stream",
|
||||
APISpotsStreamHandler,
|
||||
{"sse_spot_broadcaster": self._spot_broadcaster, **handler_opts},
|
||||
{"sse_spot_broadcaster": self._spot_broadcaster},
|
||||
),
|
||||
(
|
||||
r"/api/v2/alerts/stream",
|
||||
APIAlertsStreamHandler,
|
||||
{"sse_alert_broadcaster": self._alert_broadcaster, **handler_opts},
|
||||
{"sse_alert_broadcaster": self._alert_broadcaster},
|
||||
),
|
||||
(
|
||||
r"/api/v2/solar",
|
||||
APISolarConditionsHandler,
|
||||
{"solar_conditions": self._data_store.solar_conditions.get(), **handler_opts},
|
||||
{"solar_conditions": self._data_store.solar_conditions.get()},
|
||||
),
|
||||
(
|
||||
r"/api/v2/dxstats",
|
||||
APIDxStatsHandler,
|
||||
{"spots": self._data_store.spots, **handler_opts},
|
||||
{"spots": self._data_store.spots},
|
||||
),
|
||||
(
|
||||
r"/api/v2/options",
|
||||
APIOptionsHandler,
|
||||
{"status_data": self._data_store.status.get(), **handler_opts},
|
||||
{"status_data": self._data_store.status.get()},
|
||||
),
|
||||
(
|
||||
r"/api/v2/status",
|
||||
APIStatusHandler,
|
||||
{"status_data": self._data_store.status.get(), **handler_opts},
|
||||
{"status_data": self._data_store.status.get()},
|
||||
),
|
||||
(r"/api/v2/lookup/call", APILookupCallHandler, {**handler_opts}),
|
||||
(r"/api/v2/lookup/sigref", APILookupSIGRefHandler, {**handler_opts}),
|
||||
(r"/api/v2/lookup/grid", APILookupGridHandler, {**handler_opts}),
|
||||
(r"/api/v2/lookup/call", APILookupCallHandler),
|
||||
(r"/api/v2/lookup/sigref", APILookupSIGRefHandler),
|
||||
(r"/api/v2/lookup/grid", APILookupGridHandler),
|
||||
(
|
||||
r"/api/v2/spot",
|
||||
APISpotHandler,
|
||||
{
|
||||
"spots": self._data_store.spots,
|
||||
"spot_providers": self._data_providers,
|
||||
**handler_opts,
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -148,19 +144,18 @@ class WebServer:
|
||||
(
|
||||
r"/api/v1/spots",
|
||||
V1APISpotsHandler,
|
||||
{"spots": self._data_store.spots, **handler_opts},
|
||||
{"spots": self._data_store.spots},
|
||||
),
|
||||
(
|
||||
r"/api/v1/spots/stream",
|
||||
V1APISpotsStreamHandler,
|
||||
{"sse_spot_broadcaster": self._spot_broadcaster, **handler_opts},
|
||||
{"sse_spot_broadcaster": self._spot_broadcaster},
|
||||
),
|
||||
(
|
||||
r"/api/v1/spot",
|
||||
V1APISpotHandler,
|
||||
{
|
||||
"spots": self._data_store.spots,
|
||||
**handler_opts,
|
||||
},
|
||||
),
|
||||
(r"/api/v1/(.*)", V1RedirectHandler),
|
||||
@@ -173,41 +168,41 @@ class WebServer:
|
||||
(
|
||||
r"/",
|
||||
PageTemplateHandler,
|
||||
{"template_name": "api_only_home", **handler_opts},
|
||||
{"template_name": "api_only_home"},
|
||||
)
|
||||
]
|
||||
else:
|
||||
ui_routes = [
|
||||
(r"/", PageTemplateHandler, {"template_name": "spots", **handler_opts}),
|
||||
(r"/", PageTemplateHandler, {"template_name": "spots"}),
|
||||
(
|
||||
r"/map",
|
||||
PageTemplateHandler,
|
||||
{"template_name": "map", **handler_opts},
|
||||
{"template_name": "map"},
|
||||
),
|
||||
(
|
||||
r"/bands",
|
||||
PageTemplateHandler,
|
||||
{"template_name": "bands", **handler_opts},
|
||||
{"template_name": "bands"},
|
||||
),
|
||||
(
|
||||
r"/alerts",
|
||||
PageTemplateHandler,
|
||||
{"template_name": "alerts", **handler_opts},
|
||||
{"template_name": "alerts"},
|
||||
),
|
||||
(
|
||||
r"/conditions",
|
||||
PageTemplateHandler,
|
||||
{"template_name": "conditions", **handler_opts},
|
||||
{"template_name": "conditions"},
|
||||
),
|
||||
(
|
||||
r"/status",
|
||||
PageTemplateHandler,
|
||||
{"template_name": "status", **handler_opts},
|
||||
{"template_name": "status"},
|
||||
),
|
||||
(
|
||||
r"/about",
|
||||
PageTemplateHandler,
|
||||
{"template_name": "about", **handler_opts},
|
||||
{"template_name": "about"},
|
||||
),
|
||||
]
|
||||
# Only allow the Add Spot page if spotting is allowed
|
||||
@@ -216,7 +211,7 @@ class WebServer:
|
||||
(
|
||||
r"/add-spot",
|
||||
PageTemplateHandler,
|
||||
{"template_name": "add_spot", **handler_opts},
|
||||
{"template_name": "add_spot"},
|
||||
)
|
||||
]
|
||||
|
||||
@@ -226,7 +221,7 @@ class WebServer:
|
||||
(
|
||||
r"/apidocs",
|
||||
PageTemplateHandler,
|
||||
{"template_name": "apidocs", **handler_opts},
|
||||
{"template_name": "apidocs"},
|
||||
),
|
||||
(r"/metrics", PrometheusMetricsHandler),
|
||||
(r"/manifest.webmanifest", ManifestHandler),
|
||||
@@ -254,7 +249,11 @@ class WebServer:
|
||||
|
||||
def request_log(handler):
|
||||
"""Custom log function to provide more data about requests when enabled, and to provide the ability to turn off
|
||||
web request logging altogetether."""
|
||||
web request logging altogetether. Also records the time of the request and status in the webserver metrics. Probably
|
||||
not what this method is supposed to be used for but it's a convenient thing that gets called on every request, so
|
||||
saves having to pass the metrics around each handler individually."""
|
||||
|
||||
WEB_SERVER.web_server_metrics.record(handler.request.path, handler.get_status())
|
||||
|
||||
if LOG_WEB_REQUESTS:
|
||||
if handler.get_status() < 500:
|
||||
|
||||
Reference in New Issue
Block a user