? LOG-README ? build ? duplicity-foo ? testing/logtest.py Index: duplicity-bin =================================================================== RCS file: /sources/duplicity/duplicity/duplicity-bin,v retrieving revision 1.39 diff -r1.39 duplicity-bin 443a444 > log.setup() 510a512 > log.shutdown() Index: duplicity/collections.py =================================================================== RCS file: /sources/duplicity/duplicity/duplicity/collections.py,v retrieving revision 1.27 diff -r1.27 collections.py 552,553c552,554 < log.Log("Warning, found the following orphaned signature files:\n" < + "\n".join(self.orphaned_sig_names), 2) --- > log.Warn("Warning, found the following orphaned signature files:\n" > + "\n".join(self.orphaned_sig_names), > log.WarningCode.orphaned_sig) 556c557,558 < log.Log("Warning, found unnecessary signature chain(s)", 2) --- > log.Warn("Warning, found unnecessary signature chain(s)", > log.WarningCode.unnecessary_sig) 558,559c560,561 < log.Log("Warning, found signatures but no corresponding " < "backup files", 2) --- > log.Warn("Warning, found signatures but no corresponding " > "backup files", log.WarningCode.unmatched_sig) 562,563c564,565 < log.Log("Warning, found incomplete backup sets, probably left " < "from aborted session", 2) --- > log.Warn("Warning, found incomplete backup sets, probably left " > "from aborted session", log.WarningCode.incomplete_backup) 565,567c567,570 < log.Log("Warning, found the following orphaned backup files:\n" < + "\n".join(map(lambda x: str(x), < self.orphaned_backup_sets)), 2) --- > log.Warn("Warning, found the following orphaned backup files:\n" > + "\n".join(map(lambda x: str(x), > self.orphaned_backup_sets)), > log.WarningCode.orphaned_backup) Index: duplicity/commandline.py =================================================================== RCS file: /sources/duplicity/duplicity/duplicity/commandline.py,v retrieving revision 1.49 diff -r1.49 commandline.py 82a83,84 > "log-fd=", > "log-file=", 220c222 < sys.exit(1); --- > sys.exit(0); 231a234,246 > elif opt == "--log-fd": > log_fd = int(arg) > if log_fd < 1: > command_line_error("log-fd must be greater than zero.") > try: > log.add_fd(log_fd) > except: > command_line_error("Cannot write to log-fd %s." % arg) > elif opt == "--log-file": > try: > log.add_file(arg) > except: > command_line_error("Cannot write to log-file %s." % arg) 265c280,283 < log.setverbosity(int(arg)) --- > verb = int(arg) > if verb < 0 or verb > 9: > command_line_error("verbosity must be between 0 and 9.") > log.setverbosity(verb) 278,280c296,298 < sys.stderr.write("Command line error: %s\n" % (message,)) < sys.stderr.write("Enter 'duplicity --help' for help screen.\n") < sys.exit(1) --- > log.FatalError("Command line error: %s\n" > "Enter 'duplicity --help' for help screen.""" % (message,), > log.ErrorCode.command_line) 344a363,364 > --log-fd > --log-file Index: duplicity/gpg.py =================================================================== RCS file: /sources/duplicity/duplicity/duplicity/gpg.py,v retrieving revision 1.16 diff -r1.16 gpg.py 153c153 < if log.verbosity >= 5: --- > if log.getverbosity() >= 5: Index: duplicity/log.py =================================================================== RCS file: /sources/duplicity/duplicity/duplicity/log.py,v retrieving revision 1.7 diff -r1.7 log.py 1a2 > # Copyright 2008 Michael Terry 20a22 > import os 21a24 > import logging 23,24c26,44 < verbosity = 3 < termverbosity = 3 --- > MIN = 0 > ERROR = 0 > WARNING = 2 > NOTICE = 3 > INFO = 5 > DEBUG = 9 > MAX = 9 > > _logger = None > > def DupToLoggerLevel(verb): > """Convert duplicity level to the logging module's system, where higher is > more severe""" > return MAX - verb + 1 > > def LoggerToDupLevel(verb): > """Convert logging module level to duplicity's system, where lowere is > more severe""" > return DupToLoggerLevel(verb) 26c46 < def Log(s, verb_level): --- > def Log(s, verb_level, code=1): 28,34c48,53 < if verb_level <= termverbosity: < if verb_level <= 2: < sys.stderr.write(s + "\n") < sys.stderr.flush() < else: < sys.stdout.write(s + "\n") < sys.stdout.flush() --- > global _logger > # currentCode is a terrible hack until duplicity depends on Python 2.5 > # and its logging 'extra' keyword that allows a custom record dictionary. > _logger.currentCode = code > _logger.log(DupToLoggerLevel(verb_level), s) > _logger.currentCode = 1 38c57 < Log(s, 9) --- > Log(s, DEBUG) 42c61 < Log(s, 5) --- > Log(s, INFO) 46c65 < Log(s, 3) --- > Log(s, NOTICE) 48c67,78 < def Warn(s): --- > class WarningCode: > """Enumeration class to hold warning code values. > These values should never change, as frontends rely upon them. > Don't use 0 or negative numbers.""" > generic = 1 > orphaned_sig = 2 > unnecessary_sig = 3 > unmatched_sig = 4 > incomplete_backup = 5 > orphaned_backup = 6 > > def Warn(s, code=WarningCode.generic): 50c80,89 < Log(s, 2) --- > Log(s, WARNING, code) > > class ErrorCode: > """Enumeration class to hold error code values. > These values should never change, as frontends rely upon them. > Don't use 0 or negative numbers. This code is returned by duplicity > to indicate which error occurred via both exit code and log.""" > generic = 1 > command_line = 2 > source_mismatch = 3 52c91 < def FatalError(s): --- > def FatalError(s, code=ErrorCode.generic): 54,56c93,187 < sys.stderr.write(s + "\n") < sys.stderr.flush() < sys.exit(1) --- > Log(s, ERROR, code) > sys.exit(code) > > class DupLogRecord(logging.LogRecord): > """Custom log record that holds a message code""" > def __init__(self, code, *args, **kwargs): > global _logger > logging.LogRecord.__init__(self, *args, **kwargs) > self.code = code > > class DupLogger(logging.Logger): > """Custom logger that creates special code-bearing records""" > # currentCode is a terrible hack until duplicity depends on Python 2.5 > # and its logging 'extra' keyword that allows a custom record dictionary. > currentCode = 1 > def makeRecord(self, name, lvl, fn, lno, msg, args, exc_info, *argv, **kwargs): > return DupLogRecord(self.currentCode, name, lvl, fn, lno, msg, args, exc_info) > > class OutFilter(logging.Filter): > """Filter that only allows warning or less important messages""" > def filter(self, record): > return record.levelno <= DupToLoggerLevel(WARNING) > > class ErrFilter(logging.Filter): > """Filter that only allows messages more important than warnings""" > def filter(self, record): > return record.levelno > DupToLoggerLevel(WARNING) > > def setup(): > """Initialize logging""" > global _logger > if _logger: > return > > logging.setLoggerClass(DupLogger) > _logger = logging.getLogger("duplicity") > > # Set up our special level names > logging.addLevelName(DupToLoggerLevel(ERROR), "ERROR") > logging.addLevelName(DupToLoggerLevel(WARNING), "WARNING") > logging.addLevelName(DupToLoggerLevel(NOTICE), "NOTICE") > logging.addLevelName(DupToLoggerLevel(INFO), "INFO") > logging.addLevelName(DupToLoggerLevel(DEBUG), "DEBUG") > > # Default verbosity allows notices and above > setverbosity(NOTICE) > > # stdout and stderr are for different logging levels > outHandler = logging.StreamHandler(sys.stdout) > outHandler.addFilter(OutFilter()) > _logger.addHandler(outHandler) > > errHandler = logging.StreamHandler(sys.stderr) > errHandler.addFilter(ErrFilter()) > _logger.addHandler(errHandler) > > class MachineFormatter(logging.Formatter): > """Formatter that creates messages in a syntax easily consumable by other > processes.""" > def __init__(self): > logging.Formatter.__init__(self, "%(levelname)s %(code)s\n%(message)s") > > def format(self, record): > s = logging.Formatter.format(self, record) > # Indent each extra line with a dot and space so that the consumer > # knows it's a continuation, not a new message. Add a newline so > # consumers know the message is over. > return s.replace('\n', '\n. ') + '\n' > > class MachineFilter(logging.Filter): > """Filter that only allows levels that are consumable by other processes.""" > def filter(self, record): > # We only want to allow records that have level names. If the level > # does not have an associated name, there will be a space as the > # logging module expands levelname to "Level %d". This will confuse > # consumers. Even if we dropped the space, random levels may not > # mean anything to consumers. > s = logging.getLevelName(record.levelno) > return s.find(' ') == -1 > > def add_fd(fd): > """Add stream to which to write machine-readable logging""" > global _logger > handler = logging.StreamHandler(os.fdopen(fd, 'w')) > handler.setFormatter(MachineFormatter()) > handler.addFilter(MachineFilter()) > _logger.addHandler(handler) > > def add_file(filename): > """Add file to which to write machine-readable logging""" > global _logger > handler = logging.FileHandler(filename, 'w') > handler.setFormatter(MachineFormatter()) > handler.addFilter(MachineFilter()) > _logger.addHandler(handler) 58c189 < def setverbosity(verb, termverb = None): --- > def setverbosity(verb): 60,63c191,202 < global verbosity, termverbosity < verbosity = verb < if termverb: termverbosity = termverb < else: termverbosity = verb --- > global _logger > _logger.setLevel(DupToLoggerLevel(verb)) > > def getverbosity(): > """Get the verbosity level""" > global _logger > return LoggerToDupLevel(_logger.getEffectiveLevel()) > > def shutdown(): > """Cleanup and flush loggers""" > logging.shutdown() > Index: duplicity/manifest.py =================================================================== RCS file: /sources/duplicity/duplicity/duplicity/manifest.py,v retrieving revision 1.4 diff -r1.4 manifest.py 65c65 < --allow-source-mismatch switch to avoid seeing this message""") --- > --allow-source-mismatch switch to avoid seeing this message""", log.ErrorCode.source_mismatch) Index: testing/run-all-tests.sh =================================================================== RCS file: /sources/duplicity/duplicity/testing/run-all-tests.sh,v retrieving revision 1.7 diff -r1.7 run-all-tests.sh 13a14 > logtest.py \