import repository from arizona
[raven.git] / apps / logmon / analyze.py
1 #!/usr/bin/python
2
3 import base64
4 import datetime
5 import dateutil
6 import dateutil.parser
7 from optparse import OptionParser
8 import gzip
9 import hashlib
10 import os
11 import pickle
12 import platform
13 import re
14 import socket
15 import sys
16 import time
17 import urllib
18 import urllib2
19
20 #WEB_URL="http://quake.cs.arizona.edu/logmon/report"
21 WEB_URL="http://pl-virtual-11.CS.Princeton.EDU/logmon/report"
22
23 # number of lines to keep in duplicate database before emitting them
24 DUP_DB_MAXCOUNT = 20000          # maximum of 10,000 items
25 DUP_DB_MAXBYTES = 10*1024*1024   # ... or 10 megabytes
26
27 def _dict_size(item):
28     # just an estimate of the size of a dictionary
29     size = 0
30     for key in item:
31         size = size + len(str(key)) + len(str(item[key]))
32     return size
33
34 def _get_fileid(fn):
35     # an identifier for a file - hash the first 4kb of it
36     return hashlib.sha1(open(fn,"rb").read(4096)).hexdigest()
37
38 class log_processor:
39     def __init__(self):
40         self.item = {}
41         self.get_addrs()
42         self.bundle = []
43         self.submitted_bundles = {}
44         self.bundle_max = 100
45         self.classifiers = []
46
47         # to keep track of files that we've seen
48         self.file_db = {}
49
50         # dupchecker stuff
51         self.eliminate_dups = True
52         self.messages = {}
53         self.messages_count = 0
54         self.messages_bytes = 0
55
56     def get_addrs(self):
57         self.hostnames = [socket.gethostname()]
58         self.addrs = [socket.gethostbyname(socket.gethostname())]
59
60         if os.path.exists("/etc/planetlab/node_id"):
61             try:
62                 self.nodeid = file("/etc/planetlab/node_id","r").read()
63                 self.re_nodeid = re.compile("\\b" + self.nodeid + "\\b")
64             except:
65                 self.nodeid = None
66                 self.re_nodeid = None
67         else:
68            self.nodeid = None
69            self.re_nodeid = None
70
71     def emit_bundle(self, bundle):
72         if len(bundle) == 0:
73             return
74
75         pickle_list = pickle.dumps(bundle)
76
77         hash = base64.b64encode(hashlib.sha1(pickle_list).digest())[:16]
78
79         print "submit bundle items=", len(bundle), "len=", len(str(bundle)), "hash=", hash
80
81         if (self.check_submitted_bundle(hash)):
82             print "  previously submitted"
83             return
84
85         data = urllib.urlencode({"pickle_list": pickle_list})
86         tStart = time.time()
87         s = urllib2.urlopen(WEB_URL, data)
88         tStop = time.time()
89
90         self.log_submitted_bundle(hash)
91
92         tElapsed = tStop-tStart
93         delay = 0
94         if tElapsed > 15:
95             delay = tElapsed * 8
96         elif tElapsed > 10:
97             delay = tElapsed * 4
98         elif tElapsed > 5:
99             delay = tElapsed * 2
100         if (delay>300):
101             delay = 300
102         if (delay>0):
103             print "   elap was", tElapsed, "throttling for", delay, "seconds"
104             time.sleep(delay)
105
106         return True
107
108     def dump_messages(self):
109         # go through the self.messages, and for each message pick the item with
110         # the highest timestamp and bundle it for upload. Discard the others.
111
112         count = 0
113         for key in self.messages:
114             msgList = self.messages[key]
115             # sort by timestamp
116             msgList.sort(key=lambda item: item["timestamp"], reverse=True)
117             item = msgList[0]
118             item["qty"] = len(msgList)
119
120             self.bundle.append(item)
121             if len(self.bundle) >= self.bundle_max:
122                 self.emit_bundle(self.bundle)
123                 self.bundle = []
124
125             count = count + 1
126
127         self.messages={}
128
129         # if any lingering bundles from this dupcheck, emit them
130         self.emit_bundle(self.bundle)
131         self.bundle = []
132
133         # a good time to checkpoint the database, after having successfully
134         # complete a dupcheck/bundle submission
135         self.save_file_database()
136
137         print "dump_messages reduced", self.messages_count, "to", count, "non-duplicates"
138
139         self.messages_count = 0
140         self.messages_bytes = 0
141
142     def emit(self, item):
143         if (glo_options.dump):
144             print str(datetime.datetime.fromtimestamp(float(item["timestamp"]))).ljust(20), \
145                   item.get("kind","")[:10].ljust(10), \
146                   item.get("hostname","")[:20].ljust(20), \
147                   item.get("program","")[:10].ljust(10), \
148                   item.get("msg","")[:80]
149             extra = item.get("extra","")
150             if (extra and glo_options.dumpextra):
151                 for line in extra.split("\n"):
152                    print "   ", line
153             return
154
155         if self.eliminate_dups:
156             key = item["msg"] + item["program"] + item["kind"]
157             msgList = self.messages.get(key, None)
158             if (msgList == None):
159                 msgList = []
160                 self.messages[key] = msgList
161
162             msgList.append(item)
163             self.messages_count = self.messages_count + 1
164             self.messages_bytes = self.messages_bytes + _dict_size(item) + len(key)
165
166             if (self.messages_count >= DUP_DB_MAXCOUNT) or (self.messages_bytes >= DUP_DB_MAXBYTES):
167                 self.dump_messages()
168         else:
169             self.bundle.append(item)
170             if len(self.bundle) >= self.bundle_max:
171                 self.emit_bundle(self.bundle)
172                 self.bundle = []
173
174     def finalize(self, item):
175         if not 'hostname' in item:
176            return
177
178         if self.item:
179             if self.item["msg"].startswith("XXX_Traceback") and "extra" in self.item:
180                 # some ugliness for PHP traceback msgs
181                 self.item["msg"] = "Traceback: " + self.item["extra"].split("\n")[-1]
182
183         # looking for something around 16 bytes long. Base64 of a SHA1 is 28
184         # byte long; use the first 16 bytes ought to be good enough.
185         item["hash"] = base64.b64encode(hashlib.sha1(str(item)).digest())[:16]
186
187         self.emit(item)
188
189     def parse_extra(self, line):
190         if not line.strip():
191             # if it's just whitespace, return
192             return
193         if "no_more_extra" in self.item:
194             return
195         if "extra" in self.item:
196             if len(self.item["extra"])>20000:
197                 self.item["extra"] = self.item["extra"] + "\n" + "extra data capped at 20k"
198                 self.item["no_more_extra"] = "yes"
199             else:
200                 self.item["extra"] = self.item["extra"] + "\n" + line
201
202         else:
203             self.item["extra"] = line
204
205     def clean_segfault(self, msg):
206         parts = msg.split(" ")
207         if ("at" in parts):
208             index = parts.index("at")
209             parts[index+1] = "..."
210         if ("ip" in parts):
211             index = parts.index("ip")
212             parts[index+1] = "..."
213         if ("sp" in parts):
214             index = parts.index("sp")
215             parts[index+1] = "..."
216         if ("[" in parts[-1]):
217             parts[-1] = parts[-1].split("[",1)[0] + "[...]"
218         return " ".join(parts)
219
220     def clean_blocked(self, msg):
221         parts = msg.split(" ")
222         if len(parts)>2:
223             if ":" in parts[1]:
224                 parts[1] = parts[1].split(":")[0] + ":..."
225         return " ".join(parts)
226
227     def clean_0x(self, msg):
228         while "0x" in msg:
229             pos = msg.find("0x")
230             left = msg[:pos]
231             right = msg[pos:]
232             if (" " in right):
233                 right = "... " + right.split(" ",1)[1]
234             else:
235                 right = "..."
236             msg = left+right
237         return msg
238
239     def clean_undefined_index(self, msg):
240         tmp = msg[16:].lstrip()
241         parts = tmp.split(None,1)
242         if len(parts)==2:
243             msg = "Undefined index: ... " + parts[1]
244         return msg
245
246     def ignorable(self, program, msg):
247         if (program=="accounts"):
248             if msg.endswith(": installed ssh keys"):
249                 return True
250         elif (program=="slicemanager"):
251             if msg.endswith(": creating") or msg.endswith(": created") or msg.endswith(": started") or msg.endswith(": starting"):
252                 return True
253         elif (program=="bwmon"):
254             if msg.endswith("reset to 1gbit/1gbit"):
255                 return True
256         elif (program=="sliver_vs"):
257             if msg.endswith("starting in 0 seconds"):
258                 return True
259             if msg.endswith(": computing disk usage: beginning"):
260                 return True
261             if msg.endswith(": computing disk usage: ended"):
262                 return True
263             if msg.endswith(": setting cpu share to 1"):
264                 return True
265             if msg.endswith(": setting max disk usage to 10000000 KiB"):
266                 return True
267             if msg.endswith(": first chance..."):
268                 return True
269         return False
270
271     def classify_old(self, program, msg):
272         extra = None
273         kind = "generic"
274         if "exception" in msg:
275            # this should have been caught above, but just in case it's an
276            # exception that's not formatted the way we expect
277            kind = "exception"
278         elif "segfault" in msg:
279            kind = "segfault"
280            extra = msg
281            msg = self.clean_segfault(msg)
282         elif "failt" in msg:
283            kind = "fault"
284         elif "error" in msg:
285            kind = "error"
286         elif ("failed" in msg) or ("failure" in msg):
287            kind = "failure"
288         elif "warning" in msg:
289            kind = "warning"
290         elif "blocked" in msg:
291            kind = "blocked"
292            extra = msg
293            msg = self.clean_blocked(msg)
294         elif self.ignorable(program, msg):
295            kind = "ignore"
296
297         if (extra == msg):
298             extra = None
299
300         return (kind, msg, extra)
301
302     def classify(self, program, msg):
303         for classifier in self.classifiers:
304             c_prog = classifier["program"]
305             if re.match(c_prog, program) == None:
306                 continue
307             if (classifier["re"].search(msg)!=None):
308                 extra = None
309                 c_func = classifier["func"]
310                 if c_func:
311                     c_func = getattr(self, c_func)
312                     extra = msg
313                     msg = c_func(msg)
314                     if (extra == msg):
315                         extra = None
316                 return (classifier["kind"], msg, extra)
317         return ("generic", msg, None)
318
319     def parse_syslog(self, line):
320         date = line[:15]
321         line = line[15:].strip()
322
323         try:
324             date = dateutil.parser.parse(date)
325         except:
326             return self.parse_extra(line)
327
328         if (date.year<2000) or (date.year>2100):
329             return self.parse_extra(line)
330
331         timestamp = time.mktime(date.timetuple())
332
333         if (timestamp >= (time.time() + 60*60*24*180)):
334             # if the date is 6+ months in the future, then we're probably
335             # looking at a syslog from the previous year. (syslogs don't
336             # include a year in the timestamp)
337             date = datetime.datetime(date.year-1, date.month, date.day, date.hour, date.minute, date.second, date.microsecond, date.tzinfo)
338             timestamp = time.mktime(date.timetuple())
339
340         if not (" " in line):
341             # must be a space between hostname and the rest of the line
342             return self.parse_extra(line)
343
344         (hostpart, line) = line.split(" ",1)
345
346         # parse the username
347         #if not (": " in line):
348         #    # must be ": " between process and rest of line
349         #    return self.parse_extra(line)
350         #
351         #(username, line) = line.split(": ", 1)
352
353         if line.startswith("kernel: "):
354             line = line.split(": ", 1)[1]
355             kernelcolon=True
356         else:
357             kernelcolon=False
358
359         if (": " in line):
360             (program, msg) = line.split(": ", 1)
361             if (" " in program) or (program.isdigit()):
362                 program = ""
363                 msg = line
364         else:
365             program = ""
366             msg = line
367
368         if (kernelcolon) and (program==""):
369             program = "kernel"
370
371         if ("[" in program):
372             (program, junk) = program.split("[", 1)
373
374         # we now have timestamp, program, msg
375
376         extra = None
377         kind = "generic"
378
379         if (kind == "generic"):
380             (kind, msg, extra) = self.classify(program, msg)
381
382         if (kind == "ignore"):
383             return
384
385         # for things like kernel dumps and 'blocked' messages, we get a bunch
386         # of syslog lines together. They all have the same timestamp and the same
387         # username.
388         # we might screw up and jumble several messages together.
389         if ((program == self.item.get("program","")) and (timestamp == self.item.get("timestamp",-1))):
390             if (kind==self.item.get("kind","")):
391                 # sometimes multiple messages will happen at the same time, and
392                 # we don't wan't to string them together into one message. So,
393                 # if the classifier matched the message we're currently
394                 # appending to, then assume we just started a new message of
395                 # the same time.
396                 pass
397             else:
398                 return self.parse_extra(line)
399
400         # there's just too many syslog messages for us to log them all.
401         # concentrate on things that are errors
402         if (kind == "generic"):
403             return
404
405         # replace hostnames/ipaddresses with some generic strings; this will
406         # help eliminate duplicates in the database
407         for hostname in self.hostnames:
408             msg = msg.replace(hostname, "HOSTNAME")
409         for addr in self.addrs:
410             msg = msg.replace(addr, "IPADDR")
411         if self.re_nodeid:
412             msg = self.re_nodeid.sub("NODEID", msg)
413
414         # emit the old item if we have one on hand before we make up a new one
415         self.finalize(self.item)
416
417         self.item = {"timestamp": timestamp,
418                 "log": "syslog",
419                 "kind": kind,
420                 "hostname": self.hostnames[0],
421                 "program": program,
422 #                "username": username,
423                 "msg": msg}
424
425         if extra:
426             self.item["extra"] = extra
427
428         return self.item
429
430
431     def parse_nodemanager(self, line):
432         if not (": " in line):
433             return self.parse_extra(line)
434
435         (date, line) = line.split(": ", 1)
436         date = date.strip()
437         line = line.strip()
438         try:
439             date = dateutil.parser.parse(date)
440         except:
441             return self.parse_extra(line)
442
443         if (date.year<2000) or (date.year>2100):
444             return self.parse_extra(line)
445
446         timestamp = time.mktime(date.timetuple())
447
448         kind = "generic"
449         extra = None
450
451         if line.startswith("EXCEPTION caught <"):
452             kind = "exception"
453             line = line[18:]
454
455         msg = line
456         program = ""
457         if (": " in line):
458            (program, msg) = line.split(": ", 1)
459            if (" " in program) or (program.isdigit()):
460                program = ""
461                msg = line
462            program = program.strip()
463            msg = msg.strip()
464
465         # way too many of these to deal with...
466         if (program=="log_call"):
467            return
468
469         if (kind == "generic"):
470             (kind, msg, extra) = self.classify(program, msg)
471
472         if (kind == "ignore"):
473             return
474
475         # replace hostnames/ipaddresses with some generic strings; this will
476         # help eliminate duplicates in the database
477         for hostname in self.hostnames:
478             msg = msg.replace(hostname, "HOSTNAME")
479         for addr in self.addrs:
480             msg = msg.replace(addr, "IPADDR")
481         if self.re_nodeid:
482             msg = self.re_nodeid.sub("NODEID", msg)
483
484         # emit the old item if we have one on hand before we make up a new one
485         self.finalize(self.item)
486
487         self.item = {"timestamp": timestamp,
488                 "log": "nm",
489                 "kind": kind,
490                 "hostname": self.hostnames[0],
491                 "program": program,
492                 "msg": msg}
493
494         if extra:
495             self.item["extra"] = extra
496
497         return self.item
498
499     def split_brackets(self, left, right, line):
500         # this could probably all be done with regex...
501         line = line.lstrip()
502         if not line.startswith(left):
503             return (None, line)
504         if not (right in line):
505             return (None, line)
506         line = line.lstrip("[")
507         (token, line) = line.split(right,1)
508         line = line.lstrip()
509         return (token, line)
510
511     def parse_http_error(self, line):
512         (date, msg) = self.split_brackets("[", "]", line)
513         if not date:
514             return self.parse_extra(line)
515
516         try:
517             date = dateutil.parser.parse(date)
518         except:
519             return self.parse_extra(line)
520
521         if (date.year<2000) or (date.year>2100):
522             return self.parse_extra(line)
523
524         timestamp = time.mktime(date.timetuple())
525
526         if (timestamp >= (time.time() + 60*60*24*180)):
527             # if the date is 6+ months in the future, then we're probably
528             # looking at a syslog from the previous year. (syslogs don't
529             # include a year in the timestamp)
530             date = datetime.datetime(date.year-1, date.month, date.day, date.hour, date.minute, date.second, date.microsecond, date.tzinfo)
531             timestamp = time.mktime(date.timetuple())
532
533         (kind, msg) = self.split_brackets("[", "]", msg)
534         if not kind:
535             kind = "unknown"
536
537         if "Traceback" in msg:
538             (msg, extra) = msg.split("Traceback")
539             msg = msg.strip()
540             extra = "Traceback" + extra
541         else:
542             extra = None
543
544         program = "httpd"
545
546         # emit the old item if we have one on hand before we make up a new one
547         self.finalize(self.item)
548
549         self.item = {"timestamp": timestamp,
550                 "log": "http-error",
551                 "kind": kind,
552                 "hostname": self.hostnames[0],
553                 "program": program,
554                 "msg": msg}
555
556         if extra:
557             self.item["extra"] = extra
558
559     def parse_php(self, line):
560         (date, msg) = self.split_brackets("[", "]", line)
561         if not date:
562             return self.parse_extra(line)
563
564         try:
565             date = dateutil.parser.parse(date)
566         except:
567             return self.parse_extra(line)
568
569         if (date.year<2000) or (date.year>2100):
570             return self.parse_extra(line)
571
572         timestamp = time.mktime(date.timetuple())
573
574         if (timestamp >= (time.time() + 60*60*24*180)):
575             # if the date is 6+ months in the future, then we're probably
576             # looking at a syslog from the previous year. (syslogs don't
577             # include a year in the timestamp)
578             date = datetime.datetime(date.year-1, date.month, date.day, date.hour, date.minute, date.second, date.microsecond, date.tzinfo)
579             timestamp = time.mktime(date.timetuple())
580
581         if msg=="Traceback":
582             # some of the lines only say "Traceback" as the message
583             program = "Traceback"
584             kind = "Traceback"
585             msg = "XXX_Traceback"
586         else:
587             if not " " in msg:
588                 return self.parse_extra(line)
589
590             (program, msg) = msg.split(" ",1)
591
592             if ": " in msg:
593                 (kind, msg) = msg.split(": ",1)
594                 kind = kind.strip()
595                 msg = msg.strip()
596             else:
597                 kind = "generic"
598
599         extra = None
600
601         if msg.startswith("Undefined index:"):
602             extra = msg
603             msg = self.clean_undefined_index(msg)
604
605         # emit the old item if we have one on hand before we make up a new one
606         self.finalize(self.item)
607
608         self.item = {"timestamp": timestamp,
609                 "log": "php",
610                 "kind": kind,
611                 "hostname": self.hostnames[0],
612                 "program": program,
613                 "msg": msg}
614
615         if extra:
616             self.item["extra"] = extra
617
618
619     def do_internal(self):
620         self.finalize(self.item)
621         self.item = {"timestamp": time.time(),
622                      "log": "internal",
623                      "kind": "logmon",
624                      "program": "machine",
625                      "hostname": self.hostnames[0],
626                      "msg": str(platform.machine())}
627
628         self.finalize(self.item)
629         self.item = {"timestamp": time.time(),
630                      "log": "internal",
631                      "kind": "logmon",
632                      "program": "processor",
633                      "hostname": self.hostnames[0],
634                      "msg": str(platform.processor())}
635
636         self.finalize(self.item)
637         self.item = {"timestamp": time.time(),
638                      "log": "internal",
639                      "kind": "logmon",
640                      "program": "kernel",
641                      "hostname": self.hostnames[0],
642                      "msg": str(platform.release())}
643
644         self.finalize(self.item)
645         self.item = {"timestamp": time.time(),
646                      "log": "internal",
647                      "kind": "logmon",
648                      "program": "version",
649                      "hostname": self.hostnames[0],
650                      "msg": str(platform.version())}
651
652         self.finalize(self.item)
653         self.item = {"timestamp": time.time(),
654                      "log": "internal",
655                      "kind": "logmon",
656                      "program": "python",
657                      "hostname": self.hostnames[0],
658                      "msg": str(platform.python_version())}
659
660     def parse_file(self, filename, parsefunc=parse_nodemanager):
661         fid = _get_fileid(filename)
662         line_no = 0
663         lines_processed = 0
664
665         if filename.endswith(".gz"):
666             f = gzip.open(filename, "rt")
667         else:
668             f = open(filename, "rt")
669
670         if (fid in self.file_db) and (not glo_options.noskip):
671             skip = self.file_db[fid].get("line_no", 0)
672             if skip > 0:
673                 print "already seen", skip, "lines from", filename
674         else:
675             skip = 0
676
677         line = f.readline()
678         while (line!=""):
679             line_no = line_no + 1
680             if (line_no > skip):
681                 lines_processed = lines_processed + 1
682                 parsefunc(self, line.rstrip())
683             line = f.readline()
684
685         print "processed", lines_processed, "lines from", filename
686         self.file_db[fid] = {"line_no": line_no}
687
688     def load_submitted_bundles(self):
689         if not os.path.exists("bundle_hashes"):
690             return
691         f = open("bundle_hashes", "rt")
692         hash = f.readline()
693         while (hash!=""):
694             hash = hash.strip()
695             self.submitted_bundles[hash] = 1
696             hash = f.readline()
697
698     def load_classifiers(self):
699         f = open("classify.txt", "rt")
700         for line in f.readlines():
701             line = line.strip()
702             if line.startswith("#") or line=="":
703                 continue
704             parts = line.split(None,2)
705             if (len(parts)<3):
706                 continue
707             if ":" in parts[0]:
708                 (kind, func) = parts[0].split(":")
709             else:
710                 kind = parts[0]
711                 func = None
712             prog = parts[1]
713             if (prog=="*"):
714                 prog = ".*"
715             pattern = parts[2]
716
717             reprog = re.compile(pattern, flags=re.I)
718
719             self.classifiers.append( {"kind": kind, "func": func, "program": prog, "pattern": pattern, "re": reprog} )
720
721     def log_submitted_bundle(self, hash):
722         f = open("bundle_hashes", "at")
723         f.write(hash + "\n")
724
725     def load_file_database(self):
726         if not os.path.isfile("file_db"):
727             return
728
729         f = open("file_db", "rt")
730         try:
731             self.file_db = pickle.load(f)
732         except:
733             print "exception while loading file database"
734             self.file_db = {}
735
736     def save_file_database(self):
737         f = open("file_db", "wt")
738         pickle.dump(self.file_db, f)
739         print "file_db saved"
740
741     def check_submitted_bundle(self, hash):
742         return (hash in self.submitted_bundles)
743
744     def finish(self):
745         self.finalize(self.item)
746         self.dump_messages()
747         self.emit_bundle(self.bundle)
748         self.save_file_database()
749         self.item = {}
750         self.bundle = []
751
752 def test_classifiers():
753    processor = log_processor()
754    processor.load_classifiers()
755
756    #print processor.classifiers
757    print "failed -->", processor.classify("conf_files", "failed to retrieve /etc/sysctl.conf from https://boot.planet-lab.org/PlanetLabConf/sysctl.php?node_id=13293, skipping")
758    print "ignore -->", processor.classify("sliver_vs", "eurecom_toka: starting in 0 seconds")
759    print "ignore -->", processor.classify("sliver_vs", "princeton_codeen: first chance...")
760    print "ignore -->", processor.classify("sliver_vs", "Recorded slice id 21684 for slice waikato_scamper")
761    print "ignore -->", processor.classify("nodemanager", "mainloop - sleeping for 679 s")
762    print "ignore -->", processor.classify("CoreSched", "reserving uniprple_SP2Aservicemobility [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]")
763
764    print "disk_exception -->", processor.classify("ata1.00",  "exception Emask 0x0 SAct 0x40001 SErr 0x0 action 0x0")
765
766    sys.exit(-1)
767
768 def create_parser():
769    # Generate command line parser
770    parser = OptionParser(usage="analyze.py [options] <format> <fn> ...",
771         description="Analyze the log files")
772
773    parser.add_option("", "--dump", dest="dump",
774         help="dump msgs to output", action="store_true", default=False)
775    parser.add_option("", "--dumpextra", dest="dumpextra",
776         help="when dumping, show extra", action="store_true", default=False)
777    parser.add_option("", "--noskip", dest="noskip",
778         help="do not skip files already seen", action="store_true", default=False)
779
780    parser.disable_interspersed_args()
781
782    return parser
783
784 def main():
785    global glo_options
786
787    parser = create_parser()
788
789    (glo_options, args) = parser.parse_args()
790
791    if len(args)<2:
792       print "syntax: analyze.py [options] <format> filenames..."
793       print "   format = nodemanager|syslog|http-error"
794       sys.exit(-1)
795
796    #test_classifiers()
797
798    format = args[0]
799    filenames = args[1:]
800
801    if format == "nodemanager":
802        parser = log_processor.parse_nodemanager
803    elif format == "syslog":
804        parser = log_processor.parse_syslog
805    elif format == "http-error":
806        parser = log_processor.parse_http_error
807    elif format == "php":
808        parser = log_processor.parse_php
809    elif format == "internal":
810        pass
811    else:
812        print "unknown format", format
813        sys.exit(-1)
814
815    processor = log_processor()
816    processor.load_file_database()
817    processor.load_submitted_bundles()
818    processor.load_classifiers()
819
820    #print processor.parse_syslog("Jan  4 03:28:11 planetlab-01 kernel: INFO: task p2p:7619 blocked for more than 120 seconds.")
821    #print processor.parse_syslog("Apr 22 18:40:25 planetlab2 swapmon: slicestat: failed to parse line: 17682   ERR                           2756              689              620              0.0 /bin/bash /usr/sbin/vserver arizona_nest running")
822    #print processor.parse_syslog("Apr 20 16:12:26 planetlab2 swapmon: 80% swap consumed, slice intelfolsom_webmon is using 361.0 MB (8%) of memory")
823    #sys.exit(-1)
824
825    if format=="internal":
826        processor.do_internal()
827    else:
828        for filename in filenames:
829            if not os.path.exists(filename):
830                print filename, "does not exist"
831                continue
832            processor.parse_file(filename, parsefunc=parser)
833
834    processor.finish()
835
836 if __name__=="__main__":
837    main()
838
839
840
841