logging: local functions ==> loss of lineno

Hellmut Weber mail at hellmutweber.de
Tue Mar 30 17:49:24 EDT 2010


Hi Peter and Jean-Michel,
thanks for all your hints and pieces of code.

It took me a while to play around with what I got from you (and some 
study of Vinay's module documentation.

Now I have come up with a more complete solution I'm quite satisfied 
with. And I would be very glad to receive your comments on my code 
indicating what I could do better, given that I'm still at the very 
beginning with python.


Cheers + TIA

Hellmut


Here is my module and a corresponding test program:

leo at sylvester hw_logg $ cat loc_logg.py 

#!/bin/env python 

# -*- coding: utf-8 -*- 

"""Hw's local logging module 


To be done:
   Factorize 'trace' decorator to own module
     (to permit dependency on debug level) ???
"""

import sys # Basic system functions

import logging # Logging base
import logging.handlers

import inspect # monkey patch caller filename

# --- color change strings for terminal output ---
#
pc_yell = '\033[01;93m'
pc_purp = '\033[01;33m'
pc_gren = '\033[01;92m'
pc_blue = '\033[01;94m'
#
pc_bold = '\033[01;1m'
pc_norm = '\033[0m'
#
pc_reed = '\033[01;91m'
pc_cyan = '\033[01;96m'

level_color = {
   'fatal': pc_yell,
   'error': pc_yell,
   'warning': pc_purp,
   'info': pc_gren,
   'debug': pc_blue,
   'bold': pc_bold,
   'normal': pc_norm
   }

# --- define string building help function ---
#
def argskw_2_string(*args, **kwargs):
   """Generate String from *args and **kwargs

   Return string with
     *args list (comma separated) in first line
     **kwargs key=val list (comma separated) in second line
   """
   _str_lines = []

   if args:
     _args_strings = ''
     for a in args:
       _args_strings += str(a) + ', '
     _str_lines.append(_args_strings)

   if kwargs:
     _kwargs_strings = ''
     for k in kwargs:
       _kwargs_strings += str(k)+' = '+str(kwargs[k]) + ', '
     _str_lines.append(_kwargs_strings)
   return ''.join(_str_lines)


# --- Class for all logging functionality ---
#
class locLogg():
   _logger = None

   def __init__(self, newlogg=None):
     if not locLogg._logger:
       locLogg._logger = newlogg

   def init_logging(self,
     loclogg_output='line', # stream or/and file
     loclogg_fname='loggfile.txt',
     loclogg_dblevel='DEBUG'
     ):
     """Initiate logging with locLogg._logger

     Defines the logging handler to be used
     and initiates it.

     Possible values for loclogg_output:
       line ==> output to console
       file ==> log_file must be given
       both ==> log_file must be given
       null ==> no logging (NullHandler)
     """
     locLogg._logger.handlers=[]
     if loclogg_output in ('null'):
       class NullHandler(logging.Handler):
         def emit(self, record):
           pass
       locLogg._logger.addHandler(NullHandler())
     if loclogg_output in ('line', 'both'):
       locLogg._logger.addHandler(logging.StreamHandler())
     if loclogg_output in ('file', 'both'):
       locLogg._logger.addHandler(
           logging.handlers.RotatingFileHandler(loclogg_fname,
                maxBytes=200000, backupCount=5)
           )
     for _loc_logger in locLogg._logger.handlers:
       _loc_logger.setFormatter(logging.Formatter(
           '%(asctime)s ' + \
           '%(custom_filename)s <%(custom_lineno)d>:' + \
           '%(levelname)s %(message)s'))
     # Can this be done better ?
     _loc_levels = {'NOTSET': 0, 'DEBUG': 10,
         'INFO': 20, 'WARNING': 30, 'ERROR': 40, 'CRITICAL': 50}
     locLogg._logger.setLevel(_loc_levels[loclogg_dblevel])

   def info_log(self, msg, *args, **kwargs):
     previousFrame = inspect.currentframe().f_back
     locLogg._logger.info(str(level_color['info'])+msg+pc_norm + \
       ' ' + argskw_2_string(*args, **kwargs),
       extra={'custom_lineno': previousFrame.f_lineno,
         'custom_filename': previousFrame.f_code.co_filename })

   def debug_log(self, msg, *args, **kwargs):
     previousFrame = inspect.currentframe().f_back
     locLogg._logger.debug(str(level_color['debug'])+msg+pc_norm + \
       ' ' + argskw_2_string(*args, **kwargs),
       extra={'custom_lineno':previousFrame.f_lineno,
         'custom_filename': previousFrame.f_code.co_filename })

   def warn_log(self, msg, *args, **kwargs):
     previousFrame = inspect.currentframe().f_back
     locLogg._logger.debug(str(level_color['warning'])+msg+pc_norm + \
       ' ' + argskw_2_string(*args, **kwargs),
       extra={'custom_lineno':previousFrame.f_lineno,
         'custom_filename': previousFrame.f_code.co_filename })

   def error_log(self, msg, *args, **kwargs):
     previousFrame = inspect.currentframe().f_back
     locLogg._logger.error(str(level_color['error'])+msg+pc_norm + \
       ' ' + argskw_2_string(*args, **kwargs),
       extra={'custom_lineno':previousFrame.f_lineno,
         'custom_filename': previousFrame.f_code.co_filename })

   def fatal_log(self, msg, *args, **kwargs):
     previousFrame = inspect.currentframe().f_back
     locLogg._logger.fatal(str(level_color['fatal'])+msg+pc_norm + \
       ' ' + argskw_2_string(*args, **kwargs),
       extra={'custom_lineno':previousFrame.f_lineno,
         'custom_filename': previousFrame.f_code.co_filename })

# --- Permit definition of a tracing decorator ---
from decorator import decorator

@decorator
def trace(f, *args, **kw):
   print "@trace: calling %s with args %s, %s" % (f.func_name, args, kw)
   return f(*args, **kw)
# cf. doc of Michele Simoniato's decorator module


if __name__ == '__main__':

   _logger = logging.getLogger('jmp_logg__main__')
   foo = locLogg(_logger)
   foo.init_logging()

   foo.info_log('a foo info')
   foo.info_log('another bar info', 1,2,3, a=11, b=22, c=44)
   foo.debug_log('a debug bar info', 'a', '1aA', qwe=2, asd=99)
   foo.warn_log('a test info', 'ggg', '2U2', yxcv=2, asdf=99)

   try:
     b = 123
     c = 0
     a = b / c
   except:
     foo.fatal_log('Division by zero', b=123, c=0)

leo at sylvester hw_logg $ cat test_loclogg.py
#!/bin/env python
# -*- coding: utf-8 -*-

import logging
_logger = logging.getLogger()

import sys
pyDevelDir = '/home/leo/leo/brbeit/py-devel/Modules'
sys.path.append(pyDevelDir)

from hw_logg.loc_logg import locLogg
foo = locLogg(_logger)
foo.init_logging(
     loclogg_output='both',
     loclogg_dblevel='DEBUG')

import loc_module as jm

foo.info_log('First info with args in string: %d %d %d' % (1,2,3))
foo.warn_log('First warning')
foo.debug_log('First debug message TestTestTest', '----------', 4,5,6, 
12*25, d=34, e='qwe')

foo.debug_log('Before calling jm.doIt()')
jm.doIt(True)

foo.info_log('Second info with kwargs separate:', a=11,b=22,c=33)

x = jm.MyClass(1, 2, 'abc')
x.mymethod()
x.mymethod(123, 234, a=1, b=2, c=3)

try:
   jm.doIt(0)
except Exception, e:
   foo.error_log(str(e), 1,5,8)
   foo.fatal_log('Fatal message')

leo at sylvester hw_logg $ cat loc_module.py
# -*- coding: utf-8 -*-

import logging
_logger = logging.getLogger('jmp_logger')

import sys
pyDevelDir = '/home/leo/leo/brbeit/py-devel/Modules'
sys.path.append(pyDevelDir)

from hw_logg.loc_logg import locLogg, trace
foo = locLogg(_logger)

foo.info_log("Start importing "+__name__)

def doIt(yn=None):
     foo.debug_log("doin' stuff, yn =", str(yn)) # logLevel at calling 
point !
     print '=====> Output from doIt:', yn
     #do stuff...but suppose an error occurs?
     if yn:
       foo.info_log('yn: good value')
     else:
       raise TypeError, "bogus type error for testing"

class MyClass(object):
   @trace
   def __init__(self, *args, **kwargs):
     print 'MyClass.__init__'
   @trace
   def mymethod(self, *args, **kwargs):
     print 'MyClass.mymethod'

foo.info_log("End   importing "+__name__)

-- 
Dr. Hellmut Weber         mail at hellmutweber.de
Degenfeldstraße 2         tel   +49-89-3081172
D-80803 München-Schwabing mobil +49-172-8450321
please: No DOCs, no PPTs. why: tinyurl.com/cbgq




More information about the Python-list mailing list