class CornerMark(object): """Displays a 5 char colored empty string at the bottom right corner of terminal in case an error, fatal or warning is found.""" MARK = 5 * " " markable = {'FATAL' : 'backgroundemph', 'ERROR' : 'backgroundemph', 'WARN' : 'onyellowemph', 'WARNING' : 'onyellowemph', 'TARGET' : 'oncyanemph'} def __init__(self, gaptime): self.corner_time = float(gaptime) self.termcolors = TermColorCodes() self.len_mark = len(self.MARK) self.timer = Timer(self.corner_time) self.count = 0 self.flagged = False self.emphcolor = 'backgroundemph' def corner_mark_time(self): return self.corner_time def __term_num_cols(self): """Returns the number columns in the current terminal using the Linux tputs command line tool. :return: The number of columns currently in the terminal. """ termcols = os.popen("tput cols") ttcols = termcols.readline() termcols.close() ttcols = int(ttcols) return ttcols def notify(self, message, log): """Displays a 5 char colored empty string in case a message comes in with the level specified in the class attribute markable. First time we get a markable level, a timer is started with the number of seconds specified in self.corner_time and a colored string will be displayed for that number of seconds. The timer will not be restarted during that time. :param message: the message object wrapping the current log trace :param log: the log associated with the current message """ level = message.messageLevel.upper() isTarget = message.isATarget() # target has priority over markable levels if isTarget: self.flagged = True self.emphcolor = self.markable.get("TARGET") elif level in self.markable: self.flagged = True self.emphcolor = self.markable.get(level) if self.flagged: if self.count == 0: self.timer.startTimer() self.count += 1 if self.timer.corner_mark_ellapsed() < self.corner_time: padding = self.__term_num_cols() - self.len_mark trace = (padding * " " + getattr(self.termcolors, self.emphcolor)+ self.MARK + self.termcolors.reset) print trace else: self.timer.stopTimer() self.count = 0 self.flagged = False
class Log(object): '''Class that defines a common structure in a log''' TARGET_PROPERTY_PREFIX = "targets " def __init__(self, path, properties=None, options=None): self.path = path self.fh = None self.inode = None self.curpos = None self.size = None self.loglevel = None self.properties = properties self.ownOutputColor = None self.ownTarget = None self.patTarget = None self.inactivityTimer = None self.inactivityAccTime = 0 self.mailTimer = Timer(60) self.mailTimer.startTimer() self.logTargetColor = {} self.wasTarget = False self.emphcolor = None if properties: self.ownOutputColor = properties.get_value(path.lower()) self.ownTarget = properties.get_value(Log.TARGET_PROPERTY_PREFIX + path.lower()) if self.ownTarget: self.logTargetColor = self.targets_colors() self.patTarget = True if options and options.inactivity: self.inactivityTimer = Timer(float(options.inactivity)) self.inactivityTimer.startTimer() self.triggeredNotSent = False def getcurrInode(self): try: inode = os.stat(self.path)[ST_INO] except OSError: print "Could not stat, file removed?" raise OSError return inode def getcurrSize(self): size = os.stat(self.path)[ST_SIZE] return size def openLog(self): try: self.size = os.stat(self.path)[ST_SIZE] self.inode = os.stat(self.path)[ST_INO] except OSError: print "file "+self.path+" does not exist" raise OSError try: fd = open(self.path,'r') self.fh = fd self.curpos = fd.tell() return fd except IOError: print "Could not open file "+self.path raise IOError def readLine(self): self.fh.seek(self.curpos, 0) readline = self.fh.readline() self.updateCurPos() return readline def readLines(self): # seek to / update current position used (see #2) self.fh.seek(self.curpos, 0) readlines = self.fh.readlines() self.updateCurPos() return readlines def closeLog(self): self.fh.close() def updateCurPos(self): self.curpos = self.fh.tell() def seekLogEnd(self): # should be 2 for versions # older than 2.5 SEEK_END = 2 self.fh.seek(0,2) self.updateCurPos() def seekLogNearlyEnd(self): currpos = self.__getLast10Lines() self.fh.seek(currpos,0) self.updateCurPos() def __getLast10Lines(self): linesep = '\n' self.seekLogEnd() charRead = '' numLines = 0 # read one char at a time # as we get only last 10 lines # is not gonna be a lot of effort blockReadSize = 1 blockCount = 1 try: self.fh.seek(-blockReadSize,2) except: # file is empty, so return # with position beginning of file return 0 while (numLines <= 10): charRead = self.fh.read(blockReadSize) posactual = self.fh.tell() blockCount += 1 if charRead == linesep: numLines += 1 try: self.fh.seek(-blockReadSize*blockCount,2) except IOError: # already reached beginning # of file currpos = self.fh.tell()-posactual return currpos # add 2, to get rid of the last seek -1 # and the following \n currpos = self.fh.tell()+2 return currpos def numLines(self): count = -1 for count, _ in enumerate(open(self.path,'rU')): pass count += 1 return count def setInactivityAccTime(self, acctime): if acctime == 0: self.inactivityAccTime = 0 return self.inactivityAccTime += acctime def targetColor(self, target): return self.logTargetColor.get(target, '') def targets_colors(self): """{ compiled regex : color) } """ targetcolor = {} fields = [ k.strip() for k in self.ownTarget.split(';') ] for field in fields: regcolor = [ k.strip() for k in field.split(':') ] regex = re.compile(regcolor[0]) if len(regcolor) == 2: targetcolor[regex] = regcolor[1] else: targetcolor[regex] = None return targetcolor