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 __author__ = "K. Hilbert <Karsten.Hilbert@gmx.net>"
43 __license__ = "GPL v2 or later (details at http://www.gnu.org)"
44
45
46
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
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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
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
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
137 while 1:
138 if not tb.tb_next:
139 break
140 tb = tb.tb_next
141
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
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
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
205
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
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
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
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
272
273 __setup_logging()
274
275 if __name__ == '__main__':
276
277
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