添加错误平台

Co-authored-by: SiHuan <sihuan@sakuya.love>
Co-authored-by: 洛水居室 <luoshuijs@outlook.com>
This commit is contained in:
omg-xtao 2022-10-19 20:22:24 +08:00 committed by GitHub
parent 8c12237e85
commit 1f17e56824
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 204 additions and 15 deletions

View File

@ -58,3 +58,9 @@ LOGGER_LOCALS_MAX_STRING=80
# WEB_URL=http://localhost:8080/
# WEB_HOST=localhost
# WEB_PORT=8080
# error
# ERROR_PB_URL=https://fars.ee
# ERROR_PB_SUNSET=43200
# ERROR_PB_MAX_LINES=1000
# ERROR_SENTRY_DSN=

View File

@ -64,6 +64,11 @@ class BotConfig(BaseSettings):
web_host: str = "localhost"
web_port: int = 8080
error_pb_url: str = ""
error_pb_sunset: int = 43200
error_pb_max_lines: int = 1000
error_sentry_dsn: str = ""
class Config:
case_sensitive = False
json_loads = json.loads

26
modules/error/pb.py Normal file
View File

@ -0,0 +1,26 @@
import httpx
from core.config import config
class PbClient:
def __init__(self):
self.client = httpx.AsyncClient()
self.PB_API = config.error_pb_url
self.sunset: int = config.error_pb_sunset # 自动销毁时间 秒
self.private: bool = True
self.max_lines: int = config.error_pb_max_lines
async def create_pb(self, content: str) -> str:
if not self.PB_API:
return ""
content = "\n".join(content.splitlines()[-self.max_lines :]) + "\n"
data = {
"c": content,
}
if self.private:
data["p"] = "1"
if self.sunset:
data["sunset"] = self.sunset
data = await self.client.post(self.PB_API, data=data) # 需要错误处理
return data.headers["location"]

42
modules/error/sentry.py Normal file
View File

@ -0,0 +1,42 @@
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
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: Update, exc_info):
if not config.error_sentry_dsn:
return
try:
sender_id = update.effective_user.id if update.effective_user else update.effective_chat.id
except AttributeError:
sender_id = 0
sentry_sdk.set_context(
"Target", {"ChatID": str(update.message.chat_id), "UserID": sender_id, "Msg": update.message.text or ""}
)
sentry_sdk.capture_exception(exc_info)

View File

@ -11,6 +11,8 @@ from telegram.ext import CallbackContext
from core.bot import bot
from core.plugin import error_handler, Plugin
from modules.error.pb import PbClient
from modules.error.sentry import Sentry
from utils.log import logger
notice_chat_id = bot.config.error_notification_chat_id
@ -21,6 +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()
class ErrorHandler(Plugin):
@ -90,3 +94,19 @@ 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"错误信息已上传至 <a href='{pb_url}'>fars</a> 请查看",
parse_mode=ParseMode.HTML,
)
except Exception as exc: # pylint: disable=W0703
logger.error("上传错误信息至 fars 失败")
logger.exception(exc)
try:
sentry.report_error(update, context.error)
except Exception as exc: # pylint: disable=W0703
logger.error("上传错误信息至 sentry 失败")
logger.exception(exc)

View File

@ -5,6 +5,7 @@ from telegram.constants import ChatAction
from telegram.ext import CommandHandler, CallbackContext
from core.plugin import Plugin, handler
from modules.error.pb import PbClient
from utils.decorators.admins import bot_admins_rights_check
from utils.log import logger
@ -14,6 +15,21 @@ debug_log = os.path.join(current_dir, "logs", "debug", "debug.log")
class Log(Plugin):
def __init__(self):
self.pb_client = PbClient()
self.pb_client.sunset = 3600
self.pb_client.max_lines = 10000
async def send_to_pb(self, file_name: str):
pb_url = ""
try:
with open(file_name, "r", encoding="utf-8") as f:
pb_url = await self.pb_client.create_pb(f.read())
except Exception as exc: # pylint: disable=W0703
logger.error("上传错误信息至 fars 失败")
logger.exception(exc)
return pb_url
@handler(CommandHandler, command="send_log", block=False)
@bot_admins_rights_check
async def send_log(self, update: Update, _: CallbackContext):
@ -21,12 +37,18 @@ class Log(Plugin):
logger.info(f"用户 {user.full_name}[{user.id}] send_log 命令请求")
message = update.effective_message
if os.path.exists(error_log) and os.path.getsize(error_log) > 0:
pb_url = await self.send_to_pb(error_log)
await message.reply_chat_action(ChatAction.UPLOAD_DOCUMENT)
await message.reply_document(open(error_log, mode="rb+"), caption="Error Log")
await message.reply_document(
open(error_log, mode="rb+"), caption=f"Error Log\n{pb_url}/text" if pb_url else "Error Log"
)
else:
await message.reply_text("错误日记未找到")
if os.path.exists(debug_log) and os.path.getsize(debug_log) > 0:
pb_url = await self.send_to_pb(debug_log)
await message.reply_chat_action(ChatAction.UPLOAD_DOCUMENT)
await message.reply_document(open(debug_log, mode="rb+"), caption="Debug Log")
await message.reply_document(
open(debug_log, mode="rb+"), caption=f"Debug Log\n{pb_url}/text" if pb_url else "Debug Log"
)
else:
await message.reply_text("调试日记未找到")

78
poetry.lock generated
View File

