Skip to content

Commit 12ed5fd

Browse files
committed
Use separate unique logging object for config logging
1 parent 221d2b6 commit 12ed5fd

2 files changed

Lines changed: 74 additions & 100 deletions

File tree

cogs/startup.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,6 @@ async def on_ready(self) -> None:
4040
Shortcut accessors should only be populated once TeX-Bot is ready to make API requests.
4141
"""
4242
if settings["DISCORD_LOG_CHANNEL_WEBHOOK_URL"]:
43-
for handler in logger.handlers:
44-
if isinstance(handler, DiscordHandler):
45-
logger.removeHandler(handler)
46-
4743
discord_logging_handler: logging.Handler = DiscordHandler(
4844
service_name=self.bot.user.name if self.bot.user else "TeX-Bot",
4945
webhook_url=settings["DISCORD_LOG_CHANNEL_WEBHOOK_URL"],

config.py

Lines changed: 74 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,47 @@ def __getitem__(self, item: str) -> "Any": # type: ignore[explicit-any] # noqa
134134

135135
raise KeyError(key_error_message) from None
136136

137-
@staticmethod
138-
def _setup_logging() -> None:
137+
@classmethod
138+
def _setup_discord_log_channel_webhook(cls) -> "Logger":
139+
raw_discord_log_channel_webhook_url: str = os.getenv(
140+
"DISCORD_LOG_CHANNEL_WEBHOOK_URL",
141+
"",
142+
)
143+
144+
if raw_discord_log_channel_webhook_url and (
145+
not validators.url(raw_discord_log_channel_webhook_url)
146+
or not raw_discord_log_channel_webhook_url.startswith(
147+
"https://discord.com/api/webhooks/"
148+
)
149+
):
150+
INVALID_DISCORD_LOG_CHANNEL_WEBHOOK_URL_MESSAGE: Final[str] = (
151+
"DISCORD_LOG_CHANNEL_WEBHOOK_URL must be a valid webhook URL "
152+
"that points to a discord channel where logs should be displayed."
153+
)
154+
raise ImproperlyConfiguredError(INVALID_DISCORD_LOG_CHANNEL_WEBHOOK_URL_MESSAGE)
155+
156+
webhook_config_logger: Logger = logging.getLogger("_temp_webhook_config")
157+
158+
if raw_discord_log_channel_webhook_url:
159+
discord_logging_handler: logging.Handler = DiscordHandler(
160+
service_name="TeX-Bot",
161+
webhook_url=raw_discord_log_channel_webhook_url,
162+
)
163+
164+
discord_logging_handler.setLevel(logging.WARNING)
165+
166+
discord_logging_handler.setFormatter(
167+
logging.Formatter("{levelname} | {message}", style="{"),
168+
)
169+
170+
webhook_config_logger.addHandler(discord_logging_handler)
171+
172+
cls._settings["DISCORD_LOG_CHANNEL_WEBHOOK_URL"] = raw_discord_log_channel_webhook_url
173+
174+
return webhook_config_logger
175+
176+
@classmethod
177+
def _setup_logging(cls) -> None:
139178
raw_console_log_level: str = str(os.getenv("CONSOLE_LOG_LEVEL", "INFO")).upper()
140179

141180
if raw_console_log_level not in LOG_LEVEL_CHOICES:
@@ -177,47 +216,6 @@ def _setup_discord_bot_token(cls) -> None:
177216

178217
cls._settings["DISCORD_BOT_TOKEN"] = raw_discord_bot_token
179218

180-
@classmethod
181-
def _setup_discord_log_channel_webhook_url(cls) -> None:
182-
raw_discord_log_channel_webhook_url: str = os.getenv(
183-
"DISCORD_LOG_CHANNEL_WEBHOOK_URL",
184-
"",
185-
)
186-
187-
DISCORD_LOG_CHANNEL_WEBHOOK_URL_IS_VALID: Final[bool] = bool(
188-
not raw_discord_log_channel_webhook_url
189-
or (
190-
validators.url(raw_discord_log_channel_webhook_url)
191-
and raw_discord_log_channel_webhook_url.startswith(
192-
"https://discord.com/api/webhooks/",
193-
)
194-
),
195-
)
196-
if not DISCORD_LOG_CHANNEL_WEBHOOK_URL_IS_VALID:
197-
INVALID_DISCORD_LOG_CHANNEL_WEBHOOK_URL_MESSAGE: Final[str] = (
198-
"DISCORD_LOG_CHANNEL_WEBHOOK_URL must be a valid webhook URL "
199-
"that points to a discord channel where logs should be displayed."
200-
)
201-
raise ImproperlyConfiguredError(INVALID_DISCORD_LOG_CHANNEL_WEBHOOK_URL_MESSAGE)
202-
203-
cls._settings["DISCORD_LOG_CHANNEL_WEBHOOK_URL"] = raw_discord_log_channel_webhook_url
204-
205-
@classmethod
206-
def _setup_discord_logging_channel(cls) -> None:
207-
if cls._settings["DISCORD_LOG_CHANNEL_WEBHOOK_URL"]:
208-
discord_logging_handler: logging.Handler = DiscordHandler(
209-
service_name="TeX-Bot",
210-
webhook_url=cls._settings["DISCORD_LOG_CHANNEL_WEBHOOK_URL"],
211-
)
212-
213-
discord_logging_handler.setLevel(logging.WARNING)
214-
215-
discord_logging_handler.setFormatter(
216-
logging.Formatter("{levelname} | {message}", style="{"),
217-
)
218-
219-
logger.addHandler(discord_logging_handler)
220-
221219
@classmethod
222220
def _setup_discord_guild_id(cls) -> None:
223221
raw_discord_guild_id: str | None = os.getenv("DISCORD_GUILD_ID")
@@ -230,7 +228,6 @@ def _setup_discord_guild_id(cls) -> None:
230228
"DISCORD_GUILD_ID must be a valid Discord guild ID "
231229
"(see https://docs.pycord.dev/en/stable/api/abcs.html#discord.abc.Snowflake.id)."
232230
)
233-
logger.error(INVALID_DISCORD_GUILD_ID_MESSAGE)
234231
raise ImproperlyConfiguredError(INVALID_DISCORD_GUILD_ID_MESSAGE)
235232

236233
cls._settings["_DISCORD_MAIN_GUILD_ID"] = int(raw_discord_guild_id) # type: ignore[arg-type]
@@ -247,8 +244,8 @@ def _setup_group_full_name(cls) -> None:
247244
INVALID_GROUP_FULL_NAME: Final[str] = (
248245
"GROUP_NAME must not contain any invalid characters."
249246
)
250-
logger.error(INVALID_GROUP_FULL_NAME)
251247
raise ImproperlyConfiguredError(INVALID_GROUP_FULL_NAME)
248+
252249
cls._settings["_GROUP_FULL_NAME"] = raw_group_full_name
253250

254251
@classmethod
@@ -263,8 +260,8 @@ def _setup_group_short_name(cls) -> None:
263260
INVALID_GROUP_SHORT_NAME: Final[str] = (
264261
"GROUP_SHORT_NAME must not contain any invalid characters."
265262
)
266-
logger.error(INVALID_GROUP_SHORT_NAME)
267263
raise ImproperlyConfiguredError(INVALID_GROUP_SHORT_NAME)
264+
268265
cls._settings["_GROUP_SHORT_NAME"] = raw_group_short_name
269266

270267
@classmethod
@@ -278,7 +275,6 @@ def _setup_purchase_membership_url(cls) -> None:
278275
INVALID_PURCHASE_MEMBERSHIP_URL_MESSAGE: Final[str] = (
279276
"PURCHASE_MEMBERSHIP_URL must be a valid URL."
280277
)
281-
logger.error(INVALID_PURCHASE_MEMBERSHIP_URL_MESSAGE)
282278
raise ImproperlyConfiguredError(INVALID_PURCHASE_MEMBERSHIP_URL_MESSAGE)
283279

284280
cls._settings["PURCHASE_MEMBERSHIP_URL"] = raw_purchase_membership_url
@@ -294,7 +290,6 @@ def _setup_membership_perks_url(cls) -> None:
294290
INVALID_MEMBERSHIP_PERKS_URL_MESSAGE: Final[str] = (
295291
"MEMBERSHIP_PERKS_URL must be a valid URL."
296292
)
297-
logger.error(INVALID_MEMBERSHIP_PERKS_URL_MESSAGE)
298293
raise ImproperlyConfiguredError(INVALID_MEMBERSHIP_PERKS_URL_MESSAGE)
299294

300295
cls._settings["MEMBERSHIP_PERKS_URL"] = raw_membership_perks_url
@@ -311,7 +306,6 @@ def _setup_ping_command_easter_egg_probability(cls) -> None:
311306
os.getenv("PING_COMMAND_EASTER_EGG_PROBABILITY", "0.01"),
312307
)
313308
except ValueError as e:
314-
logger.error(INVALID_PING_COMMAND_EASTER_EGG_PROBABILITY_MESSAGE) # noqa: TRY400
315309
raise (
316310
ImproperlyConfiguredError(INVALID_PING_COMMAND_EASTER_EGG_PROBABILITY_MESSAGE)
317311
) from e
@@ -343,7 +337,6 @@ def _get_messages_dict(cls, raw_messages_file_path: str | None) -> Mapping[str,
343337
MESSAGES_FILE_PATH_DOES_NOT_EXIST_MESSAGE: Final[str] = (
344338
"MESSAGES_FILE_PATH must be a path to a file that exists."
345339
)
346-
logger.error(MESSAGES_FILE_PATH_DOES_NOT_EXIST_MESSAGE)
347340
raise ImproperlyConfiguredError(MESSAGES_FILE_PATH_DOES_NOT_EXIST_MESSAGE)
348341

349342
messages_file: IO[str]
@@ -352,11 +345,9 @@ def _get_messages_dict(cls, raw_messages_file_path: str | None) -> Mapping[str,
352345
try:
353346
messages_dict: object = json.load(messages_file)
354347
except json.JSONDecodeError as e:
355-
logger.error(JSON_DECODING_ERROR_MESSAGE) # noqa: TRY400
356348
raise ImproperlyConfiguredError(JSON_DECODING_ERROR_MESSAGE) from e
357349

358350
if not isinstance(messages_dict, Mapping):
359-
logger.error(JSON_DECODING_ERROR_MESSAGE)
360351
raise ImproperlyConfiguredError(JSON_DECODING_ERROR_MESSAGE)
361352

362353
return messages_dict
@@ -375,7 +366,6 @@ def _setup_welcome_messages(cls) -> None:
375366
and messages_dict["welcome_messages"],
376367
)
377368
if not WELCOME_MESSAGES_KEY_IS_VALID:
378-
logger.error("Unable to locate welcome messages.")
379369
raise MessagesJSONFileValueError(
380370
dict_key="welcome_messages",
381371
invalid_value=messages_dict["welcome_messages"],
@@ -396,11 +386,11 @@ def _setup_roles_messages(cls) -> None:
396386
messages_dict["roles_messages"], Iterable
397387
) and bool(messages_dict["roles_messages"])
398388
if not ROLES_MESSAGES_KEY_IS_VALID:
399-
logger.error("Unable to locate role messages.")
400389
raise MessagesJSONFileValueError(
401390
dict_key="roles_messages",
402391
invalid_value=messages_dict["roles_messages"],
403392
)
393+
404394
cls._settings["ROLES_MESSAGES"] = set(messages_dict["roles_messages"]) # type: ignore[call-overload]
405395

406396
@classmethod
@@ -415,7 +405,6 @@ def _setup_organisation_id(cls) -> None:
415405
INVALID_ORGANISATION_ID_MESSAGE: Final[str] = (
416406
"ORGANISATION_ID must be an integer 4 to 5 digits long."
417407
)
418-
logger.error(INVALID_ORGANISATION_ID_MESSAGE)
419408
raise ImproperlyConfiguredError(message=INVALID_ORGANISATION_ID_MESSAGE)
420409

421410
cls._settings["ORGANISATION_ID"] = raw_organisation_id
@@ -434,7 +423,6 @@ def _setup_members_list_auth_session_cookie(cls) -> None:
434423
INVALID_MEMBERS_LIST_AUTH_SESSION_COOKIE_MESSAGE: Final[str] = (
435424
"MEMBERS_LIST_URL_SESSION_COOKIE must be a valid .ASPXAUTH cookie."
436425
)
437-
logger.error(INVALID_MEMBERS_LIST_AUTH_SESSION_COOKIE_MESSAGE)
438426
raise ImproperlyConfiguredError(INVALID_MEMBERS_LIST_AUTH_SESSION_COOKIE_MESSAGE)
439427

440428
cls._settings["MEMBERS_LIST_AUTH_SESSION_COOKIE"] = (
@@ -451,7 +439,6 @@ def _setup_send_introduction_reminders(cls) -> None:
451439
INVALID_SEND_INTRODUCTION_REMINDERS_MESSAGE: Final[str] = (
452440
'SEND_INTRODUCTION_REMINDERS must be one of: "Once", "Interval" or "False".'
453441
)
454-
logger.error(INVALID_SEND_INTRODUCTION_REMINDERS_MESSAGE)
455442
raise ImproperlyConfiguredError(INVALID_SEND_INTRODUCTION_REMINDERS_MESSAGE)
456443

457444
if raw_send_introduction_reminders in TRUE_VALUES:
@@ -484,7 +471,6 @@ def _setup_send_introduction_reminders_delay(cls) -> None:
484471
"SEND_INTRODUCTION_REMINDERS_DELAY must contain the delay "
485472
"in any combination of seconds, minutes, hours, days or weeks."
486473
)
487-
logger.error(INVALID_SEND_INTRODUCTION_REMINDERS_DELAY_MESSAGE)
488474
raise ImproperlyConfiguredError(
489475
INVALID_SEND_INTRODUCTION_REMINDERS_DELAY_MESSAGE,
490476
)
@@ -502,7 +488,6 @@ def _setup_send_introduction_reminders_delay(cls) -> None:
502488
"SEND_INTRODUCTION_REMINDERS_DELAY must be longer than or equal to 1 day "
503489
"(in any allowed format)."
504490
)
505-
logger.error(TOO_SMALL_SEND_INTRODUCTION_REMINDERS_DELAY_MESSAGE)
506491
raise ImproperlyConfiguredError(
507492
TOO_SMALL_SEND_INTRODUCTION_REMINDERS_DELAY_MESSAGE,
508493
)
@@ -518,7 +503,6 @@ def _setup_send_introduction_reminders_interval(cls) -> None:
518503
"Invalid setup order: SEND_INTRODUCTION_REMINDERS must be set up "
519504
"before SEND_INTRODUCTION_REMINDERS_INTERVAL can be set up."
520505
)
521-
logger.error(INVALID_SETUP_ORDER_MESSAGE)
522506
raise RuntimeError(INVALID_SETUP_ORDER_MESSAGE)
523507

524508
raw_send_introduction_reminders_interval: re.Match[str] | None = re.fullmatch(
@@ -536,7 +520,6 @@ def _setup_send_introduction_reminders_interval(cls) -> None:
536520
"SEND_INTRODUCTION_REMINDERS_INTERVAL must contain the interval "
537521
"in any combination of seconds, minutes or hours."
538522
)
539-
logger.error(INVALID_SEND_INTRODUCTION_REMINDERS_INTERVAL_MESSAGE)
540523
raise ImproperlyConfiguredError(
541524
INVALID_SEND_INTRODUCTION_REMINDERS_INTERVAL_MESSAGE,
542525
)
@@ -561,7 +544,6 @@ def _setup_send_get_roles_reminders(cls) -> None:
561544
INVALID_SEND_GET_ROLES_REMINDERS_MESSAGE: Final[str] = (
562545
"SEND_GET_ROLES_REMINDERS must be a boolean value."
563546
)
564-
logger.error(INVALID_SEND_GET_ROLES_REMINDERS_MESSAGE)
565547
raise ImproperlyConfiguredError(INVALID_SEND_GET_ROLES_REMINDERS_MESSAGE)
566548

567549
cls._settings["SEND_GET_ROLES_REMINDERS"] = raw_send_get_roles_reminders in TRUE_VALUES
@@ -588,7 +570,6 @@ def _setup_send_get_roles_reminders_delay(cls) -> None:
588570
"SEND_GET_ROLES_REMINDERS_DELAY must contain the delay "
589571
"in any combination of seconds, minutes, hours, days or weeks."
590572
)
591-
logger.error(INVALID_SEND_GET_ROLES_REMINDERS_DELAY_MESSAGE)
592573
raise ImproperlyConfiguredError(
593574
INVALID_SEND_GET_ROLES_REMINDERS_DELAY_MESSAGE,
594575
)
@@ -606,7 +587,6 @@ def _setup_send_get_roles_reminders_delay(cls) -> None:
606587
"SEND_SEND_GET_ROLES_REMINDERS_DELAY "
607588
"must be longer than or equal to 1 day (in any allowed format)."
608589
)
609-
logger.error(TOO_SMALL_SEND_GET_ROLES_REMINDERS_DELAY_MESSAGE)
610590
raise ImproperlyConfiguredError(
611591
TOO_SMALL_SEND_GET_ROLES_REMINDERS_DELAY_MESSAGE,
612592
)
@@ -622,7 +602,6 @@ def _setup_advanced_send_get_roles_reminders_interval(cls) -> None:
622602
"Invalid setup order: SEND_GET_ROLES_REMINDERS must be set up "
623603
"before ADVANCED_SEND_GET_ROLES_REMINDERS_INTERVAL can be set up."
624604
)
625-
logger.error(INVALID_SETUP_ORDER_MESSAGE)
626605
raise RuntimeError(INVALID_SETUP_ORDER_MESSAGE)
627606

628607
raw_advanced_send_get_roles_reminders_interval: re.Match[str] | None = re.fullmatch(
@@ -642,7 +621,6 @@ def _setup_advanced_send_get_roles_reminders_interval(cls) -> None:
642621
"ADVANCED_SEND_GET_ROLES_REMINDERS_INTERVAL must contain the interval "
643622
"in any combination of seconds, minutes or hours."
644623
)
645-
logger.error(INVALID_ADVANCED_SEND_GET_ROLES_REMINDERS_INTERVAL_MESSAGE)
646624
raise ImproperlyConfiguredError(
647625
INVALID_ADVANCED_SEND_GET_ROLES_REMINDERS_INTERVAL_MESSAGE,
648626
)
@@ -668,7 +646,6 @@ def _setup_statistics_days(cls) -> None:
668646
INVALID_STATISTICS_DAYS_MESSAGE: Final[str] = (
669647
"STATISTICS_DAYS must contain the statistics period in days."
670648
)
671-
logger.error(INVALID_STATISTICS_DAYS_MESSAGE) # noqa: TRY400
672649
raise ImproperlyConfiguredError(INVALID_STATISTICS_DAYS_MESSAGE) from e
673650

674651
cls._settings["STATISTICS_DAYS"] = timedelta(days=raw_statistics_days)
@@ -698,7 +675,6 @@ def _setup_moderation_document_url(cls) -> None:
698675
MODERATION_DOCUMENT_URL_MESSAGE: Final[str] = (
699676
"MODERATION_DOCUMENT_URL must be a valid URL."
700677
)
701-
logger.error(MODERATION_DOCUMENT_URL_MESSAGE)
702678
raise ImproperlyConfiguredError(MODERATION_DOCUMENT_URL_MESSAGE)
703679

704680
cls._settings["MODERATION_DOCUMENT_URL"] = raw_moderation_document_url
@@ -714,7 +690,6 @@ def _setup_strike_performed_manually_warning_location(cls) -> None:
714690
"MANUAL_MODERATION_WARNING_MESSAGE_LOCATION must be a valid name "
715691
"of a channel in your group's Discord guild."
716692
)
717-
logger.error(STRIKE_PERFORMED_MANUALLY_WARNING_LOCATION_MESSAGE)
718693
raise ImproperlyConfiguredError(STRIKE_PERFORMED_MANUALLY_WARNING_LOCATION_MESSAGE)
719694

720695
cls._settings["STRIKE_PERFORMED_MANUALLY_WARNING_LOCATION"] = (
@@ -751,31 +726,35 @@ def _setup_env_variables(cls) -> None:
751726

752727
dotenv.load_dotenv()
753728

754-
cls._setup_logging()
755-
cls._setup_discord_bot_token()
756-
cls._setup_discord_log_channel_webhook_url()
757-
cls._setup_discord_logging_channel()
758-
cls._setup_discord_guild_id()
759-
cls._setup_group_full_name()
760-
cls._setup_group_short_name()
761-
cls._setup_ping_command_easter_egg_probability()
762-
cls._setup_welcome_messages()
763-
cls._setup_roles_messages()
764-
cls._setup_organisation_id()
765-
cls._setup_members_list_auth_session_cookie()
766-
cls._setup_membership_perks_url()
767-
cls._setup_purchase_membership_url()
768-
cls._setup_send_introduction_reminders()
769-
cls._setup_send_introduction_reminders_delay()
770-
cls._setup_send_introduction_reminders_interval()
771-
cls._setup_send_get_roles_reminders()
772-
cls._setup_send_get_roles_reminders_delay()
773-
cls._setup_advanced_send_get_roles_reminders_interval()
774-
cls._setup_statistics_days()
775-
cls._setup_statistics_roles()
776-
cls._setup_moderation_document_url()
777-
cls._setup_strike_performed_manually_warning_location()
778-
cls._setup_auto_add_committee_to_threads()
729+
webhook_config_logger: Logger = cls._setup_discord_log_channel_webhook()
730+
731+
try:
732+
cls._setup_logging()
733+
cls._setup_discord_bot_token()
734+
cls._setup_discord_guild_id()
735+
cls._setup_group_full_name()
736+
cls._setup_group_short_name()
737+
cls._setup_ping_command_easter_egg_probability()
738+
cls._setup_welcome_messages()
739+
cls._setup_roles_messages()
740+
cls._setup_organisation_id()
741+
cls._setup_members_list_auth_session_cookie()
742+
cls._setup_membership_perks_url()
743+
cls._setup_purchase_membership_url()
744+
cls._setup_send_introduction_reminders()
745+
cls._setup_send_introduction_reminders_delay()
746+
cls._setup_send_introduction_reminders_interval()
747+
cls._setup_send_get_roles_reminders()
748+
cls._setup_send_get_roles_reminders_delay()
749+
cls._setup_advanced_send_get_roles_reminders_interval()
750+
cls._setup_statistics_days()
751+
cls._setup_statistics_roles()
752+
cls._setup_moderation_document_url()
753+
cls._setup_strike_performed_manually_warning_location()
754+
cls._setup_auto_add_committee_to_threads()
755+
except ImproperlyConfiguredError as e:
756+
webhook_config_logger.error(e.message) # noqa: TRY400
757+
raise e from e
779758

780759
cls._is_env_variables_setup = True
781760

@@ -799,8 +778,7 @@ class RuntimeSettings(Settings):
799778

800779

801780
def run_setup() -> None:
802-
"""Execute the setup functions required, before other modules can be run."""
803-
# noinspection PyProtectedMember
781+
"""Execute the required setup functions."""
804782
settings._setup_env_variables() # noqa: SLF001
805783

806784
logger.debug("Begin database setup")

0 commit comments

Comments
 (0)