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 __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
48 import logging, sys, os, codecs, locale
49
50
51 _logfile_name = None
52 _logfile = None
53
54 _string_encoding = None
55
56
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
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
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
134 while 1:
135 if not tb.tb_next:
136 break
137 tb = tb.tb_next
138
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
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
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
202
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
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
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
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
269
270 __setup_logging()
271
272 if __name__ == '__main__':
273
274
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
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