Я реализовал регистратор в виде одиночного файла, в котором все сообщения go помещаются в очередь, и поток демона собирает эти сообщения из очереди и печатает их. Причина, по которой я использовал поток демона, заключается в том, что мне не нужно явно закрывать регистратор, как только я закончу (или приложение завершится). Я ожидал, что после удаления регистратора (сборщиком мусора) при закрытии приложения будет запущен метод __del__
, который после этого будет очищен. Я был удивлен, что это не так.
Когда я изменил поток на не-демон, он работал просто отлично (очевидно, мне пришлось внести некоторые другие изменения, чтобы приложение закрывалось). Интересно, если я что-то не так делаю, или это просто плохая практика в целом.
Приложенный здесь код: (Я хотел бы предложить, что все после функции __del__
не интересно).
import os
import sys
import time
import Queue
import weakref
import datetime
import threading
class Logger(object):
"""
Logger class implemented with a queue of messages, and supports only a single instace.
This instance can be acquired by using the "GetLogger" method.
"""
__instance = None
@classmethod
def GetLogger(cls, fpath, source_name=None):
if cls.__instance is None:
return Logger(fpath, source_name=source_name)
else:
if source_name is None:
cls.__instance().log('%s@%s: %s\n' % (cls.current_date(), cls.current_time(), "Using existing Logger instance"), "REUSAGE")
else:
cls.__instance().log('%s@%s - %-17s: %s\n' % (cls.current_date(), cls.current_time(), source_name, "Using existing Logger instance"), "REUSAGE")
return cls.__instance()
def __init__(self, fpath, start_time = time.time(), source_name=None):
if self.__instance is not None:
raise ValueError("Singleton object already exists")
self.__instance = weakref.ref(self)
self.__start_time = start_time
self.__queue = Queue.Queue()
self.__listener = threading.Thread(target=self._listen)
self.__listener.daemon = True
self.__listener.start()
if not os.path.exists(os.path.dirname(fpath)):
os.makedirs(os.path.dirname(fpath))
self.__f = open(fpath, 'a')
if source_name is None:
self.log('%s@%s: %s\n' % (self.current_date(), self.current_time(), "Created a new Logger instance"), "CREATION")
else:
self.log('%s@%s - %-17s: %s\n' % (self.current_date(), self.current_time(), source_name, "Created a new Logger instance"), "CREATION")
@staticmethod
def current_date():
return str(datetime.datetime.now().date().isoformat())
@staticmethod
def current_time():
return str(datetime.datetime.now().time().isoformat())
def _listen(self):
while True:
msg = self.__queue.get()
if msg is None:
break
self.__print_message(msg)
def __print_message(self, msg_tup): # msg_tup = (message, level, stdout)
msg_time = time.time()
if msg_tup[2]:
try:
print(("|%013.6f|%-8s>>>%s" % (msg_time - self.__start_time, msg_tup[1], msg_tup[0]))),
sys.stdout.flush()
except:
pass
try:
self.__f.write("|%013.6f|%-8s>>>%s" % (msg_time - self.__start_time, msg_tup[1], msg_tup[0]))
self.__f.flush()
except:
pass
def log(self, msg, level, to_stdout=True):
self.__queue.put((msg, level, to_stdout))
def close(self):
self.__queue.put(None)
self.__instance = None
def __del__(self):
while not self.__queue.empty():
msg = self.__queue.get()
if msg is not None:
self.__print_message(msg)
print("Dead...")
self.__f.close()
self.close()
def info(self, msg, source_name=None, to_stdout=True):
if source_name is None:
self.log('%s@%s: %s\n' % (self.current_date(), self.current_time(), msg), "INFO", to_stdout=to_stdout)
else:
self.log('%s@%s - %-17s: %s\n' % (self.current_date(), self.current_time(), source_name, msg), "INFO", to_stdout=to_stdout)
def debug(self, msg, source_name=None, to_stdout=True):
if source_name is None:
self.log('%s@%s: %s\n' %
(self.current_date(), self.current_time(), msg), "DEBUG", to_stdout=to_stdout)
else:
self.log('%s@%s - %-17s: %s\n' % (self.current_date(), self.current_time(), source_name, msg), "DEBUG", to_stdout=to_stdout)
def trace(self, msg, sdource_name=None, to_stdout=True):
if source_name is None:
self.log('%s@%s: %s\n' %
(self.current_date(), self.current_time(), msg), "TRACE", to_stdout=to_stdout)
else:
self.log('%s@%s - %-17s: %s\n' % (self.current_date(), self.current_time(), source_name, msg), "TRACE", to_stdout=to_stdout)
def warn(self, msg, source_name=None, to_stdout=True):
if source_name is None:
self.log('%s@%s: %s\n' %
(self.current_date(), self.current_time(), msg), "WARN", to_stdout=to_stdout)
else:
self.log('%s@%s - %-17s: %s\n' % (self.current_date(), self.current_time(), source_name, msg), "WARN", to_stdout=to_stdout)
def error(self, msg, source_name=None, to_stdout=True):
if source_name is None:
self.log('%s@%s: %s\n' %
(self.current_date(), self.current_time(), msg), "ERROR", to_stdout=to_stdout)
else:
self.log('%s@%s - %-17s: %s\n' % (self.current_date(), self.current_time(), source_name, msg), "ERROR", to_stdout=to_stdout)
def critical(self, msg, source_name=None, to_stdout=True):
if source_name is None:
self.log('%s@%s: %s\n' %
(self.current_date(), self.current_time(), msg), "CRITICAL", to_stdout=to_stdout)
else:
self.log('%s@%s - %-17s: %s\n' % (self.current_date(), self.current_time(), source_name, msg), "CRITICAL", to_stdout=to_stdout)