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