Skip to content

Commit b05f464

Browse files
committed
Enable discord.py logger by default.
1 parent d6eba12 commit b05f464

File tree

2 files changed

+152
-85
lines changed

2 files changed

+152
-85
lines changed

bot.py

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
11
__version__ = "4.0.2"
22

33

4+
# early import to initialize colorama
5+
from core.models import (
6+
DMDisabled,
7+
HostingMethod,
8+
InvalidConfigError,
9+
PermissionLevel,
10+
SafeFormatter,
11+
create_log_handler,
12+
configure_logging,
13+
getLogger,
14+
)
15+
416
import asyncio
517
import copy
618
import hashlib
@@ -25,33 +37,16 @@
2537
from pkg_resources import parse_version
2638

2739

28-
try:
29-
# noinspection PyUnresolvedReferences
30-
from colorama import init
31-
32-
init()
33-
except ImportError:
34-
pass
35-
3640
from core import checks
3741
from core.changelog import Changelog
3842
from core.clients import ApiClient, MongoDBClient, PluginDatabaseClient
3943
from core.config import ConfigManager
40-
from core.models import (
41-
DMDisabled,
42-
HostingMethod,
43-
InvalidConfigError,
44-
PermissionLevel,
45-
SafeFormatter,
46-
configure_logging,
47-
getLogger,
48-
)
4944
from core.thread import ThreadManager
5045
from core.time import human_timedelta
5146
from core.utils import extract_block_timestamp, normalize_alias, parse_alias, truncate, tryint
5247

53-
logger = getLogger(__name__)
5448

49+
logger = getLogger(__name__)
5550

5651
temp_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "temp")
5752
if not os.path.exists(temp_dir):
@@ -199,6 +194,28 @@ def _configure_logging(self):
199194

200195
logger.info("Log file: %s", self.log_file_name)
201196
configure_logging(self.log_file_name, log_level)
197+
198+
# Set up discord.py logging
199+
# repeat the step
200+
level_text = os.environ.get("LOG_DISCORD", "INFO").upper()
201+
log_level = logging_levels.get(level_text, logging.INFO)
202+
logger.info("Setting up discord logger, logging level: %s.", logging.getLevelName(log_level))
203+
d_logger = logging.getLogger("discord")
204+
d_logger.setLevel(log_level)
205+
is_debug = not log_level > logging.DEBUG
206+
stream = create_log_handler(level=log_level if not is_debug else logging.INFO)
207+
d_logger.addHandler(stream)
208+
file_handler = create_log_handler(
209+
self.log_file_name, rotating=True, level=log_level if not is_debug else logging.INFO
210+
)
211+
d_logger.addHandler(file_handler)
212+
213+
# internal discord.py debug logging
214+
if is_debug:
215+
debug_handler = create_log_handler("discord.log", level=log_level, mode="w", encoding="utf-8")
216+
logger.info("Discord logger DEBUG level is enabled. Log file: %s", "discord.log")
217+
d_logger.addHandler(debug_handler)
218+
202219
logger.debug("Successfully configured logging.")
203220

204221
@property
@@ -1797,16 +1814,6 @@ def main():
17971814
)
17981815
sys.exit(0)
17991816

1800-
# Set up discord.py internal logging
1801-
if os.environ.get("LOG_DISCORD"):
1802-
logger.debug(f"Discord logging enabled: {os.environ['LOG_DISCORD'].upper()}")
1803-
d_logger = logging.getLogger("discord")
1804-
1805-
d_logger.setLevel(os.environ["LOG_DISCORD"].upper())
1806-
handler = logging.FileHandler(filename="discord.log", encoding="utf-8", mode="w")
1807-
handler.setFormatter(logging.Formatter("%(asctime)s:%(levelname)s:%(name)s: %(message)s"))
1808-
d_logger.addHandler(handler)
1809-
18101817
bot = ModmailBot()
18111818
bot.run()
18121819

core/models.py

Lines changed: 117 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,26 @@
11
import logging
2+
import os
23
import re
34
import sys
4-
import os
5-
from difflib import get_close_matches
6-
from enum import IntEnum
7-
from logging.handlers import RotatingFileHandler
8-
from string import Formatter
95

