add 500 alert

This commit is contained in:
cheykrym 2026-08-06 00:50:22 +03:00
parent 6bd69cd7fb
commit 0d0db1209e
3 changed files with 103 additions and 4 deletions

View File

@ -1,6 +1,8 @@
import os
import uuid
import logging
import asyncio
import traceback
from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import JSONResponse
from fastapi.exceptions import RequestValidationError
@ -16,11 +18,101 @@ from starlette.status import (
)
from datetime import datetime
from config import settings
from common_lib.utils.http_client import client
# Настраиваем логирование
logging.basicConfig(level=logging.ERROR)
logger = logging.getLogger(__name__)
FEEDBACK_SERVICE_URL = "http://yobble-feedback-service:8000"
ERROR_LOG_ENVIRONMENT = "beta"
ERROR_LOG_METHODS = {"GET", "POST", "PUT", "PATCH", "DELETE"}
def _get_service_name() -> str:
return (
getattr(settings, "SERVICE_NAME", None)
or os.getenv("SERVICE_NAME")
or "unknown"
)
def _parse_uuid(value: str | None) -> str | None:
if not value:
return None
try:
return str(uuid.UUID(str(value)))
except ValueError:
return None
async def _read_request_body(request: Request):
try:
body = await request.body()
except Exception:
return None
if not body:
return None
content_type = request.headers.get("content-type", "")
if "application/json" in content_type:
try:
return await request.json()
except Exception:
pass
return {
"raw": body.decode("utf-8", errors="replace")
}
async def _send_internal_error_log(request: Request, exc: Exception, error_id: str) -> None:
service_name = _get_service_name()
if service_name == "feedback_service":
return
method = request.method.upper()
if method not in ERROR_LOG_METHODS:
method = "GET"
current_user = getattr(request.state, "current_user", None)
user_id = getattr(current_user, "user_id", None)
session_id = getattr(current_user, "session_id", None)
payload = {
"user_id": str(user_id) if user_id else None,
"service_name": service_name,
"source": "api",
"endpoint": request.url.path,
"method": method,
"http_status": 500,
"request_headers": dict(request.headers),
"request_body": await _read_request_body(request),
"request_query_params": dict(request.query_params),
"response_body": {
"status": "error",
"errors": [{"field": "server", "message": "Internal Server Error"}],
},
"ip_address": request.client.host if request.client else None,
"user_agent": request.headers.get("user-agent"),
"session_id": str(session_id) if session_id else _parse_uuid(request.headers.get("x-session-id")),
"error_message": str(exc),
"error_stack": "".join(traceback.format_exception(type(exc), exc, exc.__traceback__)),
"error_code": error_id,
"severity": "high",
"environment": ERROR_LOG_ENVIRONMENT,
"correlation_id": _parse_uuid(request.headers.get("x-request-id")),
}
try:
await client.post(
f"{FEEDBACK_SERVICE_URL}/internal/error-log",
json=payload,
)
except Exception as log_exc:
print("error auto log failed:", log_exc)
def register_error_handlers(app: FastAPI):
@ -156,6 +248,7 @@ def register_error_handlers(app: FastAPI):
)
print("500 critical error:", full_log_message)
asyncio.create_task(_send_internal_error_log(request, exc, error_id))
# with open(log_filename, "w", encoding="utf-8") as log_file:
# log_file.write(full_log_message)

View File

@ -183,7 +183,9 @@ async def get_current_user(
detail="Not authenticated"
)
return await _fetch_current_user(request, token, require_permissions=False, is_web=is_web)
current_user = await _fetch_current_user(request, token, require_permissions=False, is_web=is_web)
request.state.current_user = current_user
return current_user
async def get_current_user_with_permissions(
@ -205,7 +207,9 @@ async def get_current_user_with_permissions(
detail="Not authenticated"
)
return await _fetch_current_user(request, token, require_permissions=True, is_web=is_web)
current_user = await _fetch_current_user(request, token, require_permissions=True, is_web=is_web)
request.state.current_user = current_user
return current_user
async def get_current_user_or_bot(
@ -229,7 +233,9 @@ async def get_current_user_or_bot(
)
is_bot_flag = str(is_bot).lower() in ("true", "1", "yes") if is_bot else False
return await _fetch_current_user(request, token, require_permissions=False, is_bot=is_bot_flag, is_web=is_web)
current_user = await _fetch_current_user(request, token, require_permissions=False, is_bot=is_bot_flag, is_web=is_web)
request.state.current_user = current_user
return current_user
def validate_username(value: str,

View File

@ -1,6 +1,6 @@
[project]
name = "common-lib"
version = "0.0.57"
version = "0.0.58"
description = "Библиотека общих компонентов для микросервисов yobble"
authors = [{ name = "cheykrym", email = "you@example.com" }]
license = "MIT"