fix: Massively improve logging context isolation.
This commit is contained in:
@@ -717,7 +717,7 @@ class Timer:
|
||||
f"Timer <tid: {channelid}> deleted. Reason given: {reason!r}"
|
||||
)
|
||||
|
||||
@log_wrap(stack=['Timer Loop'])
|
||||
@log_wrap(action='Timer Loop')
|
||||
async def _runloop(self):
|
||||
"""
|
||||
Main loop which controls the
|
||||
|
||||
@@ -27,7 +27,7 @@ from data.columns import Integer, String, Timestamp, Bool
|
||||
|
||||
from meta import LionBot, LionCog, LionContext
|
||||
from meta.app import shard_talk, appname_from_shard
|
||||
from meta.logger import log_wrap, logging_context
|
||||
from meta.logger import log_wrap, logging_context, set_logging_context
|
||||
|
||||
from babel import ctx_translator, ctx_locale
|
||||
|
||||
@@ -280,6 +280,7 @@ class Reminders(LionCog):
|
||||
f"Scheduled new reminders: {tuple(reminder.reminderid for reminder in reminders)}",
|
||||
)
|
||||
|
||||
@log_wrap(action="Send Reminder")
|
||||
async def execute_reminder(self, reminderid):
|
||||
"""
|
||||
Send the reminder with the given reminderid.
|
||||
@@ -287,64 +288,65 @@ class Reminders(LionCog):
|
||||
This should in general only be executed from the executor shard,
|
||||
through a ReminderMonitor instance.
|
||||
"""
|
||||
with logging_context(action='Send Reminder', context=f"rid: {reminderid}"):
|
||||
reminder = await self.data.Reminder.fetch(reminderid)
|
||||
if reminder is None:
|
||||
logger.warning(
|
||||
f"Attempted to execute a reminder <rid: {reminderid}> that no longer exists!"
|
||||
)
|
||||
return
|
||||
set_logging_context(context=f"rid: {reminderid}")
|
||||
|
||||
try:
|
||||
# Try and find the user
|
||||
userid = reminder.userid
|
||||
if not (user := self.bot.get_user(userid)):
|
||||
user = await self.bot.fetch_user(userid)
|
||||
reminder = await self.data.Reminder.fetch(reminderid)
|
||||
if reminder is None:
|
||||
logger.warning(
|
||||
f"Attempted to execute a reminder <rid: {reminderid}> that no longer exists!"
|
||||
)
|
||||
return
|
||||
|
||||
# Set the locale variables
|
||||
locale = await self.bot.get_cog('BabelCog').get_user_locale(userid)
|
||||
ctx_locale.set(locale)
|
||||
ctx_translator.set(self.bot.translator)
|
||||
try:
|
||||
# Try and find the user
|
||||
userid = reminder.userid
|
||||
if not (user := self.bot.get_user(userid)):
|
||||
user = await self.bot.fetch_user(userid)
|
||||
|
||||
# Build the embed
|
||||
embed = reminder.embed
|
||||
# Set the locale variables
|
||||
locale = await self.bot.get_cog('BabelCog').get_user_locale(userid)
|
||||
ctx_locale.set(locale)
|
||||
ctx_translator.set(self.bot.translator)
|
||||
|
||||
# Attempt to send to user
|
||||
# TODO: Consider adding a View to this, for cancelling a repeated reminder or showing reminders
|
||||
await user.send(embed=embed)
|
||||
# Build the embed
|
||||
embed = reminder.embed
|
||||
|
||||
# Update the data as required
|
||||
if reminder.interval:
|
||||
now = utc_now()
|
||||
# Use original reminder time to calculate repeat, avoiding drift
|
||||
next_time = reminder.remind_at + dt.timedelta(seconds=reminder.interval)
|
||||
# Skip any expired repeats, to avoid spamming requests after downtime
|
||||
# TODO: Is this actually dst safe?
|
||||
while next_time.timestamp() <= now.timestamp():
|
||||
next_time = next_time + dt.timedelta(seconds=reminder.interval)
|
||||
await reminder.update(remind_at=next_time)
|
||||
self.monitor.schedule_task(reminder.reminderid, reminder.timestamp)
|
||||
logger.debug(
|
||||
f"Executed reminder <rid: {reminder.reminderid}> and scheduled repeat at {next_time}."
|
||||
)
|
||||
else:
|
||||
await reminder.delete()
|
||||
logger.debug(
|
||||
f"Executed reminder <rid: {reminder.reminderid}>."
|
||||
)
|
||||
except discord.HTTPException as e:
|
||||
await reminder.update(failed=True)
|
||||
# Attempt to send to user
|
||||
# TODO: Consider adding a View to this, for cancelling a repeated reminder or showing reminders
|
||||
await user.send(embed=embed)
|
||||
|
||||
# Update the data as required
|
||||
if reminder.interval:
|
||||
now = utc_now()
|
||||
# Use original reminder time to calculate repeat, avoiding drift
|
||||
next_time = reminder.remind_at + dt.timedelta(seconds=reminder.interval)
|
||||
# Skip any expired repeats, to avoid spamming requests after downtime
|
||||
# TODO: Is this actually dst safe?
|
||||
while next_time.timestamp() <= now.timestamp():
|
||||
next_time = next_time + dt.timedelta(seconds=reminder.interval)
|
||||
await reminder.update(remind_at=next_time)
|
||||
self.monitor.schedule_task(reminder.reminderid, reminder.timestamp)
|
||||
logger.debug(
|
||||
f"Reminder <rid: {reminder.reminderid}> could not be sent: {e.text}",
|
||||
f"Executed reminder <rid: {reminder.reminderid}> and scheduled repeat at {next_time}."
|
||||
)
|
||||
except Exception:
|
||||
await reminder.update(failed=True)
|
||||
logger.exception(
|
||||
f"Reminder <rid: {reminder.reminderid}> failed for an unknown reason!"
|
||||
else:
|
||||
await reminder.delete()
|
||||
logger.debug(
|
||||
f"Executed reminder <rid: {reminder.reminderid}>."
|
||||
)
|
||||
finally:
|
||||
# Dispatch for analytics
|
||||
self.bot.dispatch('reminder_sent', reminder)
|
||||
except discord.HTTPException as e:
|
||||
await reminder.update(failed=True)
|
||||
logger.debug(
|
||||
f"Reminder <rid: {reminder.reminderid}> could not be sent: {e.text}",
|
||||
)
|
||||
except Exception:
|
||||
await reminder.update(failed=True)
|
||||
logger.exception(
|
||||
f"Reminder <rid: {reminder.reminderid}> failed for an unknown reason!"
|
||||
)
|
||||
finally:
|
||||
# Dispatch for analytics
|
||||
self.bot.dispatch('reminder_sent', reminder)
|
||||
|
||||
@cmds.hybrid_group(
|
||||
name=_p('cmd:reminders', "reminders")
|
||||
|
||||
@@ -13,7 +13,7 @@ from discord.ui.button import button
|
||||
from discord.ui.text_input import TextStyle, TextInput
|
||||
|
||||
from meta import LionCog, LionBot, LionContext
|
||||
from meta.logger import logging_context, log_wrap
|
||||
from meta.logger import logging_context, log_wrap, set_logging_context
|
||||
from meta.errors import UserInputError
|
||||
from meta.app import shard_talk
|
||||
|
||||
@@ -73,8 +73,7 @@ class Blacklists(LionCog):
|
||||
f"Loaded {len(self.guild_blacklist)} blacklisted guilds."
|
||||
)
|
||||
if self.bot.is_ready():
|
||||
with logging_context(action="Guild Blacklist"):
|
||||
await self.leave_blacklisted_guilds()
|
||||
await self.leave_blacklisted_guilds()
|
||||
|
||||
@LionCog.listener('on_ready')
|
||||
@log_wrap(action="Guild Blacklist")
|
||||
@@ -84,8 +83,9 @@ class Blacklists(LionCog):
|
||||
guild for guild in self.bot.guilds
|
||||
if guild.id in self.guild_blacklist
|
||||
]
|
||||
|
||||
asyncio.gather(*(guild.leave() for guild in to_leave))
|
||||
if to_leave:
|
||||
tasks = [asyncio.create_task(guild.leave()) for guild in to_leave]
|
||||
await asyncio.gather(*tasks)
|
||||
|
||||
logger.info(
|
||||
"Left {} blacklisted guilds.".format(len(to_leave)),
|
||||
@@ -95,12 +95,12 @@ class Blacklists(LionCog):
|
||||
@log_wrap(action="Check Guild Blacklist")
|
||||
async def check_guild_blacklist(self, guild):
|
||||
"""Check if the given guild is in the blacklist, and leave if so."""
|
||||
with logging_context(context=f"gid: {guild.id}"):
|
||||
if guild.id in self.guild_blacklist:
|
||||
await guild.leave()
|
||||
logger.info(
|
||||
"Automatically left blacklisted guild '{}' (gid:{}) upon join.".format(guild.name, guild.id)
|
||||
)
|
||||
if guild.id in self.guild_blacklist:
|
||||
set_logging_context(context=f"gid: {guild.id}")
|
||||
await guild.leave()
|
||||
logger.info(
|
||||
"Automatically left blacklisted guild '{}' (gid:{}) upon join.".format(guild.name, guild.id)
|
||||
)
|
||||
|
||||
async def bot_check_once(self, ctx: LionContext) -> bool: # type:ignore
|
||||
if ctx.author.id in self.user_blacklist:
|
||||
|
||||
@@ -19,7 +19,7 @@ from discord.ui import TextInput, View
|
||||
from discord.ui.button import button
|
||||
import discord.app_commands as appcmd
|
||||
|
||||
from meta.logger import logging_context
|
||||
from meta.logger import logging_context, log_wrap
|
||||
from meta.app import shard_talk
|
||||
from meta import conf
|
||||
from meta.context import context, ctx_bot
|
||||
@@ -185,54 +185,54 @@ def mk_print(fp: io.StringIO) -> Callable[..., None]:
|
||||
return _print
|
||||
|
||||
|
||||
@log_wrap(action="Code Exec")
|
||||
async def _async(to_eval: str, style='exec'):
|
||||
with logging_context(action="Code Exec"):
|
||||
newline = '\n' * ('\n' in to_eval)
|
||||
logger.info(
|
||||
f"Exec code with {style}: {newline}{to_eval}"
|
||||
newline = '\n' * ('\n' in to_eval)
|
||||
logger.info(
|
||||
f"Exec code with {style}: {newline}{to_eval}"
|
||||
)
|
||||
|
||||
output = io.StringIO()
|
||||
_print = mk_print(output)
|
||||
|
||||
scope: dict[str, Any] = dict(sys.modules)
|
||||
scope['__builtins__'] = builtins
|
||||
scope.update(builtins.__dict__)
|
||||
scope['ctx'] = ctx = context.get()
|
||||
scope['bot'] = ctx_bot.get()
|
||||
scope['print'] = _print # type: ignore
|
||||
|
||||
try:
|
||||
if ctx and ctx.message:
|
||||
source_str = f"<msg: {ctx.message.id}>"
|
||||
elif ctx and ctx.interaction:
|
||||
source_str = f"<iid: {ctx.interaction.id}>"
|
||||
else:
|
||||
source_str = "Unknown async"
|
||||
|
||||
code = compile(
|
||||
to_eval,
|
||||
source_str,
|
||||
style,
|
||||
ast.PyCF_ALLOW_TOP_LEVEL_AWAIT
|
||||
)
|
||||
func = types.FunctionType(code, scope)
|
||||
|
||||
output = io.StringIO()
|
||||
_print = mk_print(output)
|
||||
ret = func()
|
||||
if inspect.iscoroutine(ret):
|
||||
ret = await ret
|
||||
if ret is not None:
|
||||
_print(repr(ret))
|
||||
except Exception:
|
||||
_, exc, tb = sys.exc_info()
|
||||
_print("".join(traceback.format_tb(tb)))
|
||||
_print(f"{type(exc).__name__}: {exc}")
|
||||
|
||||
scope: dict[str, Any] = dict(sys.modules)
|
||||
scope['__builtins__'] = builtins
|
||||
scope.update(builtins.__dict__)
|
||||
scope['ctx'] = ctx = context.get()
|
||||
scope['bot'] = ctx_bot.get()
|
||||
scope['print'] = _print # type: ignore
|
||||
|
||||
try:
|
||||
if ctx and ctx.message:
|
||||
source_str = f"<msg: {ctx.message.id}>"
|
||||
elif ctx and ctx.interaction:
|
||||
source_str = f"<iid: {ctx.interaction.id}>"
|
||||
else:
|
||||
source_str = "Unknown async"
|
||||
|
||||
code = compile(
|
||||
to_eval,
|
||||
source_str,
|
||||
style,
|
||||
ast.PyCF_ALLOW_TOP_LEVEL_AWAIT
|
||||
)
|
||||
func = types.FunctionType(code, scope)
|
||||
|
||||
ret = func()
|
||||
if inspect.iscoroutine(ret):
|
||||
ret = await ret
|
||||
if ret is not None:
|
||||
_print(repr(ret))
|
||||
except Exception:
|
||||
_, exc, tb = sys.exc_info()
|
||||
_print("".join(traceback.format_tb(tb)))
|
||||
_print(f"{type(exc).__name__}: {exc}")
|
||||
|
||||
result = output.getvalue().strip()
|
||||
newline = '\n' * ('\n' in result)
|
||||
logger.info(
|
||||
f"Exec complete, output: {newline}{result}"
|
||||
)
|
||||
result = output.getvalue().strip()
|
||||
newline = '\n' * ('\n' in result)
|
||||
logger.info(
|
||||
f"Exec complete, output: {newline}{result}"
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user