blob: 4e74394ac870607b72d4bcc096e103427a681567 [file] [log] [blame]
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001#!/usr/bin/env python
2#
3# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved.
4#
5# Permission to use, copy, modify, and distribute this software and its
6# documentation for any purpose and without fee is hereby granted,
7# provided that the above copyright notice appear in all copies and that
8# both that copyright notice and this permission notice appear in
9# supporting documentation, and that the name of Vinay Sajip
10# not be used in advertising or publicity pertaining to distribution
11# of the software without specific, written prior permission.
12# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
13# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
14# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
15# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
16# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
17# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18#
19# This file is part of the Python logging distribution. See
20# http://www.red-dove.com/python_logging.html
21#
22"""Test harness for the logging module. Run all tests.
23
24Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved.
25"""
26
27from select import select
28import os, sys, string, struct, types, cPickle, cStringIO
29import socket, threading, time, locale
30import logging, logging.handlers, logging.config
31
Neal Norwitz502b9e12003-01-05 18:15:23 +000032try:
33 locale.setlocale(locale.LC_ALL, '')
Jack Jansen88340092003-01-15 23:43:02 +000034except (ValueError, locale.Error):
Neal Norwitz502b9e12003-01-05 18:15:23 +000035 # this happens on a Solaris box which only supports "C" locale
Jack Jansen88340092003-01-15 23:43:02 +000036 # or a Mac OS X box which supports very little locale stuff at all
Neal Norwitz502b9e12003-01-05 18:15:23 +000037 pass
Neal Norwitzb4a2df02003-01-02 14:56:39 +000038
39BANNER = "-- %-10s %-6s ---------------------------------------------------\n"
40
41#----------------------------------------------------------------------------
42# Log receiver
43#----------------------------------------------------------------------------
44
45TIMEOUT = 10
46
47from SocketServer import ThreadingTCPServer, StreamRequestHandler
48
49class LogRecordStreamHandler(StreamRequestHandler):
50 """
51 Handler for a streaming logging request. It basically logs the record
52 using whatever logging policy is configured locally.
53 """
54
55 def handle(self):
56 """
57 Handle multiple requests - each expected to be a 4-byte length,
58 followed by the LogRecord in pickle format. Logs the record
59 according to whatever policy is configured locally.
60 """
61 while 1:
62 try:
63 chunk = self.connection.recv(4)
64 if len(chunk) < 4:
65 break
66 slen = struct.unpack(">L", chunk)[0]
67 #print slen
68 chunk = self.connection.recv(slen)
69 while len(chunk) < slen:
70 chunk = chunk + self.connection.recv(slen - len(chunk))
71 obj = self.unPickle(chunk)
72 record = logging.LogRecord(None, None, "", 0, "", (), None)
73 record.__dict__.update(obj)
74 self.handleLogRecord(record)
75 except:
76 raise
77
78 def unPickle(self, data):
79 return cPickle.loads(data)
80
81 def handleLogRecord(self, record):
82 logname = "logrecv.tcp." + record.name
83 record.msg = record.msg + " (via " + logname + ")"
84 logger = logging.getLogger(logname)
85 logger.handle(record)
86
87class LogRecordSocketReceiver(ThreadingTCPServer):
88 """
89 A simple-minded TCP socket-based logging receiver suitable for test
90 purposes.
91 """
92
93 allow_reuse_address = 1
94
95 def __init__(self, host='localhost',
96 port=logging.handlers.DEFAULT_TCP_LOGGING_PORT,
97 handler=LogRecordStreamHandler):
98 ThreadingTCPServer.__init__(self, (host, port), handler)
99 self.abort = 0
100 self.timeout = 1
101
102 def serve_until_stopped(self):
103 abort = 0
104 while not abort:
105 rd, wr, ex = select([self.socket.fileno()],
106 [], [],
107 self.timeout)
108 if rd:
109 self.handle_request()
110 abort = self.abort
111
112def runTCP(tcpserver):
113 tcpserver.serve_until_stopped()
114
115#----------------------------------------------------------------------------
116# Test 0
117#----------------------------------------------------------------------------
118
119msgcount = 0
120
121def nextmessage():
122 global msgcount
123 rv = "Message %d" % msgcount
124 msgcount = msgcount + 1
125 return rv
126
127def test0():
128 ERR = logging.getLogger("ERR")
129 ERR.setLevel(logging.ERROR)
130 INF = logging.getLogger("INF")
131 INF.setLevel(logging.INFO)
132 INF_ERR = logging.getLogger("INF.ERR")
133 INF_ERR.setLevel(logging.ERROR)
134 DEB = logging.getLogger("DEB")
135 DEB.setLevel(logging.DEBUG)
136
137 INF_UNDEF = logging.getLogger("INF.UNDEF")
138 INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
139 UNDEF = logging.getLogger("UNDEF")
140
141 GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
142 CHILD = logging.getLogger("INF.BADPARENT")
143
144 #These should log
145 ERR.log(logging.FATAL, nextmessage())
146 ERR.error(nextmessage())
147
148 INF.log(logging.FATAL, nextmessage())
149 INF.error(nextmessage())
150 INF.warn(nextmessage())
151 INF.info(nextmessage())
152
153 INF_UNDEF.log(logging.FATAL, nextmessage())
154 INF_UNDEF.error(nextmessage())
155 INF_UNDEF.warn (nextmessage())
156 INF_UNDEF.info (nextmessage())
157
158 INF_ERR.log(logging.FATAL, nextmessage())
159 INF_ERR.error(nextmessage())
160
161 INF_ERR_UNDEF.log(logging.FATAL, nextmessage())
162 INF_ERR_UNDEF.error(nextmessage())
163
164 DEB.log(logging.FATAL, nextmessage())
165 DEB.error(nextmessage())
166 DEB.warn (nextmessage())
167 DEB.info (nextmessage())
168 DEB.debug(nextmessage())
169
170 UNDEF.log(logging.FATAL, nextmessage())
171 UNDEF.error(nextmessage())
172 UNDEF.warn (nextmessage())
173 UNDEF.info (nextmessage())
174
175 GRANDCHILD.log(logging.FATAL, nextmessage())
176 CHILD.log(logging.FATAL, nextmessage())
177
178 #These should not log
179 ERR.warn(nextmessage())
180 ERR.info(nextmessage())
181 ERR.debug(nextmessage())
182
183 INF.debug(nextmessage())
184 INF_UNDEF.debug(nextmessage())
185
186 INF_ERR.warn(nextmessage())
187 INF_ERR.info(nextmessage())
188 INF_ERR.debug(nextmessage())
189 INF_ERR_UNDEF.warn(nextmessage())
190 INF_ERR_UNDEF.info(nextmessage())
191 INF_ERR_UNDEF.debug(nextmessage())
192
193 INF.info("Messages should bear numbers 0 through 24.")
194
195#----------------------------------------------------------------------------
196# Test 1
197#----------------------------------------------------------------------------
198
199#
200# First, we define our levels. There can be as many as you want - the only
201# limitations are that they should be integers, the lowest should be > 0 and
202# larger values mean less information being logged. If you need specific
203# level values which do not fit into these limitations, you can use a
204# mapping dictionary to convert between your application levels and the
205# logging system.
206#
207SILENT = 10
208TACITURN = 9
209TERSE = 8
210EFFUSIVE = 7
211SOCIABLE = 6
212VERBOSE = 5
213TALKATIVE = 4
214GARRULOUS = 3
215CHATTERBOX = 2
216BORING = 1
217
218LEVEL_RANGE = range(BORING, SILENT + 1)
219
220#
221# Next, we define names for our levels. You don't need to do this - in which
222# case the system will use "Level n" to denote the text for the level.
223#
224my_logging_levels = {
225 SILENT : 'Silent',
226 TACITURN : 'Taciturn',
227 TERSE : 'Terse',
228 EFFUSIVE : 'Effusive',
229 SOCIABLE : 'Sociable',
230 VERBOSE : 'Verbose',
231 TALKATIVE : 'Talkative',
232 GARRULOUS : 'Garrulous',
233 CHATTERBOX : 'Chatterbox',
234 BORING : 'Boring',
235}
236
237#
238# Now, to demonstrate filtering: suppose for some perverse reason we only
239# want to print out all except GARRULOUS messages. Let's create a filter for
240# this purpose...
241#
242class SpecificLevelFilter(logging.Filter):
243 def __init__(self, lvl):
244 self.level = lvl
245
246 def filter(self, record):
247 return self.level != record.levelno
248
249class GarrulousFilter(SpecificLevelFilter):
250 def __init__(self):
251 SpecificLevelFilter.__init__(self, GARRULOUS)
252
253#
254# Now, let's demonstrate filtering at the logger. This time, use a filter
255# which excludes SOCIABLE and TACITURN messages. Note that GARRULOUS events
256# are still excluded.
257#
258class VerySpecificFilter(logging.Filter):
259 def filter(self, record):
260 return record.levelno not in [SOCIABLE, TACITURN]
261
262def message(s):
263 sys.stdout.write("%s\n" % s)
264
265SHOULD1 = "This should only be seen at the '%s' logging level (or lower)"
266
267def test1():
268#
269# Now, tell the logging system to associate names with our levels.
270#
271 for lvl in my_logging_levels.keys():
272 logging.addLevelName(lvl, my_logging_levels[lvl])
273
274#
275# Now, define a test function which logs an event at each of our levels.
276#
277
278 def doLog(log):
279 for lvl in LEVEL_RANGE:
280 log.log(lvl, SHOULD1, logging.getLevelName(lvl))
281
282 log = logging.getLogger("")
283 hdlr = log.handlers[0]
284#
285# Set the logging level to each different value and call the utility
286# function to log events.
287# In the output, you should see that each time round the loop, the number of
288# logging events which are actually output decreases.
289#
290 for lvl in LEVEL_RANGE:
291 message("-- setting logging level to '%s' -----" %
292 logging.getLevelName(lvl))
293 log.setLevel(lvl)
294 doLog(log)
295 #
296 # Now, we demonstrate level filtering at the handler level. Tell the
297 # handler defined above to filter at level 'SOCIABLE', and repeat the
298 # above loop. Compare the output from the two runs.
299 #
300 hdlr.setLevel(SOCIABLE)
301 message("-- Filtering at handler level to SOCIABLE --")
302 for lvl in LEVEL_RANGE:
303 message("-- setting logging level to '%s' -----" %
304 logging.getLevelName(lvl))
305 log.setLevel(lvl)
306 doLog(log)
307
308 hdlr.setLevel(0) #turn off level filtering at the handler
309
310 garr = GarrulousFilter()
311 hdlr.addFilter(garr)
312 message("-- Filtering using GARRULOUS filter --")
313 for lvl in LEVEL_RANGE:
314 message("-- setting logging level to '%s' -----" %
315 logging.getLevelName(lvl))
316 log.setLevel(lvl)
317 doLog(log)
318 spec = VerySpecificFilter()
319 log.addFilter(spec)
320 message("-- Filtering using specific filter for SOCIABLE, TACITURN --")
321 for lvl in LEVEL_RANGE:
322 message("-- setting logging level to '%s' -----" %
323 logging.getLevelName(lvl))
324 log.setLevel(lvl)
325 doLog(log)
326
327 log.removeFilter(spec)
328 hdlr.removeFilter(garr)
329 #Undo the one level which clashes...for regression tests
330 logging.addLevelName(logging.DEBUG, "DEBUG")
331
332#----------------------------------------------------------------------------
333# Test 2
334#----------------------------------------------------------------------------
335
336MSG = "-- logging %d at INFO, messages should be seen every 10 events --"
337def test2():
338 logger = logging.getLogger("")
339 sh = logger.handlers[0]
340 logger.removeHandler(sh)
341 mh = logging.handlers.MemoryHandler(10,logging.WARN, sh)
342 logger.setLevel(logging.DEBUG)
343 logger.addHandler(mh)
344 message("-- logging at DEBUG, nothing should be seen yet --")
345 logger.debug("Debug message")
346 message("-- logging at INFO, nothing should be seen yet --")
347 logger.info("Info message")
348 message("-- logging at WARN, 3 messages should be seen --")
349 logger.warn("Warn message")
350 for i in xrange(102):
351 message(MSG % i)
352 logger.info("Info index = %d", i)
353 mh.close()
354 logger.removeHandler(mh)
355 logger.addHandler(sh)
356
357#----------------------------------------------------------------------------
358# Test 3
359#----------------------------------------------------------------------------
360
361FILTER = "a.b"
362
363def doLog3():
364 logging.getLogger("a").info("Info 1")
365 logging.getLogger("a.b").info("Info 2")
366 logging.getLogger("a.c").info("Info 3")
367 logging.getLogger("a.b.c").info("Info 4")
368 logging.getLogger("a.b.c.d").info("Info 5")
369 logging.getLogger("a.bb.c").info("Info 6")
370 logging.getLogger("b").info("Info 7")
371 logging.getLogger("b.a").info("Info 8")
372 logging.getLogger("c.a.b").info("Info 9")
373 logging.getLogger("a.bb").info("Info 10")
374
375def test3():
376 root = logging.getLogger()
377 root.setLevel(logging.DEBUG)
378 hand = root.handlers[0]
379 message("Unfiltered...")
380 doLog3()
381 message("Filtered with '%s'..." % FILTER)
382 filt = logging.Filter(FILTER)
383 hand.addFilter(filt)
384 doLog3()
385 hand.removeFilter(filt)
386
387#----------------------------------------------------------------------------
388# Test Harness
389#----------------------------------------------------------------------------
390def banner(nm, typ):
391 sep = BANNER % (nm, typ)
392 sys.stdout.write(sep)
393 sys.stdout.flush()
394
395def test_main():
396 rootLogger = logging.getLogger("")
397 rootLogger.setLevel(logging.DEBUG)
398 hdlr = logging.StreamHandler(sys.stdout)
399 fmt = logging.Formatter(logging.BASIC_FORMAT)
400 hdlr.setFormatter(fmt)
401 rootLogger.addHandler(hdlr)
402
403 #Set up a handler such that all events are sent via a socket to the log
404 #receiver (logrecv).
405 #The handler will only be added to the rootLogger for some of the tests
406 hdlr = logging.handlers.SocketHandler('localhost',
407 logging.handlers.DEFAULT_TCP_LOGGING_PORT)
408
409 #Configure the logger for logrecv so events do not propagate beyond it.
410 #The sockLogger output is buffered in memory until the end of the test,
411 #and printed at the end.
412 sockOut = cStringIO.StringIO()
413 sockLogger = logging.getLogger("logrecv")
414 sockLogger.setLevel(logging.DEBUG)
415 sockhdlr = logging.StreamHandler(sockOut)
416 sockhdlr.setFormatter(logging.Formatter(
417 "%(name)s -> %(levelname)s: %(message)s"))
418 sockLogger.addHandler(sockhdlr)
419 sockLogger.propagate = 0
420
421 #Set up servers
422 threads = []
423 tcpserver = LogRecordSocketReceiver()
424 sys.stdout.write("About to start TCP server...\n")
425 threads.append(threading.Thread(target=runTCP, args=(tcpserver,)))
426
427 for thread in threads:
428 thread.start()
429 try:
430 banner("log_test0", "begin")
431
432 rootLogger.addHandler(hdlr)
433 test0()
434 rootLogger.removeHandler(hdlr)
435
436 banner("log_test0", "end")
437
438 banner("log_test1", "begin")
439 test1()
440 banner("log_test1", "end")
441
442 banner("log_test2", "begin")
443 test2()
444 banner("log_test2", "end")
445
446 banner("log_test3", "begin")
447 test3()
448 banner("log_test3", "end")
449
450 banner("logrecv output", "begin")
451 sys.stdout.write(sockOut.getvalue())
452 sockOut.close()
453 banner("logrecv output", "end")
454
455 finally:
456 #shut down server
457 tcpserver.abort = 1
458 for thread in threads:
459 thread.join()
460
461if __name__ == "__main__":
462 sys.stdout.write("test_logging\n")
463 test_main()
464 sys.stdout.flush()