Pythonで独自のロギングハンドラ
Pythonのloggingを利用していると、独自のハンドラを作りたくなるときがある。
そういう時には、すでにloggingに定義されているハンドラのコードを参考にすることができる。
例えば、logging.handlers.SMTPHandler
は下記のように定義されている。
https://github.com/python/cpython/blob/master/Lib/logging/handlers.py
# ソースコード内のコメントは削除
class SMTPHandler(logging.Handler):
def __init__(self, mailhost, fromaddr, toaddrs, subject,
credentials=None, secure=None, timeout=5.0):
logging.Handler.__init__(self)
if isinstance(mailhost, (list, tuple)):
self.mailhost, self.mailport = mailhost
else:
self.mailhost, self.mailport = mailhost, None
if isinstance(credentials, (list, tuple)):
self.username, self.password = credentials
else:
self.username = None
self.fromaddr = fromaddr
if isinstance(toaddrs, str):
toaddrs = [toaddrs]
self.toaddrs = toaddrs
self.subject = subject
self.secure = secure
self.timeout = timeout
def getSubject(self, record):
return self.subject
def emit(self, record):
try:
import smtplib
from email.message import EmailMessage
import email.utils
port = self.mailport
if not port:
port = smtplib.SMTP_PORT
smtp = smtplib.SMTP(self.mailhost, port, timeout=self.timeout)
msg = EmailMessage()
msg['From'] = self.fromaddr
msg['To'] = ','.join(self.toaddrs)
msg['Subject'] = self.getSubject(record)
msg['Date'] = email.utils.localtime()
msg.set_content(self.format(record))
if self.username:
if self.secure is not None:
smtp.ehlo()
smtp.starttls(*self.secure)
smtp.ehlo()
smtp.login(self.username, self.password)
smtp.send_message(msg)
smtp.quit()
except Exception:
self.handleError(record)
これを参考にすると、独自ハンドラを作るには、logging.Handler
クラスを継承して、必要に応じて、親クラスのメソッドをオーバーライド、もしくはメソッド追加を行えばよいということが分かる。
その際、emit
メソッドだけは、子クラスでオーバーライドして実装してあげる必要がある。
もし、これを実装しなければ、必ずNotImplementedError
が発生するようになっている。
emit
は、ログの書き込み処理や送信処理を行うメソッドであるので、実装が必要なのはまあ当然である。
ちなみに、親クラスとなるlogging.Handler
は、下記のようになっている。
https://github.com/python/cpython/blob/master/Lib/logging/__init__.py
# ソースコード内のコメントは削除
class Handler(Filterer):
def __init__(self, level=NOTSET):
Filterer.__init__(self)
self._name = None
self.level = _checkLevel(level)
self.formatter = None
_addHandlerRef(self)
self.createLock()
def get_name(self):
return self._name
def set_name(self, name):
_acquireLock()
try:
if self._name in _handlers:
del _handlers[self._name]
self._name = name
if name:
_handlers[name] = self
finally:
_releaseLock()
name = property(get_name, set_name)
def createLock(self):
self.lock = threading.RLock()
_register_at_fork_reinit_lock(self)
def acquire(self):
if self.lock:
self.lock.acquire()
def release(self):
if self.lock:
self.lock.release()
def setLevel(self, level):
self.level = _checkLevel(level)
def format(self, record):
if self.formatter:
fmt = self.formatter
else:
fmt = _defaultFormatter
return fmt.format(record)
def emit(self, record):
raise NotImplementedError('emit must be implemented '
'by Handler subclasses')
def handle(self, record):
rv = self.filter(record)
if rv:
self.acquire()
try:
self.emit(record)
finally:
self.release()
return rv
def setFormatter(self, fmt):
self.formatter = fmt
def flush(self):
pass
def close(self):
_acquireLock()
try:
if self._name and self._name in _handlers:
del _handlers[self._name]
finally:
_releaseLock()
def handleError(self, record):
if raiseExceptions and sys.stderr:
t, v, tb = sys.exc_info()
try:
sys.stderr.write('--- Logging error ---\n')
traceback.print_exception(t, v, tb, None, sys.stderr)
sys.stderr.write('Call stack:\n')
frame = tb.tb_frame
while (frame and os.path.dirname(frame.f_code.co_filename) ==
__path__[0]):
frame = frame.f_back
if frame:
traceback.print_stack(frame, file=sys.stderr)
else:
sys.stderr.write('Logged from file %s, line %s\n' % (
record.filename, record.lineno))
try:
sys.stderr.write('Message: %r\n'
'Arguments: %s\n' % (record.msg,
record.args))
except RecursionError:
raise
except Exception:
sys.stderr.write('Unable to print the message and arguments'
' - possible formatting error.\nUse the'
' traceback above to help find the error.\n'
)
except OSError:
pass
finally:
del t, v, tb
def __repr__(self):
level = getLevelName(self.level)
return '<%s (%s)>' % (self.__class__.__name__, level)
自分の場合、DBのメール機能を使ってログを送りたかったので、DBMailHandler
クラスを作った。
Read other posts