@ -388,6 +388,28 @@ url = "https://github.com/thesadru/genshin.py"
reference = "HEAD"
resolved_reference = "7b3a4a71bfdf84d9f1bf984e91c0bcf73f9dfa7f"
[[package]]
name = "gitdb"
version = "4.0.9"
description = "Git Object Database"
category = "main"
optional = false
python-versions = ">=3.6"
[package.dependencies]
smmap = ">=3.0.1,<6"
[[package]]
name = "GitPython"
version = "3.1.29"
description = "GitPython is a python library used to interact with Git repositories"
category = "main"
optional = false
python-versions = ">=3.7"
[package.dependencies]
gitdb = ">=4.0.1,<5"
[[package]]
name = "greenlet"
version = "1.1.3"
@ -899,6 +921,38 @@ typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9
[package.extras]
jupyter = ["ipywidgets (>=7.5.1,<8.0.0)"]
[[package]]
name = "sentry-sdk"
version = "1.9.10"
description = "Python client for Sentry (https://sentry.io)"
category = "main"
optional = false
python-versions = "*"
[package.dependencies]
certifi = "*"
urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""}
[package.extras]
aiohttp = ["aiohttp (>=3.5)"]
beam = ["apache-beam (>=2.12)"]
bottle = ["bottle (>=0.12.13)"]
celery = ["celery (>=3)"]
chalice = ["chalice (>=1.16.0)"]
django = ["django (>=1.8)"]
falcon = ["falcon (>=1.4)"]
fastapi = ["fastapi (>=0.79.0)"]
flask = ["blinker (>=1.1)", "flask (>=0.11)"]
httpx = ["httpx (>=0.16.0)"]
pure_eval = ["asttokens", "executing", "pure-eval"]
pyspark = ["pyspark (>=2.4.4)"]
quart = ["blinker (>=1.1)", "quart (>=0.16.1)"]
rq = ["rq (>=0.6)"]
sanic = ["sanic (>=0.8)"]
sqlalchemy = ["sqlalchemy (>=1.2)"]
starlette = ["starlette (>=0.19.1)"]
tornado = ["tornado (>=5)"]
[[package]]
name = "setuptools"
version = "65.4.1"
@ -920,6 +974,14 @@ category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
[[package]]
name = "smmap"
version = "5.0.0"
description = "A pure Python implementation of a sliding window memory map manager"
category = "main"
optional = false
python-versions = ">=3.6"
[[package]]
name = "sniffio"
version = "1.3.0"
@ -1503,6 +1565,14 @@ frozenlist = [
{file = "frozenlist-1.3.1.tar.gz", hash = "sha256:3a735e4211a04ccfa3f4833547acdf5d2f863bfeb01cfd3edaffbc251f15cec8"},
]
genshin = []
gitdb = [
{file = "gitdb-4.0.9-py3-none-any.whl", hash = "sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd"},
{file = "gitdb-4.0.9.tar.gz", hash = "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"},
]
GitPython = [
{file = "GitPython-3.1.29-py3-none-any.whl", hash = "sha256:41eea0deec2deea139b459ac03656f0dd28fc4a3387240ec1d3c259a2c47850f"},
{file = "GitPython-3.1.29.tar.gz", hash = "sha256:cc36bfc4a3f913e66805a28e84703e419d9c264c1077e537b54f0e1af85dbefd"},
]
greenlet = [
{file = "greenlet-1.1.3-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:8c287ae7ac921dfde88b1c125bd9590b7ec3c900c2d3db5197f1286e144e712b"},
{file = "greenlet-1.1.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:870a48007872d12e95a996fca3c03a64290d3ea2e61076aa35d3b253cf34cd32"},
@ -2084,6 +2154,10 @@ rich = [
{file = "rich-12.6.0-py3-none-any.whl", hash = "sha256:a4eb26484f2c82589bd9a17c73d32a010b1e29d89f1604cd9bf3a2097b81bb5e"},
{file = "rich-12.6.0.tar.gz", hash = "sha256:ba3a3775974105c221d31141f2c116f4fd65c5ceb0698657a11e9f295ec93fd0"},
]
sentry-sdk = [
{file = "sentry-sdk-1.9.10.tar.gz", hash = "sha256:4fbace9a763285b608c06f01a807b51acb35f6059da6a01236654e08b0ee81ff"},
{file = "sentry_sdk-1.9.10-py2.py3-none-any.whl", hash = "sha256:2469240f6190aaebcb453033519eae69cfe8cc602065b4667e18ee14fc1e35dc"},
]
setuptools = [
{file = "setuptools-65.4.1-py3-none-any.whl", hash = "sha256:1b6bdc6161661409c5f21508763dc63ab20a9ac2f8ba20029aaaa7fdb9118012"},
{file = "setuptools-65.4.1.tar.gz", hash = "sha256:3050e338e5871e70c72983072fe34f6032ae1cdeeeb67338199c2f74e083a80e"},
@ -2092,6 +2166,10 @@ six = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
]
smmap = [
{file = "smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"},
{file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"},
]
sniffio = [
{file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"},
{file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"},

View File

@ -37,6 +37,8 @@ lxml = "^4.9.1"
arko-wrapper = "^0.2.3"
fastapi = "^0.85.1"
uvicorn = {extras = ["standard"], version = "^0.18.3"}
sentry-sdk = "^1.9.10"
GitPython = "^3.1.29"
[tool.poetry.extras]
pyro = ["Pyrogram", "TgCrypto"]

View File

@ -254,17 +254,5 @@ class Handler(DefaultRichHandler):
class FileHandler(Handler):
def __init__(self, *args, path: Path, **kwargs):
super().__init__(*args, **kwargs)
while True:
try:
path.parent.mkdir(exist_ok=True)
break
except FileNotFoundError:
parent = path.parent
while True:
try:
parent.mkdir(exist_ok=True)
break
except FileNotFoundError:
parent = parent.parent
path.parent.mkdir(exist_ok=True)
path.parent.mkdir(exist_ok=True, parents=True)
self.console = Console(width=180, file=FileIO(path), theme=Theme(DEFAULT_STYLE))