Docstrings should be on the inside of a function or class, not on the outside - before the
deforclass.Your logger doesn't automatically log to the next day's log. As
YEAR,MONTH,DAYandfilenameare only defined once and never update.It's abnormal to see
Logger.filenamerather thanself.filenameorcls.filename. If you need to guarantee it's the class' value rather than the instances than you can dotype(self). This makes it harder to rename your class if you ever need to in the future.Your functions should be classmethods as they're interacting with the class. staticmethod is just wrong.
Due to a combination of the above two your class can't be instanced or subclassed. This is an artificial limitation and is un-Pythonic.
It's normally better to use
import xrather thanfrom x import .... The only time I've found this to not be the case, is when you're importing fromtyping. But every other time I've found code to be more readable. As you know what the function is, without scrolling to the top of the file.There's no benefit to using
datetime.now().today()as they return the same object, but with a slightly different time due to the delay in each call.It would be better to define the format once. Currently you're duplicating it across all functions. To do so you would need to pass a
datetime/timeobject and you can just use the format specifier.f"|{type}|{datetime:%I:%M}| {msg}\n"You don't need to manually call
str.ljustyou can just change your format to include:f"{type:< {Logger.PADDING}}"Don't use
returnon the same line as anif. It's un-Pythonic.PEP 8 - Python's style guide - suggests using only 2 spaces before an inline comments
#. You've used 1 and 5.Personally I dislike inline comments as they're hard to read. Putting the comment before the line has normally always been more readable for me.
I suggest you rename
logtoinfoand allowloghandle pretty much everything. For example interaction with the format string and also not logging if it's a debug message.You could just open the file once and leave the file object to be closed when Python exits - as that the only time you'd want to actually close the file.
If you're sceptical that Python will clean up the file on exit you can just register an exit handler with
atexit.Your log levels aren't great. What if I want to ignore non-exceptions?
You don't log to
std.outorstd.err, this is a short coming.
import datetime
class Logger:
"""If a log file for today already exist, open it in append mode.
Else, create a new log file for today, and open it in append mode.
"""
DEBUG = False
PADDING = 9
FORMAT = "|{type:< {cls.PADDING}}|{datetime:%I:%M}| {msg}\n"
FILE = open(f"logs/log{datetime.datetime.now():%d-%m-%Y}.txt", "w+")
def log(cls, msg, level):
if not cls.DEBUG and level == "DEBUG":
return
cls.FILE.write(cls.FORMAT.format(
type=level,
msg=msg,
datetime=datetime.datetime.now(),
))
@classmethod
def info(cls, msg):
"""Log info"""
cls.log(msg, "INFO")
@classmethod
def update(cls, msg):
"Used to log whenever a state is updated"
cls.log(msg, "UPDATE")
@classmethod
def exception(cls, msg):
cls.log(msg, "EXCEPTION")
@staticmethod
def debug(msg):
"Only logs if the static variable {DEBUG} is set to True."
cls.log(msg, "DEBUG")
@classmethod
def clear(cls):
"""Clears the log file"""
cls.FILE.truncate(0)
@staticmethod
def object(cls, object):
"""Intended for use on objects. They usually have a lot of information;
therefor, we enclose the information with lines to make the log more readable."""
cls.FILE.write(
f"----------------------- object log\n"
+ str(object)
+ f"\n-----------------------\n"
)
if __name__ == "__main__":
Logger.info("This is a test")
Logger.info("running something")
Logger.debug("Some debugging details")
Logger.exception("Critical error!")
Logger.debug("Some more debugging details")