Changeset 470:e81a69790aa2
- Timestamp:
- 08/28/08 16:27:18 (4 months ago)
- Files:
-
- ChangeLog (modified) (1 diff)
- doc/logging.xhtml (modified) (1 diff)
- foolscap/logging/log.py (modified) (4 diffs)
- foolscap/pb.py (modified) (1 diff)
- foolscap/test/test_logging.py (modified) (7 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
ChangeLog
r469 r470 1 2008-08-28 Brian Warner <warner@lothar.com> 2 3 * foolscap/logging/log.py: add bridge-foolscap-logs-to-twisted 4 functionality, set up by either calling bridgeLogsToTwisted(), or 5 by setting the FLOGTOTWISTED environment variable (to anything). 6 The default filter will exclude events below the OPERATIONAL 7 severity level, and those generated by foolscap internals (i.e. 8 facility.startswith("foolscap") ). Closes #93. 9 * foolscap/pb.py (Tub.setOption): update to match 10 * foolscap/test/test_logging.py (Bridge): tests for it 11 (Publish): update to use new APIs 12 * doc/logging.xhtml: docs 13 1 14 2008-08-25 Brian Warner <warner@lothar.com> 2 15 doc/logging.xhtml
r461 r470 670 670 method is until its stopService() method is called). TODO: review this 671 671 behavior, we want earlier messages to be bridged too.</p> 672 673 <p>To bridge log events in the other direction (i.e. taking foolscap log 674 messages and copying them into twisted), use the 675 <code>log.bridgeLogsToTwisted()</code> call, or the 676 <code>FLOGTOTWISTED</code> environment variable. This is useful to get 677 foolscap.logging.log.msg() events copied into <code>twistd.log</code>. The 678 default filter only bridges non-noisy events (i.e. those at level OPERATIONAL 679 or higher), and does not bridge foolscal internal events.</p> 680 681 <p>You might use this if you don't buy into the foolscap logging philosophy 682 and really want log events to be continually written out to disk. You might 683 also use it if you want a long-term record of operationally-significant 684 events, or a record that will survive application crashes which don't get 685 handled by the existing Incident-recording mechanism.</p> 686 687 <pre class="python"> 688 from foolscap.logging import log 689 log.bridgeLogsToTwisted() 690 </pre> 691 672 692 673 693 <h4>stdlib 'logging' module</h4> foolscap/logging/log.py
r458 r470 295 295 296 296 class TwistedLogBridge: 297 def __init__(self, tubID=None ):297 def __init__(self, tubID=None, foolscap_logger=theLogger): 298 298 self.tubID = tubID 299 self.logger = foolscap_logger 299 300 300 301 # newer versions of Twisted have a function called … … 315 316 # ['system']: string 316 317 318 if "from-foolscap" in d: 319 return 317 320 event = d.copy() 318 321 event['tubID'] = self.tubID 319 322 message = twisted_log.textFromEventDict(d) 320 323 event.pop('message', None) 321 msg(message, **event) 324 event['from-twisted'] = True 325 self.logger.msg(message, **event) 322 326 323 327 def _old_twisted_log_observer(self, d): 328 if "from-foolscap" in d: 329 return 324 330 event = d.copy() 325 331 if "format" in d: … … 333 339 event.pop('args', None) 334 340 event['tubID'] = self.tubID 335 msg(**event) 336 337 theTwistedLogBridge = None 338 339 def setTwistedLogBridge(bridge): 340 global theTwistedLogBridge 341 if theTwistedLogBridge: 342 try: 343 twisted_log.removeObserver(theTwistedLogBridge.observer) 344 except ValueError: 345 pass 346 if bridge: 347 theTwistedLogBridge = bridge 348 twisted_log.addObserver(bridge.observer) 349 350 def bridgeTwistedLogs(): 351 setTwistedLogBridge(TwistedLogBridge()) 341 event['from-twisted'] = True 342 self.logger.msg(**event) 343 344 def bridgeLogsFromTwisted(tubID=None, 345 twisted_logger=twisted_log.theLogPublisher, 346 foolscap_logger=theLogger): 347 """Called without arguments, this arranges for all twisted log messages 348 to be bridged into the default foolscap logger. 349 350 I can also be called with a specific twisted and/or foolscap logger, 351 mostly for unit tests that don't want to modify the default instances. 352 For their benefit, I return the bridge. 353 """ 354 tlb = TwistedLogBridge(tubID, foolscap_logger) 355 twisted_logger.addObserver(tlb.observer) 356 return tlb 357 358 def bridgeLogsToTwisted(filter=None, 359 foolscap_logger=theLogger, 360 twisted_logger=twisted_log): 361 # foolscap_logger and twisted_logger are for testing purposes 362 def non_foolscap_operational_or_better(e): 363 if e.get("facility","").startswith("foolscap"): 364 return False 365 if e['level'] < OPERATIONAL: 366 return False 367 return True 368 if not filter: 369 filter = non_foolscap_operational_or_better 370 def _to_twisted(event): 371 if "from-twisted" in event: 372 return 373 if not filter(event): 374 return 375 args = {"from-foolscap": True, 376 "num": event["num"], 377 "level": event["level"], 378 } 379 twisted_logger.msg(format_message(event), **args) 380 foolscap_logger.addObserver(_to_twisted) 352 381 353 382 class LogFileObserver: … … 402 431 403 432 if "FLOGTWISTED" in os.environ: 404 bridgeTwistedLogs() 433 bridgeLogsFromTwisted() 434 435 if "FLOGTOTWISTED" in os.environ: 436 bridgeLogsToTwisted() foolscap/pb.py
r438 r470 340 340 self.setLogGathererFURLFile(value) 341 341 elif name == "bridge-twisted-logs": 342 if value:343 tlb = flog.TwistedLogBridge(self.tubID)344 flog.setTwistedLogBridge(tlb)342 assert value is not False, "cannot unbridge twisted logs" 343 if value is True: 344 return flog.bridgeLogsFromTwisted(self.tubID) 345 345 else: 346 flog.setTwistedLogBridge(None) 346 # for tests, bridge logs from a specific twisted LogPublisher 347 return flog.bridgeLogsFromTwisted(self.tubID, 348 twisted_logger=value) 347 349 elif name == "handle-old-duplicate-connections": 348 350 if value is True: foolscap/test/test_logging.py
r465 r470 349 349 def tearDown(self): 350 350 publish.Subscription.MAX_QUEUE_SIZE = self.saved_queue_size 351 log.setTwistedLogBridge(None) # disable any bridge still in place352 351 d = defer.succeed(None) 353 352 d.addCallback(lambda res: self.parent.stopService()) … … 402 401 logport_furl2 = open(furlfile, "r").read().strip() 403 402 self.failUnlessEqual(logport_furl, logport_furl2) 404 t.setOption("bridge-twisted-logs", True) 403 tw_log = twisted_log.LogPublisher() 404 tlb = t.setOption("bridge-twisted-logs", tw_log) 405 405 406 406 t2 = GoodEnoughTub() 407 407 t2.setServiceParent(self.parent) 408 408 ob = Observer() 409 410 do_twisted_errors = hasattr(self, "flushLoggedErrors")411 409 412 410 d = t2.getReference(logport_furl) … … 425 423 ob.last_received = time.time() 426 424 log.msg("message 1 here") 427 twisted_log.msg("message 2 here") 425 tw_log.msg("message 2 here") 426 428 427 # switch to generic (no tubid) bridge 429 log.bridgeTwistedLogs() 430 twisted_log.msg("message 3 here") 431 twisted_log.msg(format="%(foo)s is foo", foo="foo") 428 tw_log.removeObserver(tlb.observer) 429 log.bridgeLogsFromTwisted(None, tw_log) 430 431 tw_log.msg("message 3 here") 432 tw_log.msg(format="%(foo)s is foo", foo="foo") 432 433 log.err(failure.Failure(SampleError("err1"))) 433 434 log.err(SampleError("err2")) 434 if do_twisted_errors: 435 twisted_log.err(failure.Failure(SampleError("err3"))) 436 twisted_log.err(SampleError("err4")) 437 # twisted-2.5.0 added flushLoggedErrors, which makes 438 # it much easier for unit test to exercise error logging 439 errors = self.flushLoggedErrors(SampleError) 440 self.failUnlessEqual(len(errors), 2) 435 # simulate twisted.python.log.err, which is unfortunately 436 # not a method of LogPublisher 437 def err(_stuff=None, _why=None): 438 if isinstance(_stuff, Exception): 439 tw_log.msg(failure=failure.Failure(_stuff), 440 isError=1, why=_why) 441 else: 442 tw_log.msg(failure=_stuff, isError=1, why=_why) 443 err(failure.Failure(SampleError("err3"))) 444 err(SampleError("err4")) 441 445 d.addCallback(_emit) 442 446 # now we wait until the observer has seen nothing for a full … … 454 458 def _check_observer(res): 455 459 msgs = ob.messages 456 expected = 6 457 if do_twisted_errors: 458 expected += 2 459 self.failUnlessEqual(len(msgs), expected) 460 self.failUnlessEqual(len(msgs), 8) 460 461 #print msgs 461 462 self.failUnlessEqual(msgs[0]["message"], "message 1 here") … … 479 480 self.failUnless("err2" in str(msgs[5]["failure"])) 480 481 481 if do_twisted_errors: 482 # twisted-8.0 has textFromEventDict, which means we get a 483 # ["message"] key from log.err . In older version of 484 # twisted, we don't. 485 if msgs[6]["message"]: 486 self.failUnless("Unhandled Error" in msgs[6]["message"]) 487 self.failUnless("SampleError: err3" in msgs[6]["message"]) 488 self.failUnless(msgs[6]["isError"]) 489 self.failUnless("failure" in msgs[6]) 490 self.failUnless(msgs[6]["failure"].check(SampleError)) 491 self.failUnless("err3" in str(msgs[6]["failure"])) 492 493 # same 494 if msgs[7]["message"]: 495 self.failUnless("Unhandled Error" in msgs[7]["message"]) 496 self.failUnless("SampleError: err4" in msgs[7]["message"]) 497 self.failUnless(msgs[7]["isError"]) 498 self.failUnless("failure" in msgs[7]) 499 self.failUnless(msgs[7]["failure"].check(SampleError)) 500 self.failUnless("err4" in str(msgs[7]["failure"])) 482 # twisted-8.0 has textFromEventDict, which means we get a 483 # ["message"] key from log.err . In older version of 484 # twisted, we don't. 485 if msgs[6]["message"]: 486 self.failUnless("Unhandled Error" in msgs[6]["message"]) 487 self.failUnless("SampleError: err3" in msgs[6]["message"]) 488 self.failUnless(msgs[6]["isError"]) 489 self.failUnless("failure" in msgs[6]) 490 self.failUnless(msgs[6]["failure"].check(SampleError)) 491 self.failUnless("err3" in str(msgs[6]["failure"])) 492 493 # same 494 if msgs[7]["message"]: 495 self.failUnless("Unhandled Error" in msgs[7]["message"]) 496 self.failUnless("SampleError: err4" in msgs[7]["message"]) 497 self.failUnless(msgs[7]["isError"]) 498 self.failUnless("failure" in msgs[7]) 499 self.failUnless(msgs[7]["failure"].check(SampleError)) 500 self.failUnless("err4" in str(msgs[7]["failure"])) 501 501 502 502 d.addCallback(_check_observer) … … 1049 1049 1050 1050 def tearDown(self): 1051 log.setTwistedLogBridge(None) # disable any bridge still in place1052 1051 d = defer.succeed(None) 1053 1052 d.addCallback(lambda res: self.parent.stopService()) … … 1847 1846 return d 1848 1847 1848 1849 class Bridge(unittest.TestCase): 1850 def test_foolscap_to_twisted(self): 1851 fl = log.FoolscapLogger() 1852 tw = twisted_log.LogPublisher() 1853 log.bridgeLogsToTwisted(None, fl, tw) 1854 tw_out = [] 1855 tw.addObserver(tw_out.append) 1856 fl_out = [] 1857 fl.addObserver(fl_out.append) 1858 1859 fl.msg("one") 1860 fl.msg(format="two %(two)d", two=2) 1861 fl.msg("three", level=log.NOISY) # should be removed 1862 d = flushEventualQueue() 1863 def _check(res): 1864 self.failUnlessEqual(len(fl_out), 3) 1865 self.failUnlessEqual(fl_out[0]["message"], "one") 1866 self.failUnlessEqual(fl_out[1]["format"], "two %(two)d") 1867 self.failUnlessEqual(fl_out[2]["message"], "three") 1868 1869 self.failUnlessEqual(len(tw_out), 2) 1870 self.failUnlessEqual(tw_out[0]["message"], ("one",)) 1871 self.failUnless(tw_out[0]["from-foolscap"]) 1872 self.failUnlessEqual(tw_out[1]["message"], ("two 2",)) 1873 self.failUnless(tw_out[1]["from-foolscap"]) 1874 1875 d.addCallback(_check) 1876 return d 1877 1878 def test_twisted_to_foolscap(self): 1879 fl = log.FoolscapLogger() 1880 tw = twisted_log.LogPublisher() 1881 log.bridgeLogsFromTwisted(None, tw, fl) 1882 tw_out = [] 1883 tw.addObserver(tw_out.append) 1884 fl_out = [] 1885 fl.addObserver(fl_out.append) 1886 1887 tw.msg("one") 1888 tw.msg(format="two %(two)d", two=2) 1889 d = flushEventualQueue() 1890 def _check(res): 1891 self.failUnlessEqual(len(tw_out), 2) 1892 self.failUnlessEqual(tw_out[0]["message"], ("one",)) 1893 self.failUnlessEqual(tw_out[1]["format"], "two %(two)d") 1894 self.failUnlessEqual(tw_out[1]["two"], 2) 1895 1896 self.failUnlessEqual(len(fl_out), 2) 1897 self.failUnlessEqual(fl_out[0]["message"], "one") 1898 self.failUnless(fl_out[0]["from-twisted"]) 1899 self.failUnlessEqual(fl_out[1]["format"], "two %(two)d") 1900 self.failUnless(fl_out[1]["from-twisted"]) 1901 1902 d.addCallback(_check) 1903 return d 1904 1905 1906 def test_no_loops(self): 1907 fl = log.FoolscapLogger() 1908 tw = twisted_log.LogPublisher() 1909 log.bridgeLogsFromTwisted(None, tw, fl) 1910 log.bridgeLogsToTwisted(None, fl, tw) 1911 tw_out = [] 1912 tw.addObserver(tw_out.append) 1913 fl_out = [] 1914 fl.addObserver(fl_out.append) 1915 1916 tw.msg("one") 1917 fl.msg("two") 1918 1919 d = flushEventualQueue() 1920 def _check(res): 1921 self.failUnlessEqual(len(tw_out), 2) 1922 self.failUnlessEqual(tw_out[0]["message"], ("one",)) 1923 self.failUnlessEqual(tw_out[1]["message"], ("two",)) 1924 1925 self.failUnlessEqual(len(fl_out), 2) 1926 self.failUnlessEqual(fl_out[0]["message"], "one") 1927 self.failUnlessEqual(fl_out[1]["message"], "two") 1928 1929 d.addCallback(_check) 1930 return d 1931
