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