Source code for dbprocessing.DBlogging

"""Support for logging information from the dbprocessing chain."""

from __future__ import print_function

import datetime
import logging
import logging.handlers
import os
import time


utctoday = datetime.datetime.utcnow().date().strftime('%Y-%m-%d')
"""Current UTC date as YYYY-MM-DD (:class:`str`)"""

try:
    logname
except NameError:
    logname = None
    """Base name of the log file (``dbprocessing_`` is prepended and the date
       appended) (:class:`str`)"""

# TODO this should be setup by a config file
log_dir = os.environ.get('DBPROCESSING_LOG_DIR',
                         os.path.join('~', 'dbprocessing_logs'))
"""Directory to contain all dbprocessing log files (:class:`str`)"""
log_dir = os.path.expanduser(log_dir)
if not os.path.isdir(log_dir):
    os.makedirs(log_dir)
basename = 'dbprocessing_{0}'.format(logname if logname else 'log')
"""Name of log file without date (:class:`str`)"""
LOG_FILENAME = os.path.expanduser(os.path.join(log_dir, '{0}.log.{1}'.format(
    basename, utctoday)))
"""Full name of the log file (:class:`str`)"""

# Set up a specific logger with our desired output level
dblogger = logging.getLogger('DBLogger')
"""Logger instance for all dbprocessing code (:class:`~logging.Logger`)"""
dblogger.setLevel(logging.INFO)

# Add the log message handler to the logger
# handler = logging.handlers.TimedRotatingFileHandler(
#              LOG_FILENAME, maxBytes=20000000, backupCount=0) # keep them all
## TODO this doesn't work so hardcode the name above, so break the rotation here
# Without explicit encoding on the handler, logging during interpreter shutdown
# (e.g. DButils.closeDB(), from __del__) will fail.
# https://stackoverflow.com/questions/42372981/filehandler-encoding-producing-exception
handler = logging.handlers.TimedRotatingFileHandler(
    LOG_FILENAME, when='midnight', interval=1, backupCount=0, # keep them all
    utc=True, encoding='ascii')
"""Handler instance for all dbprocessing code
   (:class:`~logging.handlers.TimedRotatingFileHandler`)"""

LEVELS = { 'debug': logging.DEBUG,
           'info': logging.INFO,
           'warning': logging.WARNING,
           'error': logging.ERROR,
           'critical': logging.CRITICAL }
"""Map name of logging level names to numbers (:class:`dict`)"""

# create formatter
formatter = \
    logging.Formatter("%(asctime)s - %(module)s:%(lineno)d - %(levelname)s" +
                      " - %(message)s")
# """Log message formatter (:class:`~logging.Formatter`)"""
# Autodoc erroneously grabs the Formatter docstring, which
# isn't validly formatted for Sphinx, so leaving this docstring commented out.
logging.Formatter.converter = time.gmtime

# add formatter to ch
handler.setFormatter(formatter)

# add ch to logger
dblogger.addHandler(handler)

dblogger.info("DBLogger initialized")


[docs]def change_logfile(logname=None): """Switch to a new log file This implements switching default logging from one filename to another, usually used to indicate the mission being processed. The filename will always start with ``dbprocessing_`` and include a date string for the day. Parameters ---------- logname : :class:`str`, default "log" Name to include in full log filename. """ global LOG_FILENAME, handler, formatter, dblogger basename = 'dbprocessing_{0}'.format(logname if logname else 'log') old_filename = LOG_FILENAME LOG_FILENAME = os.path.expanduser(os.path.join(log_dir, '{0}.log.{1}'.format( basename, utctoday))) dblogger.info("Logging file switched from {0} to {1}".format(old_filename, LOG_FILENAME)) new_handler = logging.handlers.TimedRotatingFileHandler( LOG_FILENAME, when='midnight', interval=1, backupCount=0, # keep them all utc=True, encoding='ascii') new_handler.setFormatter(formatter) dblogger.removeHandler(handler) handler.close() dblogger.addHandler(new_handler) handler = new_handler dblogger.info("Switching logging file from {0} to {1}".format(old_filename, LOG_FILENAME))