blob: 113df5c3cdac11f588aa9f59e165c4e9b66c7b0b [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]
345 logger.removeHandler(sh)
346 mh = logging.handlers.MemoryHandler(10,logging.WARN, sh)
347 logger.setLevel(logging.DEBUG)
348 logger.addHandler(mh)
349 message("-- logging at DEBUG, nothing should be seen yet --")
350 logger.debug("Debug message")
351 message("-- logging at INFO, nothing should be seen yet --")
352 logger.info("Info message")
353 message("-- logging at WARN, 3 messages should be seen --")
354 logger.warn("Warn message")
355 for i in xrange(102):
356 message(MSG % i)
357 logger.info("Info index = %d", i)
358 mh.close()
359 logger.removeHandler(mh)
360 logger.addHandler(sh)
361
362#----------------------------------------------------------------------------
363# Test 3
364#----------------------------------------------------------------------------
365
366FILTER = "a.b"
367
368def doLog3():
369 logging.getLogger("a").info("Info 1")
370 logging.getLogger("a.b").info("Info 2")
371 logging.getLogger("a.c").info("Info 3")
372 logging.getLogger("a.b.c").info("Info 4")
373 logging.getLogger("a.b.c.d").info("Info 5")
374 logging.getLogger("a.bb.c").info("Info 6")
375 logging.getLogger("b").info("Info 7")
376 logging.getLogger("b.a").info("Info 8")
377 logging.getLogger("c.a.b").info("Info 9")
378 logging.getLogger("a.bb").info("Info 10")
379
380def test3():
381 root = logging.getLogger()
382 root.setLevel(logging.DEBUG)
383 hand = root.handlers[0]
384 message("Unfiltered...")
385 doLog3()
386 message("Filtered with '%s'..." % FILTER)
387 filt = logging.Filter(FILTER)
388 hand.addFilter(filt)
389 doLog3()
390 hand.removeFilter(filt)
391
392#----------------------------------------------------------------------------
393# Test Harness
394#----------------------------------------------------------------------------
395def banner(nm, typ):
396 sep = BANNER % (nm, typ)
397 sys.stdout.write(sep)
398 sys.stdout.flush()
399
400def test_main():
401 rootLogger = logging.getLogger("")
402 rootLogger.setLevel(logging.DEBUG)
403 hdlr = logging.StreamHandler(sys.stdout)
404 fmt = logging.Formatter(logging.BASIC_FORMAT)
405 hdlr.setFormatter(fmt)
406 rootLogger.addHandler(hdlr)
407
408 #Set up a handler such that all events are sent via a socket to the log
409 #receiver (logrecv).
410 #The handler will only be added to the rootLogger for some of the tests
411 hdlr = logging.handlers.SocketHandler('localhost',
412 logging.handlers.DEFAULT_TCP_LOGGING_PORT)
413
414 #Configure the logger for logrecv so events do not propagate beyond it.
415 #The sockLogger output is buffered in memory until the end of the test,
416 #and printed at the end.
417 sockOut = cStringIO.StringIO()
418 sockLogger = logging.getLogger("logrecv")
419 sockLogger.setLevel(logging.DEBUG)
420 sockhdlr = logging.StreamHandler(sockOut)
421 sockhdlr.setFormatter(logging.Formatter(
422 "%(name)s -> %(levelname)s: %(message)s"))
423 sockLogger.addHandler(sockhdlr)
424 sockLogger.propagate = 0
425
426 #Set up servers
427 threads = []
428 tcpserver = LogRecordSocketReceiver()
Guido van Rossum2a1d5162003-01-21 21:05:22 +0000429 #sys.stdout.write("About to start TCP server...\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000430 threads.append(threading.Thread(target=runTCP, args=(tcpserver,)))
431
432 for thread in threads:
433 thread.start()
434 try:
435 banner("log_test0", "begin")
436
437 rootLogger.addHandler(hdlr)
438 test0()
439 rootLogger.removeHandler(hdlr)
440
441 banner("log_test0", "end")
442
443 banner("log_test1", "begin")
444 test1()
445 banner("log_test1", "end")
446
447 banner("log_test2", "begin")
448 test2()
449 banner("log_test2", "end")
450
451 banner("log_test3", "begin")
452 test3()
453 banner("log_test3", "end")
454
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000455 finally:
456 #shut down server
457 tcpserver.abort = 1
458 for thread in threads:
459 thread.join()
Guido van Rossum2a1d5162003-01-21 21:05:22 +0000460 banner("logrecv output", "begin")
461 sys.stdout.write(sockOut.getvalue())
462 sockOut.close()
463 banner("logrecv output", "end")
464 sys.stdout.flush()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000465
466if __name__ == "__main__":
467 sys.stdout.write("test_logging\n")
468 test_main()