From bda5f4fdd0eeae7ba2daef1a1414d0a9ea016521 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B4=9B=E6=B0=B4=E5=B1=85=E5=AE=A4?= Date: Mon, 21 Nov 2022 14:57:12 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20Improve=20error=20push?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 2 +- modules/error/sentry.py | 50 ------------------------ modules/errorpush/__init__.py | 5 +++ modules/errorpush/logger.py | 3 ++ modules/{error => errorpush}/pb.py | 34 ++++++++++++---- modules/errorpush/sentry.py | 63 ++++++++++++++++++++++++++++++ plugins/system/errorhandler.py | 50 ++++++++++++++---------- 7 files changed, 128 insertions(+), 79 deletions(-) delete mode 100644 modules/error/sentry.py create mode 100644 modules/errorpush/__init__.py create mode 100644 modules/errorpush/logger.py rename modules/{error => errorpush}/pb.py (50%) create mode 100644 modules/errorpush/sentry.py diff --git a/.env.example b/.env.example index a108f82..2afe606 100644 --- a/.env.example +++ b/.env.example @@ -47,7 +47,7 @@ LOGGER_LOCALS_MAX_DEPTH=0 LOGGER_LOCALS_MAX_LENGTH=10 LOGGER_LOCALS_MAX_STRING=80 # 可被 logger 打印的 record 的名称(默认包含了 LOGGER_NAME ) -LOGGER_FILTERED_NAMES=["uvicorn"] +LOGGER_FILTERED_NAMES=["uvicorn","ErrorPush"] # 超时配置 可选配置项 diff --git a/modules/error/sentry.py b/modules/error/sentry.py deleted file mode 100644 index d9bc428..0000000 --- a/modules/error/sentry.py +++ /dev/null @@ -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 成功") diff --git a/modules/errorpush/__init__.py b/modules/errorpush/__init__.py new file mode 100644 index 0000000..8126606 --- /dev/null +++ b/modules/errorpush/__init__.py @@ -0,0 +1,5 @@ +from .pb import PbClient, PbClientException +from .sentry import SentryClient, SentryClientException + + +__all__ = ["PbClient", "PbClientException", "SentryClient", "SentryClientException"] diff --git a/modules/errorpush/logger.py b/modules/errorpush/logger.py new file mode 100644 index 0000000..43ace01 --- /dev/null +++ b/modules/errorpush/logger.py @@ -0,0 +1,3 @@ +import logging + +logger = logging.getLogger("ErrorPush") diff --git a/modules/error/pb.py b/modules/errorpush/pb.py similarity index 50% rename from modules/error/pb.py rename to modules/errorpush/pb.py index f2f22b6..b8d25ea 100644 --- a/modules/error/pb.py +++ b/modules/errorpush/pb.py @@ -2,23 +2,42 @@ from typing import Optional import httpx -from core.config import config from utils.log import logger +__all__ = ["PbClient", "PbClientException"] + + +class PbClientException(Exception): + pass + 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.PB_API = config.error.pb_url - self.sunset: int = config.error.pb_sunset # 自动销毁时间 单位为秒 + self.PB_API = pb_url + self.sunset: int = pb_sunset 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]: + 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: return None - logger.info("正在上传日记到 pb") - content = "\n".join(content.splitlines()[-self.max_lines:]) + "\n" + content = "\n".join(content.splitlines()[-self.max_lines :]) + "\n" data = { "c": content, } @@ -30,5 +49,4 @@ class PbClient: if result.is_error: logger.warning("上传日记到 pb 失败 status_code[%s]", result.status_code) return None - logger.success("上传日记到 pb 成功") return result.headers.get("location") diff --git a/modules/errorpush/sentry.py b/modules/errorpush/sentry.py new file mode 100644 index 0000000..5683e6d --- /dev/null +++ b/modules/errorpush/sentry.py @@ -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) diff --git a/plugins/system/errorhandler.py b/plugins/system/errorhandler.py index 0c2a592..5408a84 100644 --- a/plugins/system/errorhandler.py +++ b/plugins/system/errorhandler.py @@ -10,9 +10,9 @@ from telegram.error import BadRequest, Forbidden from telegram.ext import CallbackContext from core.bot import bot +from core.config import config from core.plugin import Plugin, error_handler -from modules.error.pb import PbClient -from modules.error.sentry import Sentry +from modules.errorpush import PbClient, SentryClient, PbClientException, SentryClientException from utils.log import logger 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") if not os.path.exists(report_dir): os.mkdir(report_dir) -pb_client = PbClient() -sentry = Sentry() +pb_client = PbClient(config.error.pb_url, config.error.pb_sunset, config.error.pb_max_lines) +sentry = SentryClient(config.error.sentry_dsn) class ErrorHandler(Plugin): @@ -91,19 +91,29 @@ class ErrorHandler(Plugin): except (BadRequest, Forbidden) as exc: logger.error(f"发送 update_id[{update.update_id}] 错误信息失败 错误信息为") logger.exception(exc) - try: - pb_url = await pb_client.create_pb(error_text) - if pb_url: - await context.bot.send_message( - chat_id=notice_chat_id, - text=f"错误信息已上传至 fars 请查看", - parse_mode=ParseMode.HTML, - ) - except Exception as exc: # pylint: disable=W0703 - logger.error("上传错误信息至 fars 失败") - logger.exception(exc) - try: - sentry.report_error(update, (type(context.error), context.error, context.error.__traceback__)) - except Exception as exc: # pylint: disable=W0703 - logger.error("上传错误信息至 sentry 失败") - logger.exception(exc) + if pb_client.enabled: + logger.info("正在上传日记到 pb") + try: + pb_url = await pb_client.create_pb(error_text) + if pb_url: + logger.success("上传日记到 pb 成功") + await context.bot.send_message( + chat_id=notice_chat_id, + text=f"错误信息已上传至 fars 请查看", + parse_mode=ParseMode.HTML, + ) + except PbClientException as exc: + logger.warning("上传错误信息至 fars 失败", exc_info=exc) + except Exception as exc: + logger.error("上传错误信息至 fars 失败") + logger.exception(exc) + if sentry.enabled: + logger.info("正在上传日记到 sentry") + try: + sentry.report_error(update, (type(context.error), context.error, context.error.__traceback__)) + logger.success("上传日记到 sentry 成功") + except SentryClientException as exc: + logger.warning("上传错误信息至 sentry 失败", exc_info=exc) + except Exception as exc: + logger.error("上传错误信息至 sentry 失败") + logger.exception(exc)