add health check endpoint
This commit is contained in:
parent
38eef0e33e
commit
b7a78ba140
2 changed files with 54 additions and 1 deletions
53
ipserv.py
53
ipserv.py
|
@ -1,7 +1,7 @@
|
||||||
import requests
|
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
|
import os
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import random
|
import random
|
||||||
from fastapi import FastAPI, Header, Request, Query, HTTPException
|
from fastapi import FastAPI, Header, Request, Query, HTTPException
|
||||||
|
@ -11,6 +11,23 @@ from contextlib import asynccontextmanager
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
|
|
||||||
|
class Manager:
|
||||||
|
def __init__(self, api: FastAPI):
|
||||||
|
self.app = api
|
||||||
|
self.waiting = 0
|
||||||
|
|
||||||
|
def __enter__(self) -> "Manager":
|
||||||
|
self.waiting += 1
|
||||||
|
if self.waiting >= 2048:
|
||||||
|
logging.critical("TCP pool full! %d/2048. Requests are now being backlogged.")
|
||||||
|
elif self.waiting > 1024:
|
||||||
|
logging.warning("TCP pool half full! %d/2048.")
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, *args):
|
||||||
|
self.waiting -= 1
|
||||||
|
|
||||||
|
|
||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
async def lifespan(_app: FastAPI):
|
async def lifespan(_app: FastAPI):
|
||||||
async with aiohttp.ClientSession(
|
async with aiohttp.ClientSession(
|
||||||
|
@ -24,6 +41,15 @@ async def lifespan(_app: FastAPI):
|
||||||
|
|
||||||
app = FastAPI(lifespan=lifespan)
|
app = FastAPI(lifespan=lifespan)
|
||||||
app.state.cache = {}
|
app.state.cache = {}
|
||||||
|
app.state.meta = Manager(app)
|
||||||
|
|
||||||
|
|
||||||
|
@app.middleware("http")
|
||||||
|
async def http_middleware(request: Request, call_next):
|
||||||
|
if app.state.meta.waiting >= 2512:
|
||||||
|
return JSONResponse({"error": os.urandom(512).decode("latin-1", "replace")}, status_code=503)
|
||||||
|
with app.state.meta:
|
||||||
|
return await call_next(request)
|
||||||
|
|
||||||
|
|
||||||
async def make_request(ip: str, headers: dict[str, str]) -> dict | HTTPException:
|
async def make_request(ip: str, headers: dict[str, str]) -> dict | HTTPException:
|
||||||
|
@ -45,6 +71,7 @@ async def make_request(ip: str, headers: dict[str, str]) -> dict | HTTPException
|
||||||
return HTTPException(500, "Failed to get upstream data.")
|
return HTTPException(500, "Failed to get upstream data.")
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
@app.get("/")
|
@app.get("/")
|
||||||
async def ip(
|
async def ip(
|
||||||
request: Request,
|
request: Request,
|
||||||
|
@ -125,3 +152,27 @@ async def im_feeling_lucky(req: Request):
|
||||||
@app.get("/raw")
|
@app.get("/raw")
|
||||||
def get_raw(req: Request):
|
def get_raw(req: Request):
|
||||||
return PlainTextResponse(req.client.host)
|
return PlainTextResponse(req.client.host)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/health")
|
||||||
|
def get_health():
|
||||||
|
detail = {"issues": []}
|
||||||
|
if app.state.meta.waiting >= 2048:
|
||||||
|
detail["status"] = "critical"
|
||||||
|
detail["issues"].append("(C) Connection pool full.")
|
||||||
|
elif app.state.meta.waiting >= 1024:
|
||||||
|
detail["status"] = "warning"
|
||||||
|
detail["issues"].append("(W) TCP pool half full.")
|
||||||
|
else:
|
||||||
|
detail["status"] = "ok"
|
||||||
|
|
||||||
|
try:
|
||||||
|
t = time.perf_counter()
|
||||||
|
async with app.state.session.get("/") as response:
|
||||||
|
await response.text()
|
||||||
|
detail["latency"] = time.perf_counter() - t
|
||||||
|
except Exception as e:
|
||||||
|
detail["issues"].append(f"(E) Failed to check upstream: {e}")
|
||||||
|
detail["status"] = "critical"
|
||||||
|
|
||||||
|
return JSONResponse(detail)
|
||||||
|
|
2
tox.ini
Normal file
2
tox.ini
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[flake8]
|
||||||
|
max-line-length = 128
|
Reference in a new issue