10-
import discord
11-
from discord.ext import commands
6+
from logging import FileHandler, StreamHandler
7+
from logging.handlers import RotatingFileHandler
8+
from typing import Optional, Union
129

13-
import _string
1410

1511
try:
16-
from colorama import Fore, Style
12+
from colorama import Fore, Style, init as color_init
1713
except ImportError:
1814
Fore = Style = type("Dummy", (object,), {"__getattr__": lambda self, item: ""})()
15+
else:
16+
color_init()
1917

2018

2119
if ".heroku" in os.environ.get("PYTHONHOME", ""):
2220
# heroku
2321
Fore = Style = type("Dummy", (object,), {"__getattr__": lambda self, item: ""})()
2422

2523

26-
class PermissionLevel(IntEnum):
27-
OWNER = 5
28-
ADMINISTRATOR = 4
29-
ADMIN = 4
30-
MODERATOR = 3
31-
MOD = 3
32-
SUPPORTER = 2
33-
RESPONDER = 2
34-
REGULAR = 1
35-
INVALID = -1
36-
37-
38-
class InvalidConfigError(commands.BadArgument):
39-
def __init__(self, msg, *args):
40-
super().__init__(msg, *args)
41-
self.msg = msg
42-
43-
@property
44-
def embed(self):
45-
# Single reference of Color.red()
46-
return discord.Embed(title="Error", description=self.msg, color=discord.Color.red())
47-
48-
4924
class ModmailLogger(logging.Logger):
5025
@staticmethod
5126
def _debug_(*msgs):
@@ -94,18 +69,87 @@ def line(self, level="info"):
9469
)
9570

9671

97-
logging.setLoggerClass(ModmailLogger)
98-
log_level = logging.INFO
99-
loggers = set()
72+
class FileFormatter(logging.Formatter):
73+
ansi_escape = re.compile(r"\x1B\[[0-?]*[ -/]*[@-~]")
74+
75+
def format(self, record):
76+
record.msg = self.ansi_escape.sub("", record.msg)
77+
return super().format(record)
10078

