All checks were successful
Build Docker Image / build-and-deploy-image (push) Successful in 38s
91 lines
2.7 KiB
Python
91 lines
2.7 KiB
Python
import asyncio
|
|
import contextlib
|
|
import os
|
|
|
|
import aiofiles
|
|
import orjson
|
|
from environs import Env
|
|
|
|
env = Env()
|
|
env.read_env()
|
|
|
|
unit_log_file: str = env.str("UNIT_LOG_FILE")
|
|
|
|
|
|
async def process_log_entry(log_entry):
|
|
with contextlib.suppress(Exception):
|
|
log_data = orjson.loads(log_entry)
|
|
request_time = float(log_data["request_time"])
|
|
url = log_data["url"]
|
|
http_method = log_data["http_method"]
|
|
key = (url, http_method)
|
|
return key, {"count": 1, "sum": request_time}
|
|
return None
|
|
|
|
|
|
async def read_and_process_logs():
|
|
metrics_data = {}
|
|
|
|
if os.path.exists(unit_log_file):
|
|
async with aiofiles.open(unit_log_file, mode="r+") as f:
|
|
lines = await f.readlines()
|
|
await f.truncate(0)
|
|
|
|
tasks = [process_log_entry(line) for line in lines]
|
|
results = await asyncio.gather(*tasks)
|
|
|
|
for result in results:
|
|
if not result:
|
|
continue
|
|
key, data = result
|
|
if key in metrics_data:
|
|
metrics_data[key]["count"] += data["count"]
|
|
metrics_data[key]["sum"] += data["sum"]
|
|
else:
|
|
metrics_data[key] = data
|
|
|
|
return metrics_data
|
|
|
|
|
|
async def generate_metrics_text():
|
|
lines = [
|
|
"# HELP request_duration_seconds_total Request duration in seconds",
|
|
"# TYPE request_duration_seconds_total counter",
|
|
"# HELP request_duration_seconds_count Number of requests",
|
|
"# TYPE request_duration_seconds_count counter",
|
|
]
|
|
|
|
metrics_data = await read_and_process_logs()
|
|
|
|
for key, data in metrics_data.items():
|
|
url, http_method = key
|
|
count = data["count"]
|
|
sum_values = data["sum"]
|
|
|
|
labels = f'url="{url}",http_method="{http_method}"'
|
|
|
|
lines.append(f"request_duration_seconds_count{{{labels}}} {count}")
|
|
lines.append(f"request_duration_seconds_total{{{labels}}} {sum_values}")
|
|
|
|
return "\n".join(lines) + "\n"
|
|
|
|
|
|
async def app(scope, receive, send):
|
|
if scope["type"] == "http":
|
|
if scope["path"] == "/metrics":
|
|
headers = [(b"content-type", b"text/plain; charset=utf-8")]
|
|
metrics_text = await generate_metrics_text()
|
|
body = metrics_text.encode("utf-8")
|
|
|
|
await send({"type": "http.response.start", "status": 200, "headers": headers})
|
|
await send({"type": "http.response.body", "body": body})
|
|
else:
|
|
await send(
|
|
{
|
|
"type": "http.response.start",
|
|
"status": 404,
|
|
"headers": [(b"content-type", b"text/plain; charset=utf-8")],
|
|
}
|
|
)
|
|
await send({"type": "http.response.body", "body": b"Not Found"})
|