Changeset 471:078b9eb64535

Show
Ignore:
Timestamp:
08/28/08 17:22:48 (4 months ago)
Author:
Brian Warner <warner@allmydata.com>
branch:
default
Message:

logging: combine multiple overlapping Incidents into a single one. Helps with #85.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • ChangeLog

    r470 r471  
    112008-08-28  Brian Warner  <warner@lothar.com> 
     2 
     3        * foolscap/logging/log.py (FoolscapLogger.declare_incident): 
     4        combine overlapping incidents, by passing new triggers to an 
     5        existing reporter instead of creating a new one. Helps with #85. 
     6        * foolscap/logging/incident.py (IncidentReporter.new_trigger): new 
     7        method to add subsequent triggers to an existing reporter. This 
     8        doesn't do anything yet. It should be improved to record the other 
     9        triggers in a trailer (since it's too late to add it to the 
     10        header). Every triggering event will make it into an incident 
     11        somewhere, but the report-file analysis tools may not know how to 
     12        pay attention to the subsequent triggers. 
     13        * foolscap/logging/interfaces.py (IIncidentReporter.new_trigger): same 
     14        * foolscap/test/test_logging.py (Incidents.test_overlapping): test it 
     15        * doc/logging.xhtml: mention incident-coalescing 
    216 
    317        * foolscap/logging/log.py: add bridge-foolscap-logs-to-twisted 
  • doc/logging.xhtml

    r470 r471  
    480480logging code should capture the otherwise-ordinary events performed by this 
    481481recovery code.</p> 
     482 
     483<p>Overlapping incidents will be combined: if an incident reporter is already 
     484active when the qualifier sees a new triggering event, that event is just 
     485added to the existing reporter.</p> 
    482486 
    483487<p>The incident reporter can be overridden as well, by calling 
  • foolscap/logging/incident.py

    r440 r471  
    6161        self.logger = logger 
    6262        self.tubid_s = tubid_s 
     63        self.active = True 
     64 
     65    def is_active(self): 
     66        return self.active 
    6367 
    6468    def format_time(self, when): 
     
    106110 
    107111        if self.TRAILING_DELAY is None: 
     112            self.active = False 
    108113            eventually(self.finished_recording) 
    109114        else: 
     
    127132        self.stop_recording() 
    128133 
     134    def new_trigger(self, ev): 
     135        # it is too late to add this to the header. We could add it to a 
     136        # trailer, though. 
     137        pass 
     138 
    129139    def stop_recording(self): 
    130140        self.still_recording = False 
     141        self.active = False 
    131142        if self.timer.active(): 
    132143            self.timer.cancel() 
  • foolscap/logging/interfaces.py

    r464 r471  
    129129    def incident_declared(triggering_event): 
    130130        """This is called when an Incident needs to be recorded.""" 
     131    def new_trigger(triggering_event): 
     132        """This is called when a triggering event occurs while an incident is 
     133        already being reported. If the event happened later, it would trigger 
     134        a new incident. Since it overlapped with the existing incident, it 
     135        will just be added to that incident. 
     136 
     137        The triggering event will also be reported through the usual 
     138        event-publish-subscribe mechanism. This method is provided to give 
     139        the reporter the opportunity to mark the event somehow, for the 
     140        benefit of incident-file analysis tools. 
     141        """ 
     142    def is_active(): 
     143        """Returns True if the reporter is still running. While in this 
     144        state, new Incident triggers will be passed to the existing reporter 
     145        instead of causing a new Incident to be declared. This will tend to 
     146        coalesce back-to-back problems into a single Incident.""" 
     147 
  • foolscap/logging/log.py

    r470 r471  
    5454        self.active_incident_qualifier = None 
    5555        self.incident_reporter_factory = IncidentReporter 
    56         self.active_incident_reporters = weakref.WeakKeyDictionary() 
     56        self.active_incident_reporter_weakref = None 
    5757        self.incidents_declared = 0 
    5858        self.incidents_recorded = 0 
     
    250250    def declare_incident(self, triggering_event): 
    251251        self.incidents_declared += 1 
     252        ir = self.get_active_incident_reporter() 
     253        if ir: 
     254            ir.new_trigger(triggering_event) 
     255            return 
    252256        if self.logdir: # just in case 
    253257            ir = self.incident_reporter_factory(self.logdir, self, "local") 
    254             self.active_incident_reporters[ir] = None 
    255             ir.incident_declared(triggering_event) 
     258            self.active_incident_reporter_weakref = weakref.ref(ir) 
     259            ir.incident_declared(triggering_event) # this takes a few seconds 
    256260 
    257261    def incident_recorded(self, filename, name, trigger): 
     
    265269        for o in self._immediate_incident_observers: 
    266270            o(name, trigger) 
     271 
     272    def get_active_incident_reporter(self): 
     273        if self.active_incident_reporter_weakref: 
     274            ir = self.active_incident_reporter_weakref() 
     275            if ir and ir.is_active(): 
     276                return ir 
     277        return None 
    267278 
    268279    def setLogPort(self, logport): 
  • foolscap/test/test_logging.py

    r470 r471  
    229229        l.msg("3-trigger", level=log.BAD) 
    230230        self.failUnlessEqual(l.incidents_declared, 1) 
    231         self.failUnlessEqual(len(l.active_incident_reporters), 1
     231        self.failUnless(l.get_active_incident_reporter()
    232232        # at this point, the uncompressed logfile should be present, and it 
    233233        # should contain all the events up to and including the trigger 
     
    307307            self.failUnlessEqual(events[-1]["d"]["message"], "6") 
    308308        d.addCallback(_check) 
     309        return d 
     310 
     311    def test_overlapping(self): 
     312        l = log.FoolscapLogger() 
     313        l.setLogDir("logging/Incidents/overlapping") 
     314        got_logdir = l.logdir 
     315        self.failUnlessEqual(got_logdir, 
     316                             os.path.abspath("logging/Incidents/overlapping")) 
     317        d = defer.Deferred() 
     318        def _go(name, trigger): 
     319            d.callback( (name, trigger) ) 
     320        l.addImmediateIncidentObserver(_go) 
     321        l.setIncidentReporterFactory(ImpatientReporter) 
     322        l.msg("1") 
     323        l.msg("2-trigger", level=log.BAD) 
     324        self.failUnlessEqual(l.incidents_declared, 1) 
     325        self.failUnless(l.get_active_incident_reporter()) 
     326        l.msg("3-trigger", level=log.BAD) 
     327        self.failUnlessEqual(l.incidents_declared, 2) 
     328        self.failUnless(l.get_active_incident_reporter()) 
     329 
     330        def _check(res): 
     331            self.failUnlessEqual(l.incidents_recorded, 1) 
     332            self.failUnlessEqual(len(l.recent_recorded_incidents), 1) 
     333            # at this point, the logfile should be present, and it should 
     334            # contain all the events up to and including both triggers 
     335 
     336            files = os.listdir(got_logdir) 
     337            self.failUnlessEqual(len(files), 1) 
     338            events = self._read_logfile(os.path.join(got_logdir, files[0])) 
     339 
     340            self.failUnlessEqual(len(events), 1+3) 
     341            self.failUnlessEqual(events[0]["header"]["trigger"]["message"], 
     342                                 "2-trigger") 
     343            self.failUnlessEqual(events[1]["d"]["message"], "1") 
     344            self.failUnlessEqual(events[2]["d"]["message"], "2-trigger") 
     345            self.failUnlessEqual(events[3]["d"]["message"], "3-trigger") 
     346        d.addCallback(_check) 
     347 
    309348        return d 
    310349