diff options
Diffstat (limited to 'rpki/log.py')
-rw-r--r-- | rpki/log.py | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/rpki/log.py b/rpki/log.py new file mode 100644 index 00000000..c605331a --- /dev/null +++ b/rpki/log.py @@ -0,0 +1,199 @@ +# $Id$ +# +# Copyright (C) 2013--2014 Dragon Research Labs ("DRL") +# Portions copyright (C) 2009--2012 Internet Systems Consortium ("ISC") +# Portions copyright (C) 2007--2008 American Registry for Internet Numbers ("ARIN") +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notices and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND DRL, ISC, AND ARIN DISCLAIM ALL +# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DRL, +# ISC, OR ARIN BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +""" +Logging facilities for RPKI libraries. +""" + +import syslog +import sys +import os +import time +import traceback as tb + +try: + have_setproctitle = False + if os.getenv("DISABLE_SETPROCTITLE") is None: + import setproctitle + have_setproctitle = True +except ImportError: + pass + +## @var enable_trace +# Whether call tracing is enabled. + +enable_trace = False + +## @var show_python_ids +# Whether __repr__() methods should show Python id numbers + +show_python_ids = False + +## @var enable_tracebacks +# Whether tracebacks are enabled globally. Individual classes and +# modules may choose to override this. + +enable_tracebacks = False + +## @var use_setproctitle +# Whether to use setproctitle (if available) to change name shown for +# this process in ps listings (etc). + +use_setproctitle = True + +## @var proctitle_extra + +# Extra text to include in proctitle display. By default this is the +# tail of the current directory name, as this is often useful, but you +# can set it to something else if you like. If None or the empty +# string, the extra information field will be omitted from the proctitle. + +proctitle_extra = os.path.basename(os.getcwd()) + +def init(ident = "rpki", flags = syslog.LOG_PID, facility = syslog.LOG_DAEMON, use_syslog = None, log_file = sys.stderr, tag_log_lines = True): + """ + Initialize logging system. + """ + + # If caller didn't say whether to use syslog, use log file if user supplied one, otherwise use syslog + + if use_syslog is None: + use_syslog = log_file is sys.stderr + + logger.use_syslog = use_syslog + logger.tag_log_lines = tag_log_lines + + if use_syslog: + syslog.openlog(ident, flags, facility) + + else: + logger.tag = ident + logger.pid = os.getpid() + logger.log_file = log_file + + if ident and have_setproctitle and use_setproctitle: + if proctitle_extra: + setproctitle.setproctitle("%s (%s)" % (ident, proctitle_extra)) + else: + setproctitle.setproctitle(ident) + +class logger(object): + """ + Closure for logging. + """ + + use_syslog = True + tag = "" + pid = 0 + log_file = sys.stderr + + def __init__(self, priority): + self.priority = priority + + def __call__(self, message): + if self.use_syslog: + syslog.syslog(self.priority, message) + elif self.tag_log_lines: + self.log_file.write("%s %s[%d]: %s\n" % (time.strftime("%F %T"), self.tag, self.pid, message)) + self.log_file.flush() + else: + self.log_file.write(message + "\n") + self.log_file.flush() + +error = logger(syslog.LOG_ERR) +warn = logger(syslog.LOG_WARNING) +note = logger(syslog.LOG_NOTICE) +info = logger(syslog.LOG_INFO) +debug = logger(syslog.LOG_DEBUG) + + +def set_trace(enable): + """ + Enable or disable call tracing. + """ + + global enable_trace + enable_trace = enable + +def trace(): + """ + Execution trace -- where are we now, and whence came we here? + """ + + if enable_trace: + bt = tb.extract_stack(limit = 3) + return debug("[%s() at %s:%d from %s:%d]" % (bt[1][2], bt[1][0], bt[1][1], bt[0][0], bt[0][1])) + +def traceback(do_it = None): + """ + Consolidated backtrace facility with a bit of extra info. Argument + specifies whether or not to log the traceback (some modules and + classes have their own controls for this, this lets us provide a + unified interface). If no argument is specified, we use the global + default value rpki.log.enable_tracebacks. + + Assertion failures generate backtraces unconditionally, on the + theory that (a) assertion failures are programming errors by + definition, and (b) it's often hard to figure out what's triggering + a particular assertion failure without the backtrace. + """ + + if do_it is None: + do_it = enable_tracebacks + + e = sys.exc_info()[1] + assert e is not None, "rpki.log.traceback() called without valid trace on stack! This should not happen." + + if do_it or isinstance(e, AssertionError): + bt = tb.extract_stack(limit = 3) + error("Exception caught in %s() at %s:%d called from %s:%d" % (bt[1][2], bt[1][0], bt[1][1], bt[0][0], bt[0][1])) + bt = tb.format_exc() + assert bt is not None, "Apparently I'm still not using the right test for null backtrace" + for line in bt.splitlines(): + warn(line) + +def log_repr(obj, *tokens): + """ + Constructor for __repr__() strings, handles suppression of Python + IDs as needed, includes self_handle when available. + """ + + # pylint: disable=W0702 + + words = ["%s.%s" % (obj.__class__.__module__, obj.__class__.__name__)] + try: + words.append("{%s}" % obj.self.self_handle) + except: + pass + + for token in tokens: + if token is not None: + try: + s = str(token) + except: + s = "???" + debug("Failed to generate repr() string for object of type %r" % type(token)) + traceback() + if s: + words.append(s) + + if show_python_ids: + words.append(" at %#x" % id(obj)) + + return "<" + " ".join(words) + ">" |