| #!/usr/bin/python |
| # |
| # Copyright 2010 Google Inc. All Rights Reserved. |
| |
| # System modules |
| import os.path |
| import sys |
| import traceback |
| |
| #TODO(yunlian@google.com): Use GetRoot from misc |
| def GetRoot(scr_name): |
| """Break up pathname into (dir+name).""" |
| abs_path = os.path.abspath(scr_name) |
| return (os.path.dirname(abs_path), os.path.basename(abs_path)) |
| |
| |
| class Logger(object): |
| """Logging helper class.""" |
| |
| MAX_LOG_FILES = 10 |
| |
| def __init__ (self, rootdir, basefilename, print_console, subdir="logs"): |
| logdir = os.path.join(rootdir, subdir) |
| basename = os.path.join(logdir, basefilename) |
| |
| try: |
| os.makedirs(logdir) |
| except OSError: |
| pass |
| # print "Warning: Logs directory '%s' already exists." % logdir |
| |
| self.print_console = print_console |
| |
| self._CreateLogFileHandles(basename) |
| |
| self._WriteTo(self.cmdfd, " ".join(sys.argv), True) |
| |
| def _AddSuffix(self, basename, suffix): |
| return "%s%s" % (basename, suffix) |
| |
| def _FindSuffix(self, basename): |
| timestamps = [] |
| found_suffix = None |
| for i in range(self.MAX_LOG_FILES): |
| suffix = str(i) |
| suffixed_basename = self._AddSuffix(basename, suffix) |
| cmd_file = "%s.cmd" % suffixed_basename |
| if not os.path.exists(cmd_file): |
| found_suffix = suffix |
| break |
| timestamps.append(os.stat(cmd_file).st_mtime) |
| |
| if found_suffix: |
| return found_suffix |
| |
| # Try to pick the oldest file with the suffix and return that one. |
| suffix = str(timestamps.index(min(timestamps))) |
| # print ("Warning: Overwriting log file: %s" % |
| # self._AddSuffix(basename, suffix)) |
| return suffix |
| |
| def _CreateLogFileHandle(self, name): |
| fd = None |
| try: |
| fd = open(name, "w") |
| except IOError: |
| print "Warning: could not open %s for writing." % name |
| return fd |
| |
| def _CreateLogFileHandles(self, basename): |
| suffix = self._FindSuffix(basename) |
| suffixed_basename = self._AddSuffix(basename, suffix) |
| |
| self.cmdfd = self._CreateLogFileHandle("%s.cmd" % suffixed_basename) |
| self.stdout = self._CreateLogFileHandle("%s.out" % suffixed_basename) |
| self.stderr = self._CreateLogFileHandle("%s.err" % suffixed_basename) |
| |
| self._CreateLogFileSymlinks(basename, suffixed_basename) |
| |
| # Symlink unsuffixed basename to currently suffixed one. |
| def _CreateLogFileSymlinks(self, basename, suffixed_basename): |
| try: |
| for extension in ["cmd", "out", "err"]: |
| src_file = "%s.%s" % (os.path.basename(suffixed_basename), extension) |
| dest_file = "%s.%s" % (basename, extension) |
| if os.path.exists(dest_file): |
| os.remove(dest_file) |
| os.symlink(src_file, dest_file) |
| except Exception as ex: |
| print "Exception while creating symlinks: %s" % str(ex) |
| |
| def _WriteTo(self, fd, msg, flush): |
| if fd: |
| fd.write(msg) |
| if flush: |
| fd.flush() |
| |
| def LogStartDots(self, print_to_console=True): |
| term_fd = self._GetStdout(print_to_console) |
| if (term_fd): |
| term_fd.flush() |
| term_fd.write(". ") |
| term_fd.flush() |
| |
| def LogAppendDot(self, print_to_console=True): |
| term_fd = self._GetStdout(print_to_console) |
| if (term_fd): |
| term_fd.write(". ") |
| term_fd.flush() |
| |
| def LogEndDots(self, print_to_console=True): |
| term_fd = self._GetStdout(print_to_console) |
| if (term_fd): |
| term_fd.write("\n") |
| term_fd.flush() |
| |
| def _LogMsg(self, file_fd, term_fd, msg, flush=True): |
| if file_fd: |
| self._WriteTo(file_fd, msg, flush) |
| if self.print_console: |
| self._WriteTo(term_fd, msg, flush) |
| |
| def _GetStdout(self, print_to_console): |
| if print_to_console: |
| return sys.stdout |
| return None |
| |
| def _GetStderr(self, print_to_console): |
| if print_to_console: |
| return sys.stderr |
| return None |
| |
| def LogCmdToFileOnly (self, cmd, machine="", user=None): |
| if not self.cmdfd: |
| return |
| |
| host = ("%s@%s" % (user, machine)) if user else machine |
| flush = True |
| cmd_string = "CMD (%s): %s\n" % (host, cmd) |
| self._WriteTo(self.cmdfd, cmd_string, flush) |
| |
| def LogCmd(self, cmd, machine="", user=None, print_to_console=True): |
| if user: |
| host = "%s@%s" % (user, machine) |
| else: |
| host = machine |
| |
| self._LogMsg(self.cmdfd, self._GetStdout(print_to_console), |
| "CMD (%s): %s\n" % (host, cmd)) |
| |
| def LogFatal(self, msg, print_to_console=True): |
| self._LogMsg(self.stderr, self._GetStderr(print_to_console), |
| "FATAL: %s\n" % msg) |
| self._LogMsg(self.stderr, self._GetStderr(print_to_console), |
| "\n".join(traceback.format_stack())) |
| sys.exit(1) |
| |
| def LogError(self, msg, print_to_console=True): |
| self._LogMsg(self.stderr, self._GetStderr(print_to_console), |
| "ERROR: %s\n" % msg) |
| |
| def LogWarning(self, msg, print_to_console=True): |
| self._LogMsg(self.stderr, self._GetStderr(print_to_console), |
| "WARNING: %s\n" % msg) |
| |
| def LogOutput(self, msg, print_to_console=True): |
| self._LogMsg(self.stdout, self._GetStdout(print_to_console), |
| "OUTPUT: %s\n" % msg) |
| |
| def LogFatalIf(self, condition, msg): |
| if condition: |
| self.LogFatal(msg) |
| |
| def LogErrorIf(self, condition, msg): |
| if condition: |
| self.LogError(msg) |
| |
| def LogWarningIf(self, condition, msg): |
| if condition: |
| self.LogWarning(msg) |
| |
| def LogCommandOutput(self, msg, print_to_console=True): |
| self._LogMsg(self.stdout, self._GetStdout(print_to_console), |
| msg, flush=False) |
| |
| def LogCommandError(self, msg, print_to_console=True): |
| self._LogMsg(self.stderr, self._GetStderr(print_to_console), |
| msg, flush=False) |
| |
| def Flush(self): |
| self.cmdfd.flush() |
| self.stdout.flush() |
| self.stderr.flush() |
| |
| |
| class MockLogger(object): |
| """Logging helper class.""" |
| |
| MAX_LOG_FILES = 10 |
| |
| def __init__ (self, rootdir, basefilename, print_console, subdir="logs"): |
| self.stdout = sys.stdout |
| self.stderr = sys.stderr |
| return None |
| |
| def _AddSuffix(self, basename, suffix): |
| return "%s%s" % (basename, suffix) |
| |
| def _FindSuffix(self, basename): |
| timestamps = [] |
| found_suffix = None |
| for i in range(self.MAX_LOG_FILES): |
| suffix = str(i) |
| suffixed_basename = self._AddSuffix(basename, suffix) |
| cmd_file = "%s.cmd" % suffixed_basename |
| if not os.path.exists(cmd_file): |
| found_suffix = suffix |
| break |
| timestamps.append(os.stat(cmd_file).st_mtime) |
| |
| if found_suffix: |
| return found_suffix |
| |
| # Try to pick the oldest file with the suffix and return that one. |
| suffix = str(timestamps.index(min(timestamps))) |
| # print ("Warning: Overwriting log file: %s" % |
| # self._AddSuffix(basename, suffix)) |
| return suffix |
| |
| def _CreateLogFileHandle(self, name): |
| print "MockLogger: creating open file handle for %s (writing)" % name |
| |
| def _CreateLogFileHandles(self, basename): |
| suffix = self._FindSuffix(basename) |
| suffixed_basename = self._AddSuffix(basename, suffix) |
| |
| print "MockLogger: opening file %s.cmd" % suffixed_basename |
| print "MockLogger: opening file %s.out" % suffixed_basename |
| print "MockLogger: opening file %s.err" % suffixed_basename |
| |
| self._CreateLogFileSymlinks(basename, suffixed_basename) |
| |
| # Symlink unsuffixed basename to currently suffixed one. |
| def _CreateLogFileSymlinks(self, basename, suffixed_basename): |
| for extension in ["cmd", "out", "err"]: |
| src_file = "%s.%s" % (os.path.basename(suffixed_basename), extension) |
| dest_file = "%s.%s" % (basename, extension) |
| print "MockLogger: Calling os.symlink(%s, %s)" % (src_file, dest_file) |
| |
| def _WriteTo(self, fd, msg, flush): |
| print "MockLogger: %s" % msg |
| |
| def LogStartDots(self, print_to_console=True): |
| print ". " |
| |
| |
| def LogAppendDot(self, print_to_console=True): |
| print ". " |
| |
| def LogEndDots(self, print_to_console=True): |
| print "\n" |
| |
| def _LogMsg(self, file_fd, term_fd, msg, flush=True): |
| print "MockLogger: %s" % msg |
| |
| def _GetStdout(self, print_to_console): |
| # if print_to_console: |
| # return sys.stdout |
| return None |
| |
| def _GetStderr(self, print_to_console): |
| # if print_to_console: |
| # return sys.stderr |
| return None |
| |
| def LogCmdToFileOnly (self, cmd, machine="", user=None): |
| return |
| |
| host = ("%s@%s" % (user, machine)) if user else machine |
| flush = True |
| cmd_string = "CMD (%s): %s\n" % (host, cmd) |
| print "MockLogger: Writing to file ONLY: %s" % cmd_string |
| |
| def LogCmd(self, cmd, machine="", user=None, print_to_console=True): |
| if user: |
| host = "%s@%s" % (user, machine) |
| else: |
| host = machine |
| |
| self._LogMsg(0, self._GetStdout(print_to_console), |
| "CMD (%s): %s\n" % (host, cmd)) |
| |
| def LogFatal(self, msg, print_to_console=True): |
| self._LogMsg(0, self._GetStderr(print_to_console), |
| "FATAL: %s\n" % msg) |
| self._LogMsg(0, self._GetStderr(print_to_console), |
| "\n".join(traceback.format_stack())) |
| print "MockLogger: Calling sysexit(1)" |
| |
| def LogError(self, msg, print_to_console=True): |
| self._LogMsg(0, self._GetStderr(print_to_console), |
| "ERROR: %s\n" % msg) |
| |
| def LogWarning(self, msg, print_to_console=True): |
| self._LogMsg(0, self._GetStderr(print_to_console), |
| "WARNING: %s\n" % msg) |
| |
| def LogOutput(self, msg, print_to_console=True): |
| self._LogMsg(0, self._GetStdout(print_to_console), |
| "OUTPUT: %s\n" % msg) |
| |
| def LogFatalIf(self, condition, msg): |
| if condition: |
| self.LogFatal(msg) |
| |
| def LogErrorIf(self, condition, msg): |
| if condition: |
| self.LogError(msg) |
| |
| def LogWarningIf(self, condition, msg): |
| if condition: |
| self.LogWarning(msg) |
| |
| def LogCommandOutput(self, msg, print_to_console=True): |
| self._LogMsg(self.stdout, self._GetStdout(print_to_console), |
| msg, flush=False) |
| |
| def LogCommandError(self, msg, print_to_console=True): |
| self._LogMsg(self.stderr, self._GetStderr(print_to_console), |
| msg, flush=False) |
| |
| def Flush(self): |
| print "MockLogger: Flushing cmdfd, stdout, stderr" |
| |
| |
| main_logger = None |
| |
| def InitLogger(script_name, log_dir, print_console=True, mock=False): |
| """Initialize a global logger. To be called only once.""" |
| global main_logger |
| assert not main_logger, "The logger has already been initialized" |
| rootdir, basefilename = GetRoot(script_name) |
| if not log_dir: |
| log_dir = rootdir |
| if not mock: |
| main_logger = Logger(log_dir, basefilename, print_console) |
| else: |
| main_logger = MockLogger(log_dir, basefilename, print_console) |
| |
| |
| def GetLogger(log_dir="", mock=False): |
| if not main_logger: |
| InitLogger(sys.argv[0], log_dir, mock=mock) |
| return main_logger |
| |
| |
| def HandleUncaughtExceptions(fun): |
| """Catches all exceptions that would go outside decorated fun scope.""" |
| |
| def _Interceptor(*args, **kwargs): |
| try: |
| return fun(*args, **kwargs) |
| except StandardError: |
| GetLogger().LogFatal("Uncaught exception:\n%s" % traceback.format_exc()) |
| |
| return _Interceptor |