Package Gnumed :: Package pycommon :: Module gmLog2
[frames] | no frames]

Source Code for Module Gnumed.pycommon.gmLog2

  1  """GNUmed logging framework setup. 
  2   
  3  All error logging, user notification and otherwise unhandled  
  4  exception handling should go through classes or functions of  
  5  this module. 
  6   
  7  Theory of operation: 
  8   
  9  This module tailors the standard logging framework to 
 10  the needs of GNUmed. 
 11   
 12  By importing gmLog2 into your code you'll get the root 
 13  logger send to a unicode file with messages in a format useful 
 14  for debugging. The filename is either taken from the 
 15  command line (--log-file=...) or derived from the name 
 16  of the main application. 
 17   
 18  The log file will be found in one of the following standard 
 19  locations: 
 20   
 21  1) given on the command line as "--log-file=LOGFILE" 
 22  2) ~/.<base_name>/<base_name>.log 
 23  3) /dir/of/binary/<base_name>.log               (mainly for DOS/Windows) 
 24   
 25  where <base_name> is derived from the name 
 26  of the main application. 
 27   
 28  If you want to specify just a directory for the log file you 
 29  must end the --log-file definition with a slash. 
 30   
 31  By importing "logging" and getting a logger your modules 
 32  never need to worry about the real message destination or whether 
 33  at any given time there's a valid logger available. 
 34   
 35  Your MAIN module simply imports gmLog2 and all other modules 
 36  will merrily and automagically start logging away. 
 37  """ 
 38  # TODO: 
 39  # - exception() 
 40  # - ascii_ctrl2mnemonic() 
 41  #======================================================================== 
 42  __version__ = "$Revision: 1.13 $" 
 43  __author__  = "K. Hilbert <Karsten.Hilbert@gmx.net>" 
 44  __license__ = "GPL v2 or later (details at http://www.gnu.org)" 
 45   
 46   
 47  # stdlib 
 48  import logging, sys, os, codecs, locale 
 49   
 50   
 51  _logfile_name = None 
 52  _logfile = None 
 53   
 54  _string_encoding = None 
 55   
 56  # table used for cooking non-printables 
 57  AsciiName = ['<#0-0x00-nul>', 
 58                           '<#1-0x01-soh>', 
 59                           '<#2-0x02-stx>', 
 60                           '<#3-0x03-etx>', 
 61                           '<#4-0x04-eot>', 
 62                           '<#5-0x05-enq>', 
 63                           '<#6-0x06-ack>', 
 64                           '<#7-0x07-bel>', 
 65                           '<#8-0x08-bs>', 
 66                           '<#9-0x09-ht>', 
 67                           '<#10-0x0A-lf>', 
 68                           '<#11-0x0B-vt>', 
 69                           '<#12-0x0C-ff>', 
 70                           '<#13-0x0D-cr>', 
 71                           '<#14-0x0E-so>', 
 72                           '<#15-0x0F-si>', 
 73                           '<#16-0x10-dle>', 
 74                           '<#17-0x11-dc1/xon>', 
 75                           '<#18-0x12-dc2>', 
 76                           '<#19-0x13-dc3/xoff>', 
 77                           '<#20-0x14-dc4>', 
 78                           '<#21-0x15-nak>', 
 79                           '<#22-0x16-syn>', 
 80                           '<#23-0x17-etb>', 
 81                           '<#24-0x18-can>', 
 82                           '<#25-0x19-em>', 
 83                           '<#26-0x1A-sub>', 
 84                           '<#27-0x1B-esc>', 
 85                           '<#28-0x1C-fs>', 
 86                           '<#29-0x1D-gs>', 
 87                           '<#30-0x1E-rs>', 
 88                           '<#31-0x1F-us>' 
 89                          ] 
 90   
 91  # msg = reduce(lambda x, y: x+y, (map(self.__char2AsciiName, list(tmp))), '') 
 92  # 
 93  #       def __char2AsciiName(self, aChar): 
 94  #               try: 
 95  #                       return AsciiName[ord(aChar)] 
 96  #               except IndexError: 
 97  #                       return aChar 
 98  # 
 99  #       def __tracestack(self): 
