🎨 Improve error push

This commit is contained in:
洛水居室 2022-11-21 14:57:12 +08:00 committed by GitHub
parent 60b8e46924
commit bda5f4fdd0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 128 additions and 79 deletions

View File

@ -47,7 +47,7 @@ LOGGER_LOCALS_MAX_DEPTH=0
LOGGER_LOCALS_MAX_LENGTH=10 LOGGER_LOCALS_MAX_LENGTH=10
LOGGER_LOCALS_MAX_STRING=80 LOGGER_LOCALS_MAX_STRING=80
# 可被 logger 打印的 record 的名称(默认包含了 LOGGER_NAME # 可被 logger 打印的 record 的名称(默认包含了 LOGGER_NAME
LOGGER_FILTERED_NAMES=["uvicorn"] LOGGER_FILTERED_NAMES=["uvicorn","ErrorPush"]
# 超时配置 可选配置项 # 超时配置 可选配置项

View File

@ -1,50 +0,0 @@
import os
import sentry_sdk
from git.repo import Repo
from git.repo.fun import rev_parse
from sentry_sdk.integrations.excepthook import ExcepthookIntegration
from sentry_sdk.integrations.httpx import HttpxIntegration
from sentry_sdk.integrations.logging import LoggingIntegration
from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration
from telegram import Update
from core.config import config
from utils.log import logger
repo = Repo(os.getcwd())
sentry_sdk_git_hash = rev_parse(repo, "HEAD").hexsha
sentry_sdk.init(
config.error.sentry_dsn,
traces_sample_rate=1.0,
release=sentry_sdk_git_hash,
environment="production",
integrations=[
HttpxIntegration(),
ExcepthookIntegration(always_run=False),
LoggingIntegration(event_level=50),
SqlalchemyIntegration(),
],
)
class Sentry:
@staticmethod
def report_error(update: object, exc_info):
if not config.error.sentry_dsn:
return
logger.info("正在上传日记到 sentry")
message: str = ""
chat_id: int = 0
user_id: int = 0
if isinstance(update, Update):
if update.effective_user:
chat_id = update.effective_user.id
if update.effective_chat:
user_id = update.effective_chat.id
if update.effective_message:
if update.effective_message.text:
message = update.effective_message.text
sentry_sdk.set_context("Target", {"ChatID": str(chat_id), "UserID": str(user_id), "Msg": message})
sentry_sdk.capture_exception(exc_info)
logger.success("上传日记到 sentry 成功")

View File

@ -0,0 +1,5 @@
from .pb import PbClient, PbClientException
from .sentry import SentryClient, SentryClientException
__all__ = ["PbClient", "PbClientException", "SentryClient", "SentryClientException"]

View File

@ -0,0 +1,3 @@
import logging
logger = logging.getLogger("ErrorPush")

View File

@ -2,23 +2,42 @@ from typing import Optional
import httpx import httpx
from core.config import config
from utils.log import logger from utils.log import logger
__all__ = ["PbClient", "PbClientException"]
class PbClientException(Exception):
pass
class PbClient: class PbClient:
def __init__(self): def __init__(self, pb_url: Optional[str] = None, pb_sunset: Optional[int] = None, pb_max_lines: int = 1000):
"""PbClient
:param pb_url:
:param pb_sunset: 自动销毁时间 单位为秒
:param pb_max_lines:
"""
self.client = httpx.AsyncClient() self.client = httpx.AsyncClient()
self.PB_API = config.error.pb_url self.PB_API = pb_url
self.sunset: int = config.error.pb_sunset # 自动销毁时间 单位为秒 self.sunset: int = pb_sunset
self.private: bool = True self.private: bool = True
self.max_lines: int = config.error.pb_max_lines self.max_lines: int = pb_max_lines
@property
def enabled(self) -> bool:
return bool(self.PB_API)
async def create_pb(self, content: str) -> Optional[str]: async def create_pb(self, content: str) -> Optional[str]:
try:
return await self._create_pb(content)
except Exception as exc:
raise PbClientException from exc
async def _create_pb(self, content: str) -> Optional[str]:
if not self.PB_API: if not self.PB_API:
return None return None
logger.info("正在上传日记到 pb") content = "\n".join(content.splitlines()[-self.max_lines :]) + "\n"
content = "\n".join(content.splitlines()[-self.max_lines:]) + "\n"
data = { data = {
"c": content, "c": content,
} }
@ -30,5 +49,4 @@ class PbClient:
if result.is_error: if result.is_error:
logger.warning("上传日记到 pb 失败 status_code[%s]", result.status_code) logger.warning("上传日记到 pb 失败 status_code[%s]", result.status_code)
return None return None
logger.success("上传日记到 pb 成功")
return result.headers.get("location") return result.headers.get("location")

View File

