discord.py默认隐藏哪些错误?



我在Stack Overflow的许多帖子中看到提到discord.py "隐藏错误"缺省情况下,如果没有配置日志记录。在文档中提到:

强烈建议配置日志模块,如no如果没有设置,将输出错误或警告。(强调我)

但是,在discord.py 1.7.3中,我观察到错误被输出到控制台。运行以下代码片段:

import discord
client = discord.Client(intents=discord.Intents.all())

@client.event
async def on_ready():
raise ValueError
client.run(TOKEN)

正确触发ValueError-错误不被吞下。

在discord.py 2.0中,已经设置了日志记录,正如输出(使用相同的代码片段)所证明的那样:

[2022-07-15 12:28:40] [INFO    ] discord.client: logging in using static token
[2022-07-15 12:28:41] [INFO    ] discord.gateway: Shard ID None has sent the IDENTIFY payload.
[2022-07-15 12:28:41] [INFO    ] discord.gateway: Shard ID None has connected to Gateway (Session ID: ...).
[2022-07-15 12:28:43] [ERROR   ] discord.client: Ignoring exception in on_ready
# ^-----------------^ These lines are all from the logging module
Traceback (most recent call last):
File "C:UsersTheFuPycharmProjectsmcve2.0venvlibsite-packagesdiscordclient.py", line 456, in _run_event
await coro(*args, **kwargs)
File "C:UsersTheFuPycharmProjectsmcve2.0main.py", line 8, in on_ready
raise ValueError
ValueError

那么,如果在discord.py 1.7.3中没有设置日志记录,为什么会输出错误?做什么错误被这个版本吞噬了?

默认情况下,在1.7.3,它完全绕过日志和直接打印到stderr(代码):

async def on_error(self, event_method, *args, **kwargs):
print('Ignoring exception in {}'.format(event_method), file=sys.stderr)
traceback.print_exc()

然后像这样调用on_error:

async def _run_event(self, coro, event_name, *args, **kwargs):
try:
await coro(*args, **kwargs)
except asyncio.CancelledError:
pass
except Exception:
try:
await self.on_error(event_name, *args, **kwargs)
except asyncio.CancelledError:
pass

不管是否设置了日志记录,它都会这样做。


我用这个精简版的bot进行了测试。请注意,错误信息中的_run_event应该与上面代码中链接的相同。

import discord
from discord.ext import commands
import logging
import sys
LOGGING_ENABLE = True
if LOGGING_ENABLE:
log = logging.getLogger()
log.setLevel(logging.INFO)
handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.INFO)
handler.setFormatter(logging.Formatter('33[0;36m%(asctime)s 33[0;32m%(levelname)s33[0;0m %(message)s', '%Y%m%d %H:%M:%S'))
log.addHandler(handler)
print(f'discord version {discord.__version__}')
intent = discord.Intents.all()
client = commands.Bot(command_prefix='sudo ', intents=intent)
@client.event
async def on_ready():
print('logged in')
print('raising exception!')
raise ValueError()
print('starting client.run:')
client.run(...)

表示日志记录是而不是设置

discord version 1.7.3
starting client.run:
logged in
raising exception!
Ignoring exception in on_ready
Traceback (most recent call last):
File "/usr/local/lib/python3.9/site-packages/discord/client.py", line 343, in _run_event
await coro(*args, **kwargs)
File "/home/greateric/Documents/PYTHON/old-discordbot/bot_cut.py", line 14, in on_ready
raise ValueError()
ValueError
当日志

时,给出和设置

discord version 1.7.3
starting client.run:
20220717 14:50:10 INFO logging in using static token
20220717 14:50:11 INFO Shard ID None has sent the IDENTIFY payload.
20220717 14:50:12 INFO Shard ID None has connected to Gateway: <a bunch of irrelevant stuff>.
logged in
raising exception!
Ignoring exception in on_ready
Traceback (most recent call last):
File "/usr/local/lib/python3.9/site-packages/discord/client.py", line 343, in _run_event
await coro(*args, **kwargs)
File "/home/greateric/Documents/PYTHON/old-discordbot/bot_cut.py", line 23, in on_ready
raise ValueError()
ValueError
^C20220717 14:50:15 INFO Cleaning up tasks.
20220717 14:50:15 INFO Cleaning up after 1 tasks.
20220717 14:50:15 INFO All tasks finished cancelling.
20220717 14:50:15 INFO Closing the event loop.

在2.0中,启用了日志记录,它实际上"很好";打印错误

