Commit 12a6f2db authored by bescoto's avatar bescoto

Added error logging code


git-svn-id: http://svn.savannah.nongnu.org/svn/rdiff-backup@280 2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109
parent 19b43534
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
"""Manage logging, displaying and recording messages with required verbosity""" """Manage logging, displaying and recording messages with required verbosity"""
import time, sys, traceback, types import time, sys, traceback, types
import Globals import Globals, static
class LoggerError(Exception): pass class LoggerError(Exception): pass
...@@ -183,3 +183,65 @@ class Logger: ...@@ -183,3 +183,65 @@ class Logger:
Log = Logger() Log = Logger()
class ErrorLog:
"""Log each recoverable error in error_log file
There are three types of recoverable errors: ListError, which
happens trying to list a directory or stat a file, UpdateError,
which happen when trying to update a changed file, and
SpecialFileError, which happen when a special file cannot be
created. See the error policy file for more info.
"""
log_fileobj = None
log_inc_rp = None
def open(cls, compress = 1):
"""Open the error log, prepare for writing"""
assert not cls.log_fileobj and not cls.log_inc_rp, "log already open"
if compress: typestr = 'data.gz'
else: typestr = 'data'
cls.log_inc_rp = Global.rbdir.append("error_log.%s.%s" %
(Time.curtimestr, typestr))
assert not cls.log_inc_rp.lstat(), "Error file already exists"
cls.log_fileobj = cls.log_inc_rp.open("wb", compress = compress)
def isopen(cls):
"""True if the error log file is currently open"""
return cls.log_fileobj is not None
def write(cls, error_type, rp, exc):
"""Add line to log file indicating error exc with file rp"""
s = cls.get_log_string(error_type, rp, exc)
Log(s, 2)
if Globals.null_separator: s += "\0"
else:
s = re.sub("\n", " ", s)
s += "\n"
cls.log_fileobj.write(s)
def get_indexpath(cls, rp):
"""Return filename for logging. rp is a rpath, string, or tuple"""
try: return rp.get_indexpath()
except AttributeError:
if type(rp) is types.TupleTypes: return "/".join(rp)
else: return str(rp)
def write_if_open(cls, error_type, rp, exc):
"""Call cls.write(...) if error log open, only log otherwise"""
if cls.isopen(): cls.write(error_type, rp, exc)
else: Log(cls.get_log_string(error_type, rp, exc), 2)
def get_log_string(cls, error_type, rp, exc):
"""Return log string to put in error log"""
assert (error_type == "ListError" or error_type == "UpdateError" or
error_type == "SpecialFileError"), "Unknown type "+error_type
return "%s %s %s" % (error_type, cls.get_indexpath(rp), str(exc))
def close(cls):
"""Close the error log file"""
assert not cls.log_fileobj.close()
cls.log_fileobj = cls.log_inc_rp = None
static.MakeClass(ErrorLog)
...@@ -26,8 +26,7 @@ documentation on what this code does can be found on the man page. ...@@ -26,8 +26,7 @@ documentation on what this code does can be found on the man page.
from __future__ import generators from __future__ import generators
import re import re
from log import Log import FilenameMapping, robust, rpath, Globals, log
import FilenameMapping, robust, rpath, Globals
class SelectError(Exception): class SelectError(Exception):
...@@ -87,7 +86,7 @@ class Select: ...@@ -87,7 +86,7 @@ class Select:
self.rpath = rootrp self.rpath = rootrp
self.prefix = self.rpath.path self.prefix = self.rpath.path
def set_iter(self, iterate_parents = None, sel_func = None): def set_iter(self, sel_func = None):
"""Initialize more variables, get ready to iterate """Initialize more variables, get ready to iterate
Selection function sel_func is called on each rpath and is Selection function sel_func is called on each rpath and is
...@@ -96,17 +95,24 @@ class Select: ...@@ -96,17 +95,24 @@ class Select:
""" """
if not sel_func: sel_func = self.Select if not sel_func: sel_func = self.Select
self.rpath.setdata() # this may have changed since Select init self.rpath.setdata() # this may have changed since Select init
self.iter = self.Iterate_fast(self.rpath, sel_func) self.iter = self.filter_readable(self.Iterate_fast(self.rpath,
sel_func))
# only iterate parents if we are not starting from beginning
self.next = self.iter.next self.next = self.iter.next
self.__iter__ = lambda: self self.__iter__ = lambda: self
return self return self
def filter_readable(self, rp_iter):
"""Yield rps in iter except the unreadable regular files"""
for rp in rp_iter:
if not rp.isreg() or rp.readable(): yield rp
else: log.ErrorLog.write_if_open("ListError", rp,
"Regular file lacks read permissions")
def Iterate_fast(self, rpath, sel_func): def Iterate_fast(self, rpath, sel_func):
"""Like Iterate, but don't recur, saving time""" """Like Iterate, but don't recur, saving time"""
def error_handler(exc, filename): def error_handler(exc, filename):
Log("Error initializing file %s/%s" % (rpath.path, filename), 2) log.ErrorLog.write_if_open("ListError",
rpath.index + (filename,), exc)
return None return None
def diryield(rpath): def diryield(rpath):
...@@ -117,7 +123,7 @@ class Select: ...@@ -117,7 +123,7 @@ class Select:
and should be included iff something inside is included. and should be included iff something inside is included.
""" """
for filename in robust.listrp(rpath): for filename in self.listdir(rpath):
new_rpath = robust.check_common_error(error_handler, new_rpath = robust.check_common_error(error_handler,
rpath.append, (filename,)) rpath.append, (filename,))
if new_rpath: if new_rpath:
...@@ -174,13 +180,23 @@ class Select: ...@@ -174,13 +180,23 @@ class Select:
for rp in iid: yield rp for rp in iid: yield rp
else: assert 0, "Invalid selection result %s" % (str(s),) else: assert 0, "Invalid selection result %s" % (str(s),)
def listdir(self, dir_rp):
"""List directory rpath with error logging"""
def error_handler(exc):
log.ErrorLog.write_if_open("ListError", dir_rp, exc)
return []
dir_listing = robust.check_common_error(error_handler, dir_rp.listdir)
dir_listing.sort()
return dir_listing
def iterate_in_dir(self, rpath, rec_func, sel_func): def iterate_in_dir(self, rpath, rec_func, sel_func):
"""Iterate the rpaths in directory rpath.""" """Iterate the rpaths in directory rpath."""
def error_handler(exc, filename): def error_handler(exc, filename):
Log("Error initializing file %s/%s" % (rpath.path, filename), 2) log.ErrorLog.write_if_open("ListError",
rpath.index + (filename,), exc)
return None return None
for filename in robust.listrp(rpath): for filename in self.listdir(rpath):
new_rp = robust.check_common_error( new_rp = robust.check_common_error(
error_handler, rpath.append, [filename]) error_handler, rpath.append, [filename])
if new_rp: if new_rp:
...@@ -251,7 +267,7 @@ class Select: ...@@ -251,7 +267,7 @@ class Select:
def parse_catch_error(self, exc): def parse_catch_error(self, exc):
"""Deal with selection error exc""" """Deal with selection error exc"""
if isinstance(exc, FilePrefixError): if isinstance(exc, FilePrefixError):
Log.FatalError( log.Log.FatalError(
"""Fatal Error: The file specification """Fatal Error: The file specification
' %s' ' %s'
cannot match any files in the base directory cannot match any files in the base directory
...@@ -260,8 +276,8 @@ Useful file specifications begin with the base directory or some ...@@ -260,8 +276,8 @@ Useful file specifications begin with the base directory or some
pattern (such as '**') which matches the base directory.""" % pattern (such as '**') which matches the base directory.""" %
(exc, self.prefix)) (exc, self.prefix))
elif isinstance(e, GlobbingError): elif isinstance(e, GlobbingError):
Log.FatalError("Fatal Error while processing expression\n" log.Log.FatalError("Fatal Error while processing expression\n"
"%s" % exc) "%s" % exc)
else: raise else: raise
def parse_rbdir_exclude(self): def parse_rbdir_exclude(self):
...@@ -273,7 +289,7 @@ pattern (such as '**') which matches the base directory.""" % ...@@ -273,7 +289,7 @@ pattern (such as '**') which matches the base directory.""" %
"""Exit with error if last selection function isn't an exclude""" """Exit with error if last selection function isn't an exclude"""
if (self.selection_functions and if (self.selection_functions and
not self.selection_functions[-1].exclude): not self.selection_functions[-1].exclude):
Log.FatalError( log.Log.FatalError(
"""Last selection expression: """Last selection expression:
%s %s
only specifies that files be included. Because the default is to only specifies that files be included. Because the default is to
...@@ -296,10 +312,10 @@ probably isn't what you meant.""" % ...@@ -296,10 +312,10 @@ probably isn't what you meant.""" %
filelist_name is just a string used for logging. filelist_name is just a string used for logging.
""" """
Log("Reading filelist %s" % filelist_name, 4) log.Log("Reading filelist %s" % filelist_name, 4)
tuple_list, something_excluded = \ tuple_list, something_excluded = \
self.filelist_read(filelist_fp, inc_default, filelist_name) self.filelist_read(filelist_fp, inc_default, filelist_name)
Log("Sorting filelist %s" % filelist_name, 4) log.Log("Sorting filelist %s" % filelist_name, 4)
tuple_list.sort() tuple_list.sort()
i = [0] # We have to put index in list because of stupid scoping rules i = [0] # We have to put index in list because of stupid scoping rules
...@@ -324,11 +340,11 @@ probably isn't what you meant.""" % ...@@ -324,11 +340,11 @@ probably isn't what you meant.""" %
"""Warn if prefix is incorrect""" """Warn if prefix is incorrect"""
prefix_warnings[0] += 1 prefix_warnings[0] += 1
if prefix_warnings[0] < 6: if prefix_warnings[0] < 6:
Log("Warning: file specification '%s' in filelist %s\n" log.Log("Warning: file specification '%s' in filelist %s\n"
"doesn't start with correct prefix %s. Ignoring." % "doesn't start with correct prefix %s. Ignoring." %
(exc, filelist_name, self.prefix), 2) (exc, filelist_name, self.prefix), 2)
if prefix_warnings[0] == 5: if prefix_warnings[0] == 5:
Log("Future prefix errors will not be logged.", 2) log.Log("Future prefix errors will not be logged.", 2)
something_excluded, tuple_list = None, [] something_excluded, tuple_list = None, []
separator = Globals.null_separator and "\0" or "\n" separator = Globals.null_separator and "\0" or "\n"
...@@ -341,7 +357,7 @@ probably isn't what you meant.""" % ...@@ -341,7 +357,7 @@ probably isn't what you meant.""" %
tuple_list.append(tuple) tuple_list.append(tuple)
if not tuple[1]: something_excluded = 1 if not tuple[1]: something_excluded = 1
if filelist_fp.close(): if filelist_fp.close():
Log("Error closing filelist %s" % filelist_name, 2) log.Log("Error closing filelist %s" % filelist_name, 2)
return (tuple_list, something_excluded) return (tuple_list, something_excluded)
def filelist_parse_line(self, line, include): def filelist_parse_line(self, line, include):
...@@ -399,7 +415,7 @@ probably isn't what you meant.""" % ...@@ -399,7 +415,7 @@ probably isn't what you meant.""" %
See the man page on --[include/exclude]-globbing-filelist See the man page on --[include/exclude]-globbing-filelist
""" """
Log("Reading globbing filelist %s" % list_name, 4) log.Log("Reading globbing filelist %s" % list_name, 4)
separator = Globals.null_separator and "\0" or "\n" separator = Globals.null_separator and "\0" or "\n"
for line in filelist_fp.read().split(separator): for line in filelist_fp.read().split(separator):
if not line: continue # skip blanks if not line: continue # skip blanks
...@@ -423,7 +439,7 @@ probably isn't what you meant.""" % ...@@ -423,7 +439,7 @@ probably isn't what you meant.""" %
assert include == 0 or include == 1 assert include == 0 or include == 1
try: regexp = re.compile(regexp_string) try: regexp = re.compile(regexp_string)
except: except:
Log("Error compiling regular expression %s" % regexp_string, 1) log.Log("Error compiling regular expression %s" % regexp_string, 1)
raise raise
def sel_func(rp): def sel_func(rp):
...@@ -437,8 +453,8 @@ probably isn't what you meant.""" % ...@@ -437,8 +453,8 @@ probably isn't what you meant.""" %
def devfiles_get_sf(self, include): def devfiles_get_sf(self, include):
"""Return a selection function matching all dev files""" """Return a selection function matching all dev files"""
if self.selection_functions: if self.selection_functions:
Log("Warning: exclude-device-files is not the first " log.Log("Warning: exclude-device-files is not the first "
"selector.\nThis may not be what you intended", 3) "selector.\nThis may not be what you intended", 3)
def sel_func(rp): def sel_func(rp):
if rp.isdev(): return include if rp.isdev(): return include
else: return None else: return None
...@@ -449,8 +465,8 @@ probably isn't what you meant.""" % ...@@ -449,8 +465,8 @@ probably isn't what you meant.""" %
def special_get_sf(self, include): def special_get_sf(self, include):
"""Return sel function matching sockets, symlinks, sockets, devs""" """Return sel function matching sockets, symlinks, sockets, devs"""
if self.selection_functions: if self.selection_functions:
Log("Warning: exclude-special-files is not the first " log.Log("Warning: exclude-special-files is not the first "
"selector.\nThis may not be what you intended", 3) "selector.\nThis may not be what you intended", 3)
def sel_func(rp): def sel_func(rp):
if rp.issym() or rp.issock() or rp.isfifo() or rp.isdev(): if rp.issym() or rp.issock() or rp.isfifo() or rp.isdev():
return include return include
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment