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 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 = 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
def test_ellapsed_with_start(self): timer = Timer(time_counter=TimeCounter(5)) timer.startTimer() self.assertEqual(timer.ellapsed(), 0)
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.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 return fd except IOError: print "Could not open file " + self.path raise IOError def _set_cur_pos(self): """Needs to be called for MacOS to work. Issue #11. Works fine as well for Linux. The only thing it does is just set the current position on the log. In Linux though, this is not really necessary to call, but does not harm either. It might be due to different implementations on readline/readlines. """ self.fh.seek(self.fh.tell()) def readLine(self): self._set_cur_pos() return self.fh.readline() def readLines(self): self._set_cur_pos() return self.fh.readlines() def closeLog(self): self.fh.close() def seekLogEnd(self): # should be 2 for versions # older than 2.5 SEEK_END = 2 self.fh.seek(0, 2) def seekLogNearlyEnd(self): currpos = self.__getLast10Lines() self.fh.seek(currpos, 0) 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
def test_start_timer(self): timer = Timer(time_counter=TimeCounter(10)) self.assertEqual(timer.startTimer(), 10)