X-Git-Url: https://git.ralfj.de/saartuer.git/blobdiff_plain/83d646a505bed8c75eef15855da7046e5854106d..82c945d5140bce7c1884c8bc734f3403626bb5f1:/libtuer.py diff --git a/libtuer.py b/libtuer.py index a655fcd..f1e8d54 100644 --- a/libtuer.py +++ b/libtuer.py @@ -1,21 +1,145 @@ -import logging, logging.handlers, syslog, os +import logging, logging.handlers, os, time, queue, threading, subprocess +import traceback, smtplib +import email.mime.text, email.utils + +# Logging configuration +syslogLevel = logging.INFO +mailLevel = logging.CRITICAL # must be "larger" than syslog level! +from config import mailAddress +printLevel = logging.DEBUG + +# Mail logging handler +def sendeMail(subject, text, receivers, sender='sphinx@hacksaar.de', replyTo=None): + assert isinstance(receivers, list) + if not len(receivers): return # nothing to do + # construct content + msg = email.mime.text.MIMEText(text.encode('UTF-8'), 'plain', 'UTF-8') + msg['Subject'] = subject + msg['Date'] = email.utils.formatdate(localtime=True) + msg['From'] = sender + msg['To'] = ', '.join(receivers) + if replyTo is not None: + msg['Reply-To'] = replyTo + # put into envelope and send + s = smtplib.SMTP('localhost') + s.sendmail(sender, receivers, msg.as_string()) + s.quit() # logging function class Logger: def __init__ (self): - import __main__ as main - self.name = os.path.basename(main.__file__) - self.logger = logging.getLogger(self.name) - self.logger.setLevel(logging.INFO) - self.handler = logging.handlers.SysLogHandler(address = '/dev/log', facility = logging.handlers.SysLogHandler.LOG_LOCAL0) - self.logger.addHandler(self.handler) - def log (self, what): - thestr = "%s[%d]: %s" % (self.name,os.getpid(),what) - print (thestr) - self.logger.info(thestr) + self.syslog = logging.getLogger("tuerd") + self.syslog.setLevel(logging.DEBUG) + self.syslog.addHandler(logging.handlers.SysLogHandler(address = '/dev/log', + facility = logging.handlers.SysLogHandler.LOG_LOCAL0)) + + def _log (self, lvl, what): + thestr = "%s[%d]: %s" % ("tuerd", os.getpid(), what) + # console log + if lvl >= printLevel: + print(thestr) + # syslog + if lvl >= syslogLevel: + self.syslog.log(lvl, thestr) + # mail log + if lvl >= mailLevel and mailAddress is not None: + sendeMail('Kritischer Türfehler', what, mailAddress) + + def debug(self, what): + self._log(logging.DEBUG, what) + def info(self, what): + self._log(logging.INFO, what) + def warning(self, what): + self._log(logging.WARNING, what) + def error(self, what): + self._log(logging.ERROR, what) + def critical(self, what): + self._log(logging.CRITICAL, what) logger = Logger() -def log (what): - logger.log(what) - +# run a Python command asynchronously +def fire_and_forget(f): + def _fire_and_forget(): + try: + f() + except Exception: + logger.critical("fire_and_forget: Got exception out of callback:\n%s" % traceback.format_exc()) + t = threading.Thread(target=_fire_and_forget) + t.start() + +# run a command asynchronously and log the return value if not 0 +# prefix must be a string identifying the code position where the call came from +def fire_and_forget_cmd (cmd, log_prefix): + logger.debug("Firing and forgetting command from %s: %s" % (log_prefix,str(cmd))) + def _fire_and_forget_cmd (): + with open("/dev/null", "w") as fnull: + retcode = subprocess.call(cmd, stdout=fnull, stderr=fnull) + if retcode is not 0: + logger.error("%sReturn code %d at command: %s" % (log_prefix,retcode,str(cmd))) + fire_and_forget(_fire_and_forget_cmd) + +# Threaded callback class +class ThreadFunction(): + _CALL = 0 + _TERM = 1 + + def __init__(self, f, name): + self.name = name + self._f = f + self._q = queue.Queue() + self._t = threading.Thread(target=self._thread_func) + self._t.start() + + def _thread_func(self): + while True: + (cmd, data) = self._q.get() + # run command + if cmd == ThreadFunction._CALL: + try: + self._f(*data) + except Exception as e: + logger.critical("ThreadFunction: Got exception out of handler thread %s:\n%s" % (self.name, traceback.format_exc())) + elif cmd == ThreadFunction._TERM: + assert data is None + break + else: + logger.error("ThreadFunction: Command %d does not exist" % cmd) + + def __call__(self, *arg): + self._q.put((ThreadFunction._CALL, arg)) + + def stop(self): + # empty the queue + try: + while True: + self._q.get_nowait() + except queue.Empty: + pass + # now wait till the job-in-progress is done + self._q.put((ThreadFunction._TERM, None)) + self._t.join() + +# Thread timer-repeater class: Call a function every seconds +class ThreadRepeater(): + def __init__(self, f, sleep_time, name): + self.name = name + self._f = f + self._stop = False + self._sleep_time = sleep_time + self._t = threading.Thread(target=self._thread_func) + self._t.start() + + def _thread_func(self): + while True: + if self._stop: + break + try: + self._f() + except Exception as e: + logger.critical("ThreadRepeater: Got exception out of handler thread %s:\n%s" % (self.name, traceback.format_exc())) + time.sleep(self._sleep_time) + + def stop(self): + self._stop = True + self._t.join()