100  #               """extract data from the current execution stack 
101  # 
102  #               this is rather fragile, I guess 
103  #               """ 
104  #               stack = traceback.extract_stack() 
105  #               self.__modulename = stack[-4][0] 
106  #               self.__linenumber = stack[-4][1] 
107  #               self.__functionname = stack[-4][2] 
108  #               if (self.__functionname == "?"): 
109  #                       self.__functionname = "Main" 
110   
111  #=============================================================== 
112  # external API 
113  #=============================================================== 
114 -def flush():
115 logger = logging.getLogger('gm.logging') 116 logger.critical(u'-------- synced log file -------------------------------') 117 root_logger = logging.getLogger() 118 for handler in root_logger.handlers: 119 handler.flush()
120 #===============================================================
121 -def log_stack_trace(message=None):
122 123 logger = logging.getLogger('gm.logging') 124 125 tb = sys.exc_info()[2] 126 if tb is None: 127 try: 128 tb = sys.last_traceback 129 except AttributeError: 130 logger.debug(u'no stack to trace') 131 return 132 133 # recurse back to root caller 134 while 1: 135 if not tb.tb_next: 136 break 137 tb = tb.tb_next 138 # and put the frames on a stack 139 stack_of_frames = [] 140 frame = tb.tb_frame 141 while frame: 142 stack_of_frames.append(frame) 143 frame = frame.f_back 144 stack_of_frames.reverse() 145 146 if message is not None: 147 logger.debug(message) 148 logger.debug(u'stack trace follows:') 149 logger.debug(u'(locals by frame, outmost frame first)') 150 for frame in stack_of_frames: 151 logger.debug ( 152 u'>>> execution frame [%s] in [%s] at line %s <<<', 153 frame.f_code.co_name, 154 frame.f_code.co_filename, 155 frame.f_lineno 156 ) 157 for varname, value in frame.f_locals.items(): 158 if varname == u'__doc__': 159 continue 160 161 try: 162 value = unicode(value, encoding = _string_encoding, errors = 'replace') 163 except TypeError: 164 try: 165 value = unicode(value) 166 except (UnicodeDecodeError, TypeError): 167 value = '%s' % str(value) 168 value = value.decode(_string_encoding, 'replace') 169 170 logger.debug(u'%20s = %s', varname, value)
171 #===============================================================
172 -def set_string_encoding(encoding=None):
173 174 logger = logging.getLogger('gm.logging') 175 176 global _string_encoding 177 178 if encoding is not None: 179 codecs.lookup(encoding) 180 _string_encoding = encoding 181 logger.info(u'setting python.str -> python.unicode encoding to <%s> (explicit)', _string_encoding) 182 return True 183 184 enc = sys.getdefaultencoding() 185 if enc != 'ascii': 186 _string_encoding = enc 187 logger.info(u'setting python.str -> python.unicode encoding to <%s> (sys.getdefaultencoding)', _string_encoding) 188 return True 189 190 enc = locale.getlocale()[1] 191 if enc is not None: 192 _string_encoding = enc 193 logger.info(u'setting python.str -> python.unicode encoding to <%s> (locale.getlocale)', _string_encoding) 194 return True 195 196 # FIXME: or rather use utf8 ? 197 _string_encoding = locale.getpreferredencoding(do_setlocale=False) 198 logger.info(u'setting python.str -> python.unicode encoding to <%s> (locale.getpreferredencoding)', _string_encoding) 199 return True
200 #=============================================================== 201 # internal API 202 #===============================================================
203 -def __setup_logging():
204 205 set_string_encoding() 206 207 global _logfile 208 if _logfile is not None: 209 return True 210 211 if not __get_logfile_name(): 212 return False 213 214 if sys.version[:3] < '2.5': 215 fmt = u'%(asctime)s %(levelname)-8s %(name)s (%(pathname)s @ #%(lineno)d): %(message)s' 216 else: 217 fmt = u'%(asctime)s %(levelname)-8s %(name)s (%(pathname)s::%(funcName)s() #%(lineno)d): %(message)s' 218 219 _logfile = codecs.open(filename = _logfile_name, mode = 'wb', encoding = 'utf8', errors = 'replace') 220 221 logging.basicConfig ( 222 format = fmt, 223 datefmt = '%Y-%m-%d %H:%M:%S', 224 level = logging.DEBUG, 225 stream = _logfile 226 ) 227 228 logger = logging.getLogger('gm.logging') 229 logger.critical(u'-------- start of logging ------------------------------') 230 logger.info(u'log file is <%s>', _logfile_name) 231 logger.info(u'log level is [%s]', logging.getLevelName(logger.getEffectiveLevel())) 232 logger.info(u'log file encoding is <utf8>') 233 logger.info(u'initial python.str -> python.unicode encoding is <%s>', _string_encoding)
234 #---------------------------------------------------------------
235 -def __get_logfile_name():
236 237 global _logfile_name 238 if _logfile_name is not None: 239 return _logfile_name 240 241 def_log_basename = os.path.splitext(os.path.basename(sys.argv[0]))[0] 242 def_log_name = '%s-%s.log' % (def_log_basename, os.getpid()) 243 244 # given on command line ? 245 for option in sys.argv[1:]: 246 if option.startswith('--log-file='): 247 (name,value) = option.split('=') 248 (dir, name) = os.path.split(value) 249 if dir == '': 250 dir = '.' 251 if name == '': 252 name = def_log_name 253 _logfile_name = os.path.abspath(os.path.expanduser(os.path.join(dir, name))) 254 return True 255 256 # else store it in ~/.def_log_basename/def_log_name 257 dir = os.path.expanduser(os.path.join('~', '.' + def_log_basename)) 258 try: 259 os.makedirs(dir) 260 except OSError, e: 261 if (e.errno == 17) and not os.path.isdir(dir): 262 raise 263 264 _logfile_name = os.path.join(dir, def_log_name) 265 266 return True
267 #=============================================================== 268 # main 269 #--------------------------------------------------------------- 270 __setup_logging() 271 272 if __name__ == '__main__': 273 274 #-----------------------------------------------------------
275 - def test():
276 logger = logging.getLogger('gmLog2.test') 277 logger.error("I expected to see %s::test()" % __file__) 278 try: 279 int(None) 280 except: 281 logger.exception(u'unhandled exception') 282 log_stack_trace() 283 flush()
284 #----------------------------------------------------------- 285 if len(sys.argv) > 1 and sys.argv[1] == u'test': 286 test() 287 #=============================================================== 288