Changeset 495:2fa51824742b
- Timestamp:
- 10/14/08 16:14:28 (3 months ago)
- Files:
-
- ChangeLog (modified) (1 diff)
- foolscap/logging/cli.py (modified) (3 diffs)
- foolscap/logging/gatherer.py (modified) (6 diffs)
- foolscap/logging/incident.py (modified) (2 diffs)
- foolscap/test/test_logging.py (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
ChangeLog
r494 r495 1 1 2008-10-14 Brian Warner <warner@allmydata.com> 2 3 * foolscap/logging/cli.py: new "flogtool classify-incident" 4 subcommand: given an incident, say what categories it falls into. 5 Closes #102. 6 * foolscap/logging/gatherer.py (IncidentGathererService): factor 7 out the classification pieces into IncidentClassifierBase 8 * foolscap/logging/incident.py (IncidentClassifierBase): same 9 (IncidentClassifier.run): support for the new CLI command 10 * foolscap/test/test_logging.py (Incidents.test_classify): test it 2 11 3 12 * foolscap/logging/gatherer.py foolscap/logging/cli.py
r459 r495 5 5 6 6 import foolscap 7 from foolscap.logging.tail import TailOptions 8 from foolscap.logging.gatherer import CreateGatherOptions, \ 9 CreateIncidentGatherOptions 10 from foolscap.logging.dumper import DumpOptions 11 from foolscap.logging.web import WebViewerOptions 12 from foolscap.logging.filter import FilterOptions 7 from foolscap.logging.tail import TailOptions, LogTail 8 from foolscap.logging.gatherer import \ 9 CreateGatherOptions, create_log_gatherer, \ 10 CreateIncidentGatherOptions, create_incident_gatherer 11 from foolscap.logging.dumper import DumpOptions, LogDumper 12 from foolscap.logging.web import WebViewerOptions, WebViewer 13 from foolscap.logging.filter import FilterOptions, Filter 14 from foolscap.logging.incident import ClassifyOptions, IncidentClassifier 13 15 14 16 class Options(usage.Options): … … 27 29 ("web-viewer", None, WebViewerOptions, 28 30 "view the logs through a web page"), 31 ("classify-incident", None, ClassifyOptions, 32 "classify a stored Incident file"), 29 33 ] 30 34 … … 46 50 def dispatch(command, options): 47 51 if command == "tail": 48 from foolscap.logging.tail import LogTail49 52 lt = LogTail(options) 50 53 lt.run(options.target_furl) 51 54 52 55 elif command == "create-gatherer": 53 from foolscap.logging.gatherer import create_log_gatherer54 56 create_log_gatherer(options) 55 57 56 58 elif command == "create-incident-gatherer": 57 from foolscap.logging.gatherer import create_incident_gatherer58 59 create_incident_gatherer(options) 59 60 60 61 elif command == "dump": 61 from foolscap.logging.dumper import LogDumper62 62 ld = LogDumper() 63 63 ld.run(options) 64 64 65 65 elif command == "filter": 66 from foolscap.logging.filter import Filter67 66 f = Filter() 68 67 f.run(options) 69 68 70 69 elif command == "web-viewer": 71 from foolscap.logging.web import WebViewer72 70 wv = WebViewer() 73 71 wv.run(options) 72 73 elif command == "classify-incident": 74 ic = IncidentClassifier() 75 ic.run(options) 74 76 75 77 else: foolscap/logging/gatherer.py
r494 r495 12 12 import foolscap 13 13 from foolscap.logging.interfaces import RILogGatherer, RILogObserver 14 from foolscap.logging.incident import IncidentClassifierBase 14 15 from foolscap.util import get_local_ip_for 15 16 … … 391 392 return None 392 393 393 class IncidentGathererService(GatheringBase ):394 class IncidentGathererService(GatheringBase, IncidentClassifierBase): 394 395 # create this with 'flogtool create-incident-gatherer BASEDIR' 395 396 # run this as 'cd BASEDIR && twistd -y gatherer.tac' … … 420 421 def __init__(self, classifiers=[], basedir=None, stdout=None): 421 422 GatheringBase.__init__(self, basedir) 422 self.classifiers = []423 IncidentClassifierBase.__init__(self) 423 424 self.classifiers.extend(classifiers) 424 425 self.stdout = stdout 425 426 self.incidents_received = 0 # for tests 426 427 def add_classifier(self, f):428 # there are old .tac files that call this explicitly429 self.classifiers.append(f)430 427 431 428 … … 437 434 if not os.path.isdir(outputdir): 438 435 os.makedirs(outputdir) 439 self.add_classify_files( )436 self.add_classify_files(self.basedir) 440 437 self.classify_stored_incidents(indir) 441 438 GatheringBase.startService(self) 442 443 def add_classify_files(self):444 for fn in os.listdir(self.basedir):445 if not (fn.startswith("classify_") and fn.endswith(".py")):446 continue447 f = open(os.path.join(self.basedir, fn), "r")448 localdict = {}449 exec f in localdict450 self.add_classifier(localdict["classify_incident"])451 439 452 440 def classify_stored_incidents(self, indir): … … 472 460 incident = self.load_incident(abs_fn) 473 461 rel_fn = os.path.join("incidents", tubid_s, fn) 474 self. classify_incident(rel_fn, tubid_s, incident)462 self.move_incident(rel_fn, tubid_s, incident) 475 463 count += 1 476 464 print >>stdout, "done classifying %d stored incidents" % count 477 478 def load_incident(self, abs_fn):479 assert abs_fn.endswith(".bz2")480 f = bz2.BZ2File(abs_fn, "r")481 header = pickle.load(f)["header"]482 events = []483 while True:484 try:485 wrapped = pickle.load(f)486 except (EOFError, ValueError):487 break488 events.append(wrapped["d"])489 f.close()490 return (header, events)491 465 492 466 def remote_logport(self, nodeid, publisher): … … 504 478 def new_incident(self, abs_fn, rel_fn, tubid_s, incident): 505 479 stdout = self.stdout or sys.stdout 506 self. classify_incident(rel_fn, tubid_s, incident)480 self.move_incident(rel_fn, tubid_s, incident) 507 481 self.incidents_received += 1 508 482 509 def classify_incident(self, rel_fn, tubid_s, incident):483 def move_incident(self, rel_fn, tubid_s, incident): 510 484 stdout = self.stdout or sys.stdout 511 categories = set() 512 for f in self.classifiers: 513 (header, events) = incident 514 trigger = header["trigger"] 515 c = f(trigger) 516 if c: # allow the classifier to return None, or [], or ["foo"] 517 if isinstance(c, str): 518 c = [c] # or just "foo" 519 categories.update(c) 520 if not categories: 521 categories.add("unknown") 485 categories = self.classify_incident(incident) 522 486 for c in categories: 523 487 fn = os.path.join(self.basedir, "classified", c) foolscap/logging/incident.py
r477 r495 1 1 2 import os.path, time, pickle, bz22 import sys, os.path, time, pickle, bz2 3 3 from zope.interface import implements 4 from twisted.python import usage 4 5 from twisted.internet import reactor 5 6 from foolscap.logging.interfaces import IIncidentReporter … … 165 166 class NonTrailingIncidentReporter(IncidentReporter): 166 167 TRAILING_DELAY = None 168 169 170 class ClassifyOptions(usage.Options): 171 stdout = sys.stdout 172 stderr = sys.stderr 173 synopsis = "Usage: flogtool classify-incident [options] INCIDENTFILE.." 174 175 optParameters = [ 176 ("classifier-directory", "c", ".", 177 "directory with classify_*.py functions to import"), 178 ] 179 180 def parseArgs(self, *files): 181 self.files = files 182 183 184 class IncidentClassifierBase: 185 186 def __init__(self): 187 self.classifiers = [] 188 189 def add_classifier(self, f): 190 # there are old .tac files that call this explicitly 191 self.classifiers.append(f) 192 193 def add_classify_files(self, plugindir): 194 plugindir = os.path.expanduser(plugindir) 195 for fn in os.listdir(plugindir): 196 if not (fn.startswith("classify_") and fn.endswith(".py")): 197 continue 198 f = open(os.path.join(plugindir, fn), "r") 199 localdict = {} 200 exec f in localdict 201 self.add_classifier(localdict["classify_incident"]) 202 203 def load_incident(self, abs_fn): 204 assert abs_fn.endswith(".bz2") 205 f = bz2.BZ2File(abs_fn, "r") 206 header = pickle.load(f)["header"] 207 events = [] 208 while True: 209 try: 210 wrapped = pickle.load(f) 211 except (EOFError, ValueError): 212 break 213 events.append(wrapped["d"]) 214 f.close() 215 return (header, events) 216 217 def classify_incident(self, incident): 218 categories = set() 219 for f in self.classifiers: 220 (header, events) = incident 221 trigger = header["trigger"] 222 c = f(trigger) 223 if c: # allow the classifier to return None, or [], or ["foo"] 224 if isinstance(c, str): 225 c = [c] # or just "foo" 226 categories.update(c) 227 if not categories: 228 categories.add("unknown") 229 return categories 230 231 class IncidentClassifier(IncidentClassifierBase): 232 def run(self, options): 233 self.add_classify_files(options["classifier-directory"]) 234 out = options.stdout 235 for f in options.files: 236 abs_fn = os.path.expanduser(f) 237 incident = self.load_incident(abs_fn) 238 categories = self.classify_incident(incident) 239 print >>out, "%s: %s" % (f, ",".join(sorted(categories))) 240 foolscap/test/test_logging.py
r494 r495 352 352 return d 353 353 354 def test_classify(self): 355 l = log.FoolscapLogger() 356 l.setIncidentReporterFactory(incident.NonTrailingIncidentReporter) 357 l.setLogDir("logging/Incidents/classify") 358 got_logdir = l.logdir 359 l.msg("foom", level=log.BAD) 360 d = fireEventually() 361 def _check(res): 362 files = [fn for fn in os.listdir(got_logdir) if fn.endswith(".bz2")] 363 self.failUnlessEqual(len(files), 1) 364 365 ic = incident.IncidentClassifier() 366 def classify_foom(trigger): 367 if "foom" in trigger.get("message",""): 368 return "foom" 369 ic.add_classifier(classify_foom) 370 options = incident.ClassifyOptions() 371 options.parseOptions([os.path.join(got_logdir, fn) for fn in files]) 372 options.stdout = StringIO() 373 ic.run(options) 374 out = options.stdout.getvalue() 375 self.failUnless(out.strip().endswith(": foom"), out) 376 d.addCallback(_check) 377 return d 354 378 355 379 class Observer(Referenceable):
