blob: 9c47408b23ae6d5e0e537dbd38107f8768abcb40 [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
Guido van Rossum2a1d5162003-01-21 21:05:22 +000027import select
Neal Norwitzb4a2df02003-01-02 14:56:39 +000028import 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]
Neal Norwitzb4a2df02003-01-02 14:56:39 +000067 chunk = self.connection.recv(slen)
68 while len(chunk) < slen:
69 chunk = chunk + self.connection.recv(slen - len(chunk))
70 obj = self.unPickle(chunk)
71 record = logging.LogRecord(None, None, "", 0, "", (), None)
72 record.__dict__.update(obj)
73 self.handleLogRecord(record)
74 except:
75 raise
76
77 def unPickle(self, data):
78 return cPickle.loads(data)
79
80 def handleLogRecord(self, record):
81 logname = "logrecv.tcp." + record.name
82 record.msg = record.msg + " (via " + logname + ")"
83 logger = logging.getLogger(logname)
84 logger.handle(record)
85
86class LogRecordSocketReceiver(ThreadingTCPServer):
87 """
88 A simple-minded TCP socket-based logging receiver suitable for test
89 purposes.
90 """
91
92 allow_reuse_address = 1
93
94 def __init__(self, host='localhost',
95 port=logging.handlers.DEFAULT_TCP_LOGGING_PORT,
96 handler=LogRecordStreamHandler):
97 ThreadingTCPServer.__init__(self, (host, port), handler)
98 self.abort = 0
99 self.timeout = 1
100
101 def serve_until_stopped(self):
102 abort = 0
103 while not abort:
Guido van Rossum2a1d5162003-01-21 21:05:22 +0000104 rd, wr, ex = select.select([self.socket.fileno()],
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000105 [], [],
106 self.timeout)
107 if rd:
108 self.handle_request()
109 abort = self.abort
110
Guido van Rossum2a1d5162003-01-21 21:05:22 +0000111 def process_request(self, request, client_address):
112 #import threading
113 t = threading.Thread(target = self.finish_request,
114 args = (request, client_address))
115 t.start()
116
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000117def runTCP(tcpserver):
118 tcpserver.serve_until_stopped()
119
120#----------------------------------------------------------------------------
121# Test 0
122#----------------------------------------------------------------------------
123
124msgcount = 0
125
126def nextmessage():
127 global msgcount
128 rv = "Message %d" % msgcount
129 msgcount = msgcount + 1
130 return rv
131
132def test0():
133 ERR = logging.getLogger("ERR")
134 ERR.setLevel(logging.ERROR)
135 INF = logging.getLogger("INF")
136 INF.setLevel(logging.INFO)
137 INF_ERR = logging.getLogger("INF.ERR")
138 INF_ERR.setLevel(logging.ERROR)
139 DEB = logging.getLogger("DEB")
140 DEB.setLevel(logging.DEBUG)
141
142 INF_UNDEF = logging.getLogger("INF.UNDEF")
143 INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
144 UNDEF = logging.getLogger("UNDEF")
145
146 GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
147 CHILD = logging.getLogger("INF.BADPARENT")
148
149 #These should log
150 ERR.log(logging.FATAL, nextmessage())
151 ERR.error(nextmessage())
152
153 INF.log(logging.FATAL, nextmessage())
154 INF.error(nextmessage())
155 INF.warn(nextmessage())
156 INF.info(nextmessage())
157
158 INF_UNDEF.log(logging.FATAL, nextmessage())
159 INF_UNDEF.error(nextmessage())
160 INF_UNDEF.warn (nextmessage())
161 INF_UNDEF.info (nextmessage())
162
163 INF_ERR.log(logging.FATAL, nextmessage())
164 INF_ERR.error(nextmessage())
165
166 INF_ERR_UNDEF.log(logging.FATAL, nextmessage())
167 INF_ERR_UNDEF.error(nextmessage())
168
169 DEB.log(logging.FATAL, nextmessage())
170 DEB.error(nextmessage())
171 DEB.warn (nextmessage())
172 DEB.info (nextmessage())
173 DEB.debug(nextmessage())
174
175 UNDEF.log(logging.FATAL, nextmessage())
176 UNDEF.error(nextmessage())
177 UNDEF.warn (nextmessage())
178 UNDEF.info (nextmessage())
179
180 GRANDCHILD.log(logging.FATAL, nextmessage())
181 CHILD.log(logging.FATAL, nextmessage())
182
183 #These should not log
184 ERR.warn(nextmessage())
185 ERR.info(nextmessage())
186 ERR.debug(nextmessage())
187
188 INF.debug(nextmessage())
189 INF_UNDEF.debug(nextmessage())
190
191 INF_ERR.warn(nextmessage())
192 INF_ERR.info(nextmessage())
193 INF_ERR.debug(nextmessage())
194 INF_ERR_UNDEF.warn(nextmessage())
195 INF_ERR_UNDEF.info(nextmessage())
196 INF_ERR_UNDEF.debug(nextmessage())
197
198 INF.info("Messages should bear numbers 0 through 24.")
199
200#----------------------------------------------------------------------------
201# Test 1
202#----------------------------------------------------------------------------
203
204#
205# First, we define our levels. There can be as many as you want - the only
206# limitations are that they should be integers, the lowest should be > 0 and
207# larger values mean less information being logged. If you need specific
208# level values which do not fit into these limitations, you can use a
209# mapping dictionary to convert between your application levels and the
210# logging system.
211#
212SILENT = 10
213TACITURN = 9
214TERSE = 8
215EFFUSIVE = 7
216SOCIABLE = 6
217VERBOSE = 5
218TALKATIVE = 4
219GARRULOUS = 3
220CHATTERBOX = 2
221BORING = 1
222
223LEVEL_RANGE = range(BORING, SILENT + 1)
224
225#
226# Next, we define names for our levels. You don't need to do this - in which
227# case the system will use "Level n" to denote the text for the level.
228#
229my_logging_levels = {
230 SILENT : 'Silent',
231 TACITURN : 'Taciturn',
232 TERSE : 'Terse',
233 EFFUSIVE : 'Effusive',
234 SOCIABLE : 'Sociable',
235 VERBOSE : 'Verbose',
236 TALKATIVE : 'Talkative',
237 GARRULOUS : 'Garrulous',
238 CHATTERBOX : 'Chatterbox',
239 BORING : 'Boring',
240}
241
242#
243# Now, to demonstrate filtering: suppose for some perverse reason we only
244# want to print out all except GARRULOUS messages. Let's create a filter for
245# this purpose...
246#
247class SpecificLevelFilter(logging.Filter):
248 def __init__(self, lvl):
249 self.level = lvl
250
251 def filter(self, record):
252 return self.level != record.levelno
253
254class GarrulousFilter(SpecificLevelFilter):
255 def __init__(self):
256 SpecificLevelFilter.__init__(self, GARRULOUS)
257
258#
259# Now, let's demonstrate filtering at the logger. This time, use a filter
260# which excludes SOCIABLE and TACITURN messages. Note that GARRULOUS events
261# are still excluded.
262#
263class VerySpecificFilter(logging.Filter):
264 def filter(self, record):
265 return record.levelno not in [SOCIABLE, TACITURN]
266
267def message(s):
268 sys.stdout.write("%s\n" % s)
269
270SHOULD1 = "This should only be seen at the '%s' logging level (or lower)"
271
272def test1():
273#
274# Now, tell the logging system to associate names with our levels.
275#
276 for lvl in my_logging_levels.keys():
277 logging.addLevelName(lvl, my_logging_levels[lvl])
278
279#
280# Now, define a test function which logs an event at each of our levels.
281#
282
283 def doLog(log):
284 for lvl in LEVEL_RANGE:
285 log.log(lvl, SHOULD1, logging.getLevelName(lvl))
286
287 log = logging.getLogger("")
288 hdlr = log.handlers[0]
289#
290# Set the logging level to each different value and call the utility
291# function to log events.
292# In the output, you should see that each time round the loop, the number of
293# logging events which are actually output decreases.
294#
295 for lvl in LEVEL_RANGE:
296 message("-- setting logging level to '%s' -----" %
297 logging.getLevelName(lvl))
298 log.setLevel(lvl)
299 doLog(log)
300 #
301 # Now, we demonstrate level filtering at the handler level. Tell the
302 # handler defined above to filter at level 'SOCIABLE', and repeat the
303 # above loop. Compare the output from the two runs.
304 #
305 hdlr.setLevel(SOCIABLE)
306 message("-- Filtering at handler level to SOCIABLE --")
307 for lvl in LEVEL_RANGE:
308 message("-- setting logging level to '%s' -----" %
309 logging.getLevelName(lvl))
310 log.setLevel(lvl)
311 doLog(log)
312
313 hdlr.setLevel(0) #turn off level filtering at the handler
314
315 garr = GarrulousFilter()
316 hdlr.addFilter(garr)
317 message("-- Filtering using GARRULOUS filter --")
318 for lvl in LEVEL_RANGE:
319 message("-- setting logging level to '%s' -----" %
320 logging.getLevelName(lvl))
321 log.setLevel(lvl)
322 doLog(log)
323 spec = VerySpecificFilter()
324 log.addFilter(spec)
325 message("-- Filtering using specific filter for SOCIABLE, TACITURN --")
326 for lvl in LEVEL_RANGE:
327 message("-- setting logging level to '%s' -----" %
328 logging.getLevelName(lvl))
329 log.setLevel(lvl)
330 doLog(log)
331
332 log.removeFilter(spec)
333 hdlr.removeFilter(garr)
334 #Undo the one level which clashes...for regression tests
335 logging.addLevelName(logging.DEBUG, "DEBUG")
336
337#----------------------------------------------------------------------------
338# Test 2
339#----------------------------------------------------------------------------
340
341MSG = "-- logging %d at INFO, messages should be seen every 10 events --"
342def test2():
343 logger = logging.getLogger("")
344 sh = logger.handlers[0]
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000345 sh.close()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000346 logger.removeHandler(sh)
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000347 mh = logging.handlers.MemoryHandler(10,logging.WARNING, sh)
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000348 logger.setLevel(logging.DEBUG)
349 logger.addHandler(mh)
350 message("-- logging at DEBUG, nothing should be seen yet --")
351 logger.debug("Debug message")
352 message("-- logging at INFO, nothing should be seen yet --")
353 logger.info("Info message")
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000354 message("-- logging at WARNING, 3 messages should be seen --")
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000355 logger.warn("Warn message")
356 for i in xrange(102):
357 message(MSG % i)
358 logger.info("Info index = %d", i)
359 mh.close()
360 logger.removeHandler(mh)
361 logger.addHandler(sh)
362
363#----------------------------------------------------------------------------
364# Test 3
365#----------------------------------------------------------------------------
366
367FILTER = "a.b"
368
369def doLog3():
370 logging.getLogger("a").info("Info 1")
371 logging.getLogger("a.b").info("Info 2")
372 logging.getLogger("a.c").info("Info 3")
373 logging.getLogger("a.b.c").info("Info 4")
374 logging.getLogger("a.b.c.d").info("Info 5")
375 logging.getLogger("a.bb.c").info("Info 6")
376 logging.getLogger("b").info("Info 7")
377 logging.getLogger("b.a").info("Info 8")
378 logging.getLogger("c.a.b").info("Info 9")
379 logging.getLogger("a.bb").info("Info 10")
380
381def test3():
382 root = logging.getLogger()
383 root.setLevel(logging.DEBUG)
384 hand = root.handlers[0]
385 message("Unfiltered...")
386 doLog3()
387 message("Filtered with '%s'..." % FILTER)
388 filt = logging.Filter(FILTER)
389 hand.addFilter(filt)
390 doLog3()
391 hand.removeFilter(filt)
392
393#----------------------------------------------------------------------------
394# Test Harness
395#----------------------------------------------------------------------------
396def banner(nm, typ):
397 sep = BANNER % (nm, typ)
398 sys.stdout.write(sep)
399 sys.stdout.flush()
400
401def test_main():
402 rootLogger = logging.getLogger("")
403 rootLogger.setLevel(logging.DEBUG)
404 hdlr = logging.StreamHandler(sys.stdout)
405 fmt = logging.Formatter(logging.BASIC_FORMAT)
406 hdlr.setFormatter(fmt)
407 rootLogger.addHandler(hdlr)
408
409 #Set up a handler such that all events are sent via a socket to the log
410 #receiver (logrecv).
411 #The handler will only be added to the rootLogger for some of the tests
412 hdlr = logging.handlers.SocketHandler('localhost',
413 logging.handlers.DEFAULT_TCP_LOGGING_PORT)
414
415 #Configure the logger for logrecv so events do not propagate beyond it.
416 #The sockLogger output is buffered in memory until the end of the test,
417 #and printed at the end.
418 sockOut = cStringIO.StringIO()
419 sockLogger = logging.getLogger("logrecv")
420 sockLogger.setLevel(logging.DEBUG)
421 sockhdlr = logging.StreamHandler(sockOut)
422 sockhdlr.setFormatter(logging.Formatter(
423 "%(name)s -> %(levelname)s: %(message)s"))
424 sockLogger.addHandler(sockhdlr)
425 sockLogger.propagate = 0
426
427 #Set up servers
428 threads = []
429 tcpserver = LogRecordSocketReceiver()
Guido van Rossum2a1d5162003-01-21 21:05:22 +0000430 #sys.stdout.write("About to start TCP server...\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000431 threads.append(threading.Thread(target=runTCP, args=(tcpserver,)))
432
433 for thread in threads:
434 thread.start()
435 try:
436 banner("log_test0", "begin")
437
438 rootLogger.addHandler(hdlr)
439 test0()
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000440 hdlr.close()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000441 rootLogger.removeHandler(hdlr)
442
443 banner("log_test0", "end")
444
445 banner("log_test1", "begin")
446 test1()
447 banner("log_test1", "end")
448
449 banner("log_test2", "begin")
450 test2()
451 banner("log_test2", "end")
452
453 banner("log_test3", "begin")
454 test3()
455 banner("log_test3", "end")
456
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000457 finally:
458 #shut down server
459 tcpserver.abort = 1
460 for thread in threads:
461 thread.join()
Guido van Rossum2a1d5162003-01-21 21:05:22 +0000462 banner("logrecv output", "begin")
463 sys.stdout.write(sockOut.getvalue())
464 sockOut.close()
465 banner("logrecv output", "end")
466 sys.stdout.flush()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000467
468if __name__ == "__main__":
469 sys.stdout.write("test_logging\n")
470 test_main()