I want to send an E-Mail automatically after each script run, whether it was successfull or not. The E-Mail should include logs and in case of an error the stack trace. The subject should contain a postfix that tells whether there the run was with errors ("ERROR") or without errors ("SUCCESS").
Currently I'm using using the follwing code, which does this in an ugly way:
if __name__ == '__main__':
# setup logging and e-mail messaging
logger = logging.getLogger(__name__) # Creating an instance of logger
logger.setLevel(logging.DEBUG)
mail_handler = BufferingSMTPHandler(fromaddr='xyz@test.de',
toaddr=['xyz@tzest.de'],
subject='JOB: Testjob')
mail_handler.setLevel(logging.INFO) # Set minimum level to receive messages
logger.addHandler(mail_handler)
# rest of __main__ is at the end of the file!
def handle_exception(exc_type, exc_value, exc_traceback):
if issubclass(exc_type, KeyboardInterrupt):
sys.__excepthook__(exc_type, exc_value, exc_traceback)
return
logger.critical("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback))
mail_handler.set_failed(True)
mail_handler.flush()
if __name__ == '__main__':
.... do work here
logger.info('TEST')
....
mail_handler.set_failed(False) # reached end of the script, so success is assumed
mail_handler.flush()
The BufferingSMTPHandler looks like this:
import logging
import logging.handlers
import smtplib
class BufferingSMTPHandler(logging.handlers.BufferingHandler):
def __init__(self, subject, fromaddr, toaddr, mailhost='mysmtpserver', capacity=500):
logging.handlers.BufferingHandler.__init__(self, capacity)
self.mailhost = mailhost
self.fromaddr = fromaddr
self.toaddr = toaddr
self.subject = subject
self.failed = False
self.setFormatter(logging.Formatter(
'{levelname} - {asctime} - {lineno} - {name} - In {funcName}: {message}',
style='{')) # Formatter to prettify logs
def set_failed(self, failed):
self.failed = failed
def flush(self): # Method to send emails
if len(self.buffer) <= 0:
return
with smtplib.SMTP(self.mailhost) as smtp:
if self.failed:
subject = self.subject + ' - ERROR'
else:
subject = self.subject + ' - SUCCESS'
body = ''
for record in self.buffer:
body += self.format(record) + '
' # Populating body of the message with formatted logs
msg = f'Subject: {subject}
{body}'
smtp.sendmail(self.fromaddr, self.toaddr, msg)
self.buffer.clear() # Clearing buffer to allude sending same logs
Is there a way to do this without adding so much code to the main script?
E.g. in this kind of way (pseudo-code):
from status_mailer import AutoStatusMailer
if __name__ == '__main__':
mailer = AutoStatusMailer(fromaddr='xyz@test.de', toaddr=['xyz@tzest.de'], subject='JOB: Testjob')
logger = mailer.get_logger()
.... do work here
logger.info('TEST')
...
... status mail is automatically sent afterwards