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