I've seen mentioned that with Python's built-in logging functionality you can create multiple handlers to output logs of different levels to different locations. I've already setup my logger with a QueueHandler and a RotatingFileHandler so that log messages are queued and written to a rotating file so as to minimally impact the runtime of my other code.
What I want to do next is use an email handler to notify myself if my application encounters any higher level issues during execution.
I've found answers like this: Send email with Python logging, but I haven't yet found something that combines everything I'm doing.
Below is the code I'm currently working with.
import logging
import logging.handlers
import logging.config
import os
import platform
from queue import Queue
class JSONLogFormatter(logging.Formatter):
def format(self, record):
# removed for brevity, this formats the log output...
return json.dumps(log_record)
def get_log_path(app_name):
# removed for brevity, this generates log_path based on the OS my code is running on
return log_path
def initialize(app_name="app_name", log_level=logging.DEBUG, log_size=10*1024*1024, num_files=10):
# Create the main logger
logger = logging.getLogger(__name__)
logger.setLevel(log_level)
# Create a queue for log records
log_queue = Queue()
# Create a QueueHandler to send logs to the queue
queue_handler = logging.handlers.QueueHandler(log_queue)
# Set a log level for the QueueHandler if needed
# queue_handler.setLevel(log_level)
# Add the Handler to the main logger
logger.addHandler(queue_handler)
# Get the log path
log_path = get_log_path(app_name)
# Create a RotatingFileHandler that manages file size & rotation
rotating_file_handler = logging.handlers.RotatingFileHandler(
log_path,
log_size, # default = 10x1024x1024 = 10 MB per log file
num_files # default = 10 = 100 MBs worth of logs
)
rotating_file_handler.setFormatter(JSONLogFormatter())
# Set a log level for the RotatingFileHandler if needed
# rotating_file_handler.setLevel(log_level)
# Create a QueueListener to process records in a separate thread
queue_listener = logging.handlers.QueueListener(log_queue, rotating_file_handler)
return logger, queue_listener
def _test():
logger, listener = initialize()
listener.start()
try:
logger.info("This log entry should go to a platform-appropriate directory.")
finally:
listener.stop()
You can use python's SMTPHandler.
Create SMTPHandler instance.
Set the SMPTHandler instance level to 'WARNING'.
Add it to QueueListener. You can add multiple handlers to QueueListener.
QueueListener(queue, *handlers, respect_handler_level=False)
Set respect_handler_level
to True
If
respect_handler_level
is True, a handler’s level is respected (compared with the level for the message) when deciding whether to pass messages to that handler.
import logging
from logging.handlers import SMTPHandler
def initialize(app_name="app_name", log_level=logging.DEBUG, log_size=10*1024*1024, num_files=10):
# Create the main logger
logger = logging.getLogger(__name__)
logger.setLevel(log_level)
# provide necessary arguments
mailHandler = SMTPHandler(mailhost, fromaddr, toaddrs=['user1@gmail.com', 'user2@gmail.com'], subject, credentials=(username, password), secure=())
mailHandler.setLevel(logging.WARNING)
logger.addHandler(mailHandler)
# Your code here
queue_listener = logging.handlers.QueueListener(log_queue, rotating_file_handler, mailHandler, respect_handler_level=True)
return logger, queue_listener
def _test():
logger, listener = initialize()
listener.start()
try:
logger.info("This log entry should go to a platform-appropriate directory.")
logger.warning('this is a warning log')
finally:
listener.stop()
"want to send an email when logger.warn() or worse is called"
Note:
logger.warn()
is deprecated, do not use it - use logger.warning()
instead. link