# GeneNetwork logger
#
# The standard python logging module is very good. This logger adds a
# few facilities on top of that. Main one being that it picks up
# settings for log levels (global and by module) and (potentially)
# offers some fine grained log levels for the standard levels.
#
# All behaviour is defined here. Global settings (defined in
# default_settings.py).
#
# To use logging and settings put this at the top of a module:
#
# import utility.logger
# logger = utility.logger.getLogger(__name__ )
#
# To override global behaviour set the LOG_LEVEL in default_settings.py
# or use an environment variable, e.g.
#
# env LOG_LEVEL=INFO ./bin/genenetwork2
#
# To override log level for a module replace that with, for example,
#
# import logging
# import utility.logger
# logger = utility.logger.getLogger(__name__,level=logging.DEBUG)
#
# We'll add more overrides soon.
import logging
import string
from inspect import isfunction
from pprint import pformat as pf
from inspect import stack
import datetime
from utility.tools import LOG_LEVEL, LOG_LEVEL_DEBUG, LOG_SQL
class GNLogger:
"""A logger class with some additional functionality, such as
multiple parameter logging, SQL logging, timing, colors, and lazy
functions.
"""
def __init__(self, name):
self.logger = logging.getLogger(name)
def setLevel(self, value):
"""Set the undelying log level"""
self.logger.setLevel(value)
def debug(self, *args):
"""Call logging.debug for multiple args. Use (lazy) debugf and
level=num to filter on LOG_LEVEL_DEBUG.
"""
self.collect(self.logger.debug, *args)
def debug20(self, *args):
"""Call logging.debug for multiple args. Use level=num to filter on
LOG_LEVEL_DEBUG (NYI).
"""
if level <= LOG_LEVEL_DEBUG:
if self.logger.getEffectiveLevel() < 20:
self.collect(self.logger.debug, *args)
def info(self, *args):
"""Call logging.info for multiple args"""
self.collect(self.logger.info, *args)
def warning(self, *args):
"""Call logging.warning for multiple args"""
self.collect(self.logger.warning, *args)
# self.logger.warning(self.collect(*args))
def error(self, *args):
"""Call logging.error for multiple args"""
now = datetime.datetime.utcnow()
time_str = now.strftime('%H:%M:%S UTC %Y%m%d')
l = [time_str] + list(args)
self.collect(self.logger.error, *l)
def infof(self, *args):
"""Call logging.info for multiple args lazily"""
# only evaluate function when logging
if self.logger.getEffectiveLevel() < 30:
self.collectf(self.logger.debug, *args)
def debugf(self, level=0, *args):
"""Call logging.debug for multiple args lazily and handle
LOG_LEVEL_DEBUG correctly
"""
# only evaluate function when logging
if level <= LOG_LEVEL_DEBUG:
if self.logger.getEffectiveLevel() < 20:
self.collectf(self.logger.debug, *args)
def sql(self, sqlcommand, fun=None):
"""Log SQL command, optionally invoking a timed fun"""
if LOG_SQL:
caller = stack()[1][3]
if caller in ['fetchone', 'fetch1', 'fetchall']:
caller = stack()[2][3]
self.info(caller, sqlcommand)
if fun:
result = fun(sqlcommand)
if LOG_SQL:
self.info(result)
return result
def collect(self, fun, *args):
"""Collect arguments and use fun to output"""
out = "." + stack()[2][3]
for a in args:
if len(out) > 1:
out += ": "
if isinstance(a, str):
out = out + a
else:
out = out + pf(a, width=160)
fun(out)
def collectf(self, fun, *args):
"""Collect arguments and use fun to output one by one"""
out = "." + stack()[2][3]
for a in args:
if len(out) > 1:
out += ": "
if isfunction(a):
out += a()
else:
if isinstance(a, str):
out = out + a
else:
out = out + pf(a, width=160)
fun(out)
# Get the module logger. You can override log levels at the
# module level
def getLogger(name, level=None):
gnlogger = GNLogger(name)
logger = gnlogger.logger
if level:
logger.setLevel(level)
else:
logger.setLevel(LOG_LEVEL)
logger.info("Log level of " + name + " set to " + \
logging.getLevelName(logger.getEffectiveLevel()))
return gnlogger