101-
ch = logging.StreamHandler(stream=sys.stdout)
102-
ch.setLevel(log_level)
103-
formatter = logging.Formatter(
79+
80+
log_stream_formatter = logging.Formatter(
10481
"%(asctime)s %(name)s[%(lineno)d] - %(levelname)s: %(message)s", datefmt="%m/%d/%y %H:%M:%S"
10582
)
106-
ch.setFormatter(formatter)
83+
log_file_formatter = FileFormatter(
84+
"%(asctime)s %(name)s[%(lineno)d] - %(levelname)s: %(message)s",
85+
datefmt="%Y-%m-%d %H:%M:%S",
86+
)
87+
88+
89+
def create_log_handler(
90+
filename: Optional[str] = None,
91+
*,
92+
rotating: bool = False,
93+
level: int = logging.DEBUG,
94+
mode: str = "a+",
95+
encoding: str = "utf-8",
96+
maxBytes: int = 48000,
97+
backupCount: int = 1,
98+
**kwargs,
99+
) -> Union[FileHandler, RotatingFileHandler, StreamHandler]:
100+
"""
101+
Return a pre-configured log handler. This function is made for consistency sake with
102+
pre-defined default values for parameters and formatters to pass to handler class.
103+
Additional keyword arguments also can be specified, just in case.
104+
105+
Plugin developers should not use this and only use the `getLogger` instead to instantiate the ModmailLogger object.
106+
107+
Parameters
108+
-----------
109+
filename : Optional[Path]
110+
Specifies that a `FileHandler` or `RotatingFileHandler` be created, using the specified filename,
111+
rather than a `StreamHandler`. Defaults to `None`.
112+
rotating : bool
113+
Whether the file handler should be the `RotatingFileHandler`. Defaults to `False`. Note, this
114+
argument only compatible if the `filename` is specified, otherwise `ValueError` will be raised.
115+
level : int
116+
The root logger level for the handler. Defaults to `logging.DEBUG`.
117+
mode : str
118+
If filename is specified, open the file in this mode. Defaults to 'a+'.
119+
encoding : str
120+
If this keyword argument is specified along with filename, its value is used when the `FileHandler` is created,
121+
and thus used when opening the output file. Defaults to 'utf-8'.
122+
maxBytes : int
123+
The max file size before the rollover occurs. Defaults to 48000. Rollover occurs whenever the current log file
124+
is nearly `maxBytes` in length; but if either of `maxBytes` or `backupCount` is zero, rollover never occurs, so you
125+
generally want to set `backupCount` to at least 1.
126+
backupCount : int
127+
Max number of backup files. Defaults to 1. If this is set to zero, rollover will never occur.
128+
"""
129+
if filename is None and rotating:
130+
raise ValueError("`filename` must be set to instantiate a `RotatingFileHandler`.")
131+
132+
if filename is None:
133+
handler = StreamHandler(stream=sys.stdout, **kwargs)
134+
handler.setFormatter(log_stream_formatter)
135+
elif not rotating:
136+
handler = FileHandler(filename, mode=mode, encoding=encoding, **kwargs)
137+
handler.setFormatter(log_file_formatter)
138+
else:
139+
handler = RotatingFileHandler(
140+
filename, mode=mode, encoding=encoding, maxBytes=maxBytes, backupCount=backupCount, **kwargs
141+
)
142+
handler.setFormatter(log_file_formatter)
143+
144+
handler.setLevel(level)
145+
return handler
107146

108-
ch_debug = None
147+
148+
logging.setLoggerClass(ModmailLogger)
149+
log_level = logging.INFO
150+
loggers = set()
151+
ch: StreamHandler = create_log_handler(level=log_level)
152+
ch_debug: Optional[RotatingFileHandler] = None
109153

110154

111155
def getLogger(name=None) -> ModmailLogger:
@@ -118,24 +162,9 @@ def getLogger(name=None) -> ModmailLogger:
118162
return logger
119163

120164

121-
class FileFormatter(logging.Formatter):
122-
ansi_escape = re.compile(r"\x1B\[[0-?]*[ -/]*[@-~]")
123-
124-
def format(self, record):
125-
record.msg = self.ansi_escape.sub("", record.msg)
126-
return super().format(record)
127-
128-
129-
def configure_logging(name, level=None):
165+
def configure_logging(name, level: Optional[int] = None):
130166
global ch_debug, log_level
131-
ch_debug = RotatingFileHandler(name, mode="a+", maxBytes=48000, backupCount=1, encoding="utf-8")
132-
133-
formatter_debug = FileFormatter(
134-
"%(asctime)s %(name)s[%(lineno)d] - %(levelname)s: %(message)s",
135-
datefmt="%Y-%m-%d %H:%M:%S",
136-
)
137-
ch_debug.setFormatter(formatter_debug)
138-
ch_debug.setLevel(logging.DEBUG)
167+
ch_debug = create_log_handler(name, rotating=True)
139168

140169
if level is not None:
141170
log_level = level
@@ -147,6 +176,25 @@ def configure_logging(name, level=None):
147176
logger.addHandler(ch_debug)
148177

149178

179+
from string import Formatter
180+
from difflib import get_close_matches
181+
from enum import IntEnum
182+
import _string
183+
import discord
184+
from discord.ext import commands
185+
186+
187+
class InvalidConfigError(commands.BadArgument):
188+
def __init__(self, msg, *args):
189+
super().__init__(msg, *args)
190+
self.msg = msg
191+
192+
@property
193+
def embed(self):
194+
# Single reference of Color.red()
195+
return discord.Embed(title="Error", description=self.msg, color=discord.Color.red())
196+
197+
150198
class _Default:
151199
pass
152200

@@ -271,6 +319,18 @@ async def ack(self):
271319
return
272320

273321

322+
class PermissionLevel(IntEnum):
323+
OWNER = 5
324+
ADMINISTRATOR = 4
325+
ADMIN = 4
326+
MODERATOR = 3
327+
MOD = 3
328+
SUPPORTER = 2
329+
RESPONDER = 2
330+
REGULAR = 1
331+
INVALID = -1
332+
333+
274334
class DMDisabled(IntEnum):
275335
NONE = 0
276336
NEW_THREADS = 1

0 commit comments

Comments
 (0)