discord version 2.0.0a
20220717 14:53:09 WARNING PyNaCl is not installed, voice will NOT be supported
starting client.run:
2022-07-17 14:53:09 INFO     discord.client logging in using static token
20220717 14:53:09 INFO logging in using static token
2022-07-17 14:53:09 INFO     discord.gateway Shard ID None has sent the IDENTIFY payload.
20220717 14:53:09 INFO Shard ID None has sent the IDENTIFY payload.
2022-07-17 14:53:09 INFO     discord.gateway Shard ID None has connected to Gateway (Session ID: ...).
20220717 14:53:09 INFO Shard ID None has connected to Gateway (Session ID: ...).
logged in
raising exception!
2022-07-17 14:53:12 ERROR    discord.client Ignoring exception in on_ready
Traceback (most recent call last):
File "/home/greateric/Documents/PYTHON/old-discordbot/venv/lib/python3.9/site-packages/discord/client.py", line 456, in _run_event
await coro(*args, **kwargs)
File "/home/greateric/Documents/PYTHON/old-discordbot/bot_cut.py", line 23, in on_ready
raise ValueError()
ValueError
20220717 14:53:12 ERROR Ignoring exception in on_ready
Traceback (most recent call last):
File "/home/greateric/Documents/PYTHON/old-discordbot/venv/lib/python3.9/site-packages/discord/client.py", line 456, in _run_event
await coro(*args, **kwargs)
File "/home/greateric/Documents/PYTHON/old-discordbot/bot_cut.py", line 23, in on_ready
raise ValueError()
ValueError

(我不知道这里发生了什么,但我认为这是两个日志处理程序相互争斗。屏幕截图(颜色在这里)

实际上,当我禁用日志记录时,会发生以下情况:

discord version 2.0.0a
starting client.run:
2022-07-17 14:56:40 INFO     discord.client logging in using static token
2022-07-17 14:56:40 INFO     discord.gateway Shard ID None has sent the IDENTIFY payload.
2022-07-17 14:56:40 INFO     discord.gateway Shard ID None has connected to Gateway (Session ID: 85657d0594417664850b2c03c2ca0d77).
logged in
raising exception!
2022-07-17 14:56:42 ERROR    discord.client Ignoring exception in on_ready
Traceback (most recent call last):
File "/home/greateric/Documents/PYTHON/old-discordbot/venv/lib/python3.9/site-packages/discord/client.py", line 456, in _run_event
await coro(*args, **kwargs)
File "/home/greateric/Documents/PYTHON/old-discordbot/bot_cut.py", line 23, in on_ready
raise ValueError()
ValueError

看起来discord.py现在正在创建自己的日志处理程序(独立于我上次创建的那个)。

代码不同。(代码片段,来自commit52f3a3496bea13fefc08b38f9ed01641e565d0eb)

async def on_error(self, event_method: str, /, *args: Any, **kwargs: Any) -> None:
"""|coro|
The default error handler provided by the client.
By default this logs to the library logger however it could be
overridden to have a different implementation.
Check :func:`~discord.on_error` for more details.
.. versionchanged:: 2.0
``event_method`` parameter is now positional-only
and instead of writing to ``sys.stderr`` it logs instead.
"""
_log.exception('Ignoring exception in %s', event_method)

该记录器独立于您自己设置的记录器,因此:

_log = logging.getLogger(__name__)

它还在__init__.py中添加了一个处理程序:

logging.getLogger(__name__).addHandler(logging.NullHandler())

以以下两个文件为例:

a.py

import logging
import sys
handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.INFO)
handler.setFormatter(logging.Formatter('33[0;36m%(asctime)s 33[0;32m%(levelname)s33[0;0m %(message)s', '%Y%m%d %H:%M:%S'))
logging.getLogger(__name__).addHandler(handler)
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
logger.info('hi from a.py')
print('done executing a.py')

b.py

import logging
import a
import sys
LOGGING_ENABLE = True
if LOGGING_ENABLE:
logger = logging.getLogger()
handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.INFO)
handler.setFormatter(logging.Formatter('33[0;33mreminder that this is the formatter of b.py 33[0;36m%(asctime)s 33[0;32m%(levelname)s33[0;0m %(message)s', '%Y%m%d %H:%M:%S'))
logger = logging.getLogger()
logger.addHandler(handler)
logger.setLevel(logging.INFO)
logger.info('hi from b.py')
print('just finished running b.py')

这是启用LOGGING_ENABLE时的输出。这两个记录器是完全相互独立的(它们有不同的格式化器)。

20220717 15:10:49 INFO hi from a.py
done executing a.py
reminder that this is the formatter of b.py 20220717 15:10:49 INFO hi from b.py
just finished running b.py

b.py中没有记录器时,a.py的记录器仍然工作:

20220717 15:11:19 INFO hi from a.py
done executing a.py
just finished running b.py

Discord.py的2.0日志记录器独立于代码的日志记录器或缺少日志记录器。它自己的记录器将始终打印错误,而不管您自己的记录器设置是什么。


任何版本都不应该包含错误(除了KeyboardInterrupt等非Exception错误)。_run_event基本上是相同的2.0:

async def _run_event(
self,
coro: Callable[..., Coroutine[Any, Any, Any]],
event_name: str,
*args: Any,
**kwargs: Any,
) -> None:
try:
await coro(*args, **kwargs)
except asyncio.CancelledError:
pass
except Exception:
try:
await self.on_error(event_name, *args, **kwargs)
except asyncio.CancelledError:
pass

如果"没有错误",通常意味着它被故意忽略了,例如,如果您覆盖了错误处理程序。也可能是你的终端由于某种原因忽略了stderr,我曾经在一个不同的项目中遇到过这种情况。

在1.7.3中,错误将被打印为stderr,而在2.0中,错误将使用discord的记录器记录。

当它说文档上没有输出时,它指的是额外的日志信息,比如logging in using static token信息。错误回溯仍然直接打印到stderr。

最新更新