# # Python module for common functions related logging and error notification. # import Setup, time, sys, traceback # Reading from a process' buffer will block until the buffer has been # entirely read, or the stream has closed. For this reason, we must # set the buffer really small. PROC_BUFFSIZE = 1024 PIPE_READ_PAUSE_LENGTH = 1 PIPE_READ_ATTEMPTS = 4 NEWLINE = '\n' # Flag used for identifying if there was a fatal error _FATAL_COUNT = 0 # Logging data FATAL = 0 ERR = 1 WARN = 2 INFO = 3 VERBOSE = 4 DEBUG = 5 _LOGLEVEL_TEXT = ('FATAL', 'ERR', 'WARN', 'INFO', 'VERBOSE', 'DEBUG') def log(level, msg, sendEmail=True): ''' Logs a message to the daemon log file. Use this over "print". ''' if not isinstance(level, int): level = VERBOSE if level <= FATAL: mod = sys.modules['cutil'] mod._FATAL_COUNT = mod._FATAL_COUNT + 1 if level <= Setup.LOGGING.logLevel: outmsg = "%s [%s] " % (time.strftime(Setup.LOGGING.timeFormat), _LOGLEVEL_TEXT[level]) if isinstance(msg, list) or isinstance(msg, tuple): for x in msg: outmsg = outmsg + str(x) else: outmsg = outmsg + str(msg) f = file(Setup.LOGGING.logfile, 'a') try: f.write(outmsg + NEWLINE) finally: f.close() if sendEmail and level <= Setup.LOGGING.emailLogLevel: emailProblem(outmsg) def hasFatal(): ''' Checks if a FATAL error was reported. This can be used to signal the Daemon process to stop running because a critical event could not be properly handled. Returns True if a FATAL message is known, otherwise False. ''' mod = sys.modules['cutil'] return mod._FATAL_COUNT > 0 def clearFatal(): ''' Clears the current fatal status. ''' mod = sys.modules['cutil'] mod._FATAL_COUNT = 0 def nonblockPopen(cmd): ''' Performs a Popen, returning the Popen4 instance, using non-blocking I/O for the input stream (child process' output stream). ''' import popen2, fcntl, os conn = popen2.Popen4(cmd, PROC_BUFFSIZE) flags = fcntl.fcntl(conn.fromchild.fileno(), fcntl.F_GETFL, 0) flags = flags | os.O_NONBLOCK fcntl.fcntl(conn.fromchild.fileno(), fcntl.F_SETFL, flags) return conn def nonblockReadlines(stream, lines = None, attempts = PIPE_READ_ATTEMPTS): ''' Read lines from a stream in a non-blocking way. ''' if lines == None: lines = [] retries = 0 while retries < 3: retries += 1 line = '' for attempts in range(1, attempts): try: line = stream.readline() except IOError, err: if err.errno != 11: # 11 = "Resource not available" which is okay in non-blocking # mode. raise RemoteConnectionException("NonBlocking Read", "Unexpected IOError on read: " + err) raise err if line != None and len(line) > 0: break time.sleep(PIPE_READ_PAUSE_LENGTH) if line != None and len(line) > 0: retries = 0 lines.append(line) return lines def log_error(level=ERR, sendEmail=True): """ Print a stack trace usefull for debugging. """ info = sys.exc_info() log(level, traceback.format_exception(info[0],info[1],info[2]), sendEmail) def emailProblem(msg): send_email(Setup.LOGGING.adminEmail, Setup.LOGGING.emailErrorSubject, msg) def send_email(toEmails, subject, body): ''' Send a plain-text e-mail ''' mailhost = Setup.LOGGING.smtpHost if mailhost is None or len(mailhost) <= 0: log(VERBOSE, 'Sending e-mails is disabled.') return COMMASPACE = ', ' import smtplib, types try: mailport = smtplib.SMTP(mailhost) except: log(ERR, 'Unable to connect to SMTP host "' + mailhost + '"!', False) log_error(sendEmail=False) return if Setup.LOGGING.emailReplyTo: replyto_line = 'Reply-To: %s\n' % Setup.LOGGING.emailReplyTo else: replyto_line = '' if type(toEmails) in types.StringTypes: toEmails = [ toEmails ] message = 'From: %s\n' +\ 'To: %s\n' +\ 'Subject: %s %s\n' +\ '%s' +\ '\n' +\ '%s' #print 'message body = [%s]' % message message = message % ( Setup.LOGGING.emailSender, COMMASPACE.join(toEmails), Setup.LOGGING.emailSubjectPrefix, subject, replyto_line, body) try: failed = mailport.sendmail(Setup.LOGGING.emailSender, toEmails, message) if failed: log(ERR, ['Problem sending email to SMTP host: ', repr(failed)]) except: log('Problem sending email to SMTP host "' + mailhost + '"!') log_error(sendEmail=False) try: mailport.quit() except: log_error(sendEmail=False)
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#7 | 5855 | Matt Albrecht | Update files for the log daemon | ||
#6 | 5850 | Matt Albrecht | Update logDaemon to version 1.5. | ||
#5 | 5810 | Matt Albrecht |
New version of logDaemon that has better bug fixes. Also added server-status web page for a pure client-side AJAX app that pulls together different server-side statistics. |
||
#4 | 5805 | Matt Albrecht | Update to version 1.3-alpha | ||
#3 | 5788 | Matt Albrecht | Bug fixes to the logDaemon, bumping the version number to 1.2 | ||
#2 | 5783 | Matt Albrecht | Updates to cutil with more functionality | ||
#1 | 5780 | Matt Albrecht | Update tool with lots of goodies | ||
//guest/matt_albrecht/scripts/logDaemon/common.py | |||||
#1 | 5424 | Matt Albrecht | Add a journal file daemon tool. |