@ -0,0 +1,63 @@
import os
from typing import Optional
import sentry_sdk
from git.repo import Repo
from git.repo.fun import rev_parse
from sentry_sdk.integrations.excepthook import ExcepthookIntegration
from sentry_sdk.integrations.httpx import HttpxIntegration
from sentry_sdk.integrations.logging import LoggingIntegration
from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration
from telegram import Update
__all__ = ["SentryClient", "SentryClientException"]
class SentryClientException(Exception):
pass
class SentryClient:
def __init__(self, sentry_dsn: Optional[str] = None):
self.sentry_dsn = sentry_dsn
if sentry_dsn:
repo = Repo(os.getcwd())
sentry_sdk_git_hash = rev_parse(repo, "HEAD").hexsha
sentry_sdk.init(
sentry_dsn,
traces_sample_rate=1.0,
release=sentry_sdk_git_hash,
environment="production",
integrations=[
HttpxIntegration(),
ExcepthookIntegration(always_run=False),
LoggingIntegration(event_level=50),
SqlalchemyIntegration(),
],
)
@property
def enabled(self) -> bool:
return bool(self.sentry_dsn)
def report_error(self, update: object, exc_info):
try:
return self._report_error(update, exc_info)
except Exception as exc:
raise SentryClientException from exc
def _report_error(self, update: object, exc_info):
if not self.sentry_dsn:
return
message: str = ""
chat_id: int = 0
user_id: int = 0
if isinstance(update, Update):
if update.effective_user:
chat_id = update.effective_user.id
if update.effective_chat:
user_id = update.effective_chat.id
if update.effective_message and update.effective_message.text:
message = update.effective_message.text
sentry_sdk.set_context("Target", {"ChatID": str(chat_id), "UserID": str(user_id), "Msg": message})
sentry_sdk.capture_exception(exc_info)

View File

@ -10,9 +10,9 @@ from telegram.error import BadRequest, Forbidden
from telegram.ext import CallbackContext from telegram.ext import CallbackContext
from core.bot import bot from core.bot import bot
from core.config import config
from core.plugin import Plugin, error_handler from core.plugin import Plugin, error_handler
from modules.error.pb import PbClient from modules.errorpush import PbClient, SentryClient, PbClientException, SentryClientException
from modules.error.sentry import Sentry
from utils.log import logger from utils.log import logger
notice_chat_id = bot.config.error.notification_chat_id notice_chat_id = bot.config.error.notification_chat_id
@ -23,8 +23,8 @@ if not os.path.exists(logs_dir):
report_dir = os.path.join(current_dir, "report") report_dir = os.path.join(current_dir, "report")
if not os.path.exists(report_dir): if not os.path.exists(report_dir):
os.mkdir(report_dir) os.mkdir(report_dir)
pb_client = PbClient() pb_client = PbClient(config.error.pb_url, config.error.pb_sunset, config.error.pb_max_lines)
sentry = Sentry() sentry = SentryClient(config.error.sentry_dsn)
class ErrorHandler(Plugin): class ErrorHandler(Plugin):
@ -91,19 +91,29 @@ class ErrorHandler(Plugin):
except (BadRequest, Forbidden) as exc: except (BadRequest, Forbidden) as exc:
logger.error(f"发送 update_id[{update.update_id}] 错误信息失败 错误信息为") logger.error(f"发送 update_id[{update.update_id}] 错误信息失败 错误信息为")
logger.exception(exc) logger.exception(exc)
if pb_client.enabled:
logger.info("正在上传日记到 pb")
try: try:
pb_url = await pb_client.create_pb(error_text) pb_url = await pb_client.create_pb(error_text)
if pb_url: if pb_url:
logger.success("上传日记到 pb 成功")
await context.bot.send_message( await context.bot.send_message(
chat_id=notice_chat_id, chat_id=notice_chat_id,
text=f"错误信息已上传至 <a href='{pb_url}'>fars</a> 请查看", text=f"错误信息已上传至 <a href='{pb_url}'>fars</a> 请查看",
parse_mode=ParseMode.HTML, parse_mode=ParseMode.HTML,
) )
except Exception as exc: # pylint: disable=W0703 except PbClientException as exc:
logger.warning("上传错误信息至 fars 失败", exc_info=exc)
except Exception as exc:
logger.error("上传错误信息至 fars 失败") logger.error("上传错误信息至 fars 失败")
logger.exception(exc) logger.exception(exc)
if sentry.enabled:
logger.info("正在上传日记到 sentry")
try: try:
sentry.report_error(update, (type(context.error), context.error, context.error.__traceback__)) sentry.report_error(update, (type(context.error), context.error, context.error.__traceback__))
except Exception as exc: # pylint: disable=W0703 logger.success("上传日记到 sentry 成功")
except SentryClientException as exc:
logger.warning("上传错误信息至 sentry 失败", exc_info=exc)
except Exception as exc:
logger.error("上传错误信息至 sentry 失败") logger.error("上传错误信息至 sentry 失败")
logger.exception(exc) logger.exception(exc)