Home | History | Annotate | Download | only in cros_utils
      1 # Copyright 2010 Google Inc. All Rights Reserved.
      2 """Logging helper module."""
      3 
      4 from __future__ import print_function
      5 
      6 # System modules
      7 import os.path
      8 import sys
      9 import traceback
     10 
     11 
     12 #TODO(yunlian (at] google.com): Use GetRoot from misc
     13 def GetRoot(scr_name):
     14   """Break up pathname into (dir+name)."""
     15   abs_path = os.path.abspath(scr_name)
     16   return (os.path.dirname(abs_path), os.path.basename(abs_path))
     17 
     18 
     19 class Logger(object):
     20   """Logging helper class."""
     21 
     22   MAX_LOG_FILES = 10
     23 
     24   def __init__(self, rootdir, basefilename, print_console, subdir='logs'):
     25     logdir = os.path.join(rootdir, subdir)
     26     basename = os.path.join(logdir, basefilename)
     27 
     28     try:
     29       os.makedirs(logdir)
     30     except OSError:
     31       pass
     32       # print("Warning: Logs directory '%s' already exists." % logdir)
     33 
     34     self.print_console = print_console
     35 
     36     self._CreateLogFileHandles(basename)
     37 
     38     self._WriteTo(self.cmdfd, ' '.join(sys.argv), True)
     39 
     40   def _AddSuffix(self, basename, suffix):
     41     return '%s%s' % (basename, suffix)
     42 
     43   def _FindSuffix(self, basename):
     44     timestamps = []
     45     found_suffix = None
     46     for i in range(self.MAX_LOG_FILES):
     47       suffix = str(i)
     48       suffixed_basename = self._AddSuffix(basename, suffix)
     49       cmd_file = '%s.cmd' % suffixed_basename
     50       if not os.path.exists(cmd_file):
     51         found_suffix = suffix
     52         break
     53       timestamps.append(os.stat(cmd_file).st_mtime)
     54 
     55     if found_suffix:
     56       return found_suffix
     57 
     58     # Try to pick the oldest file with the suffix and return that one.
     59     suffix = str(timestamps.index(min(timestamps)))
     60     # print ("Warning: Overwriting log file: %s" %
     61     #       self._AddSuffix(basename, suffix))
     62     return suffix
     63 
     64   def _CreateLogFileHandle(self, name):
     65     fd = None
     66     try:
     67       fd = open(name, 'w')
     68     except IOError:
     69       print('Warning: could not open %s for writing.' % name)
     70     return fd
     71 
     72   def _CreateLogFileHandles(self, basename):
     73     suffix = self._FindSuffix(basename)
     74     suffixed_basename = self._AddSuffix(basename, suffix)
     75 
     76     self.cmdfd = self._CreateLogFileHandle('%s.cmd' % suffixed_basename)
     77     self.stdout = self._CreateLogFileHandle('%s.out' % suffixed_basename)
     78     self.stderr = self._CreateLogFileHandle('%s.err' % suffixed_basename)
     79 
     80     self._CreateLogFileSymlinks(basename, suffixed_basename)
     81 
     82   # Symlink unsuffixed basename to currently suffixed one.
     83   def _CreateLogFileSymlinks(self, basename, suffixed_basename):
     84     try:
     85       for extension in ['cmd', 'out', 'err']:
     86         src_file = '%s.%s' % (os.path.basename(suffixed_basename), extension)
     87         dest_file = '%s.%s' % (basename, extension)
     88         if os.path.exists(dest_file):
     89           os.remove(dest_file)
     90         os.symlink(src_file, dest_file)
     91     except Exception as ex:
     92       print('Exception while creating symlinks: %s' % str(ex))
     93 
     94   def _WriteTo(self, fd, msg, flush):
     95     if fd:
     96       fd.write(msg)
     97       if flush:
     98         fd.flush()
     99 
    100   def LogStartDots(self, print_to_console=True):
    101     term_fd = self._GetStdout(print_to_console)
    102     if term_fd:
    103       term_fd.flush()
    104       term_fd.write('. ')
    105       term_fd.flush()
    106 
    107   def LogAppendDot(self, print_to_console=True):
    108     term_fd = self._GetStdout(print_to_console)
    109     if term_fd:
    110       term_fd.write('. ')
    111       term_fd.flush()
    112 
    113   def LogEndDots(self, print_to_console=True):
    114     term_fd = self._GetStdout(print_to_console)
    115     if term_fd:
    116       term_fd.write('\n')
    117       term_fd.flush()
    118 
    119   def LogMsg(self, file_fd, term_fd, msg, flush=True):
    120     if file_fd:
    121       self._WriteTo(file_fd, msg, flush)
    122     if self.print_console:
    123       self._WriteTo(term_fd, msg, flush)
    124 
    125   def _GetStdout(self, print_to_console):
    126     if print_to_console:
    127       return sys.stdout
    128     return None
    129 
    130   def _GetStderr(self, print_to_console):
    131     if print_to_console:
    132       return sys.stderr
    133     return None
    134 
    135   def LogCmdToFileOnly(self, cmd, machine='', user=None):
    136     if not self.cmdfd:
    137       return
    138 
    139     host = ('%s@%s' % (user, machine)) if user else machine
    140     flush = True
    141     cmd_string = 'CMD (%s): %s\n' % (host, cmd)
    142     self._WriteTo(self.cmdfd, cmd_string, flush)
    143 
    144   def LogCmd(self, cmd, machine='', user=None, print_to_console=True):
    145     if user:
    146       host = '%s@%s' % (user, machine)
    147     else:
    148       host = machine
    149 
    150     self.LogMsg(self.cmdfd, self._GetStdout(print_to_console),
    151                 'CMD (%s): %s\n' % (host, cmd))
    152 
    153   def LogFatal(self, msg, print_to_console=True):
    154     self.LogMsg(self.stderr, self._GetStderr(print_to_console),
    155                 'FATAL: %s\n' % msg)
    156     self.LogMsg(self.stderr, self._GetStderr(print_to_console),
    157                 '\n'.join(traceback.format_stack()))
    158     sys.exit(1)
    159 
    160   def LogError(self, msg, print_to_console=True):
    161     self.LogMsg(self.stderr, self._GetStderr(print_to_console),
    162                 'ERROR: %s\n' % msg)
    163 
    164   def LogWarning(self, msg, print_to_console=True):
    165     self.LogMsg(self.stderr, self._GetStderr(print_to_console),
    166                 'WARNING: %s\n' % msg)
    167 
    168   def LogOutput(self, msg, print_to_console=True):
    169     self.LogMsg(self.stdout, self._GetStdout(print_to_console),
    170                 'OUTPUT: %s\n' % msg)
    171 
    172   def LogFatalIf(self, condition, msg):
    173     if condition:
    174       self.LogFatal(msg)
    175 
    176   def LogErrorIf(self, condition, msg):
    177     if condition:
    178       self.LogError(msg)
    179 
    180   def LogWarningIf(self, condition, msg):
    181     if condition:
    182       self.LogWarning(msg)
    183 
    184   def LogCommandOutput(self, msg, print_to_console=True):
    185     self.LogMsg(self.stdout,
    186                 self._GetStdout(print_to_console),
    187                 msg,
    188                 flush=False)
    189 
    190   def LogCommandError(self, msg, print_to_console=True):
    191     self.LogMsg(self.stderr,
    192                 self._GetStderr(print_to_console),
    193                 msg,
    194                 flush=False)
    195 
    196   def Flush(self):
    197     self.cmdfd.flush()
    198     self.stdout.flush()
    199     self.stderr.flush()
    200 
    201 
    202 class MockLogger(object):
    203   """Logging helper class."""
    204 
    205   MAX_LOG_FILES = 10
    206 
    207   def __init__(self, *_args, **_kwargs):
    208     self.stdout = sys.stdout
    209     self.stderr = sys.stderr
    210     return None
    211 
    212   def _AddSuffix(self, basename, suffix):
    213     return '%s%s' % (basename, suffix)
    214 
    215   def _FindSuffix(self, basename):
    216     timestamps = []
    217     found_suffix = None
    218     for i in range(self.MAX_LOG_FILES):
    219       suffix = str(i)
    220       suffixed_basename = self._AddSuffix(basename, suffix)
    221       cmd_file = '%s.cmd' % suffixed_basename
    222       if not os.path.exists(cmd_file):
    223         found_suffix = suffix
    224         break
    225       timestamps.append(os.stat(cmd_file).st_mtime)
    226 
    227     if found_suffix:
    228       return found_suffix
    229 
    230     # Try to pick the oldest file with the suffix and return that one.
    231     suffix = str(timestamps.index(min(timestamps)))
    232     # print ("Warning: Overwriting log file: %s" %
    233     #       self._AddSuffix(basename, suffix))
    234     return suffix
    235 
    236   def _CreateLogFileHandle(self, name):
    237     print('MockLogger: creating open file handle for %s (writing)' % name)
    238 
    239   def _CreateLogFileHandles(self, basename):
    240     suffix = self._FindSuffix(basename)
    241     suffixed_basename = self._AddSuffix(basename, suffix)
    242 
    243     print('MockLogger: opening file %s.cmd' % suffixed_basename)
    244     print('MockLogger: opening file %s.out' % suffixed_basename)
    245     print('MockLogger: opening file %s.err' % suffixed_basename)
    246 
    247     self._CreateLogFileSymlinks(basename, suffixed_basename)
    248 
    249   # Symlink unsuffixed basename to currently suffixed one.
    250   def _CreateLogFileSymlinks(self, basename, suffixed_basename):
    251     for extension in ['cmd', 'out', 'err']:
    252       src_file = '%s.%s' % (os.path.basename(suffixed_basename), extension)
    253       dest_file = '%s.%s' % (basename, extension)
    254       print('MockLogger: Calling os.symlink(%s, %s)' % (src_file, dest_file))
    255 
    256   def _WriteTo(self, _fd, msg, _flush):
    257     print('MockLogger: %s' % msg)
    258 
    259   def LogStartDots(self, _print_to_console=True):
    260     print('. ')
    261 
    262   def LogAppendDot(self, _print_to_console=True):
    263     print('. ')
    264 
    265   def LogEndDots(self, _print_to_console=True):
    266     print('\n')
    267 
    268   def LogMsg(self, _file_fd, _term_fd, msg, **_kwargs):
    269     print('MockLogger: %s' % msg)
    270 
    271   def _GetStdout(self, _print_to_console):
    272     return None
    273 
    274   def _GetStderr(self, _print_to_console):
    275     return None
    276 
    277   def LogCmdToFileOnly(self, *_args, **_kwargs):
    278     return
    279 
    280   # def LogCmdToFileOnly(self, cmd, machine='', user=None):
    281   #   host = ('%s@%s' % (user, machine)) if user else machine
    282   #   cmd_string = 'CMD (%s): %s\n' % (host, cmd)
    283   #   print('MockLogger: Writing to file ONLY: %s' % cmd_string)
    284 
    285   def LogCmd(self, cmd, machine='', user=None, print_to_console=True):
    286     if user:
    287       host = '%s@%s' % (user, machine)
    288     else:
    289       host = machine
    290 
    291     self.LogMsg(0, self._GetStdout(print_to_console),
    292                 'CMD (%s): %s\n' % (host, cmd))
    293 
    294   def LogFatal(self, msg, print_to_console=True):
    295     self.LogMsg(0, self._GetStderr(print_to_console), 'FATAL: %s\n' % msg)
    296     self.LogMsg(0, self._GetStderr(print_to_console),
    297                 '\n'.join(traceback.format_stack()))
    298     print('MockLogger: Calling sysexit(1)')
    299 
    300   def LogError(self, msg, print_to_console=True):
    301     self.LogMsg(0, self._GetStderr(print_to_console), 'ERROR: %s\n' % msg)
    302 
    303   def LogWarning(self, msg, print_to_console=True):
    304     self.LogMsg(0, self._GetStderr(print_to_console), 'WARNING: %s\n' % msg)
    305 
    306   def LogOutput(self, msg, print_to_console=True):
    307     self.LogMsg(0, self._GetStdout(print_to_console), 'OUTPUT: %s\n' % msg)
    308 
    309   def LogFatalIf(self, condition, msg):
    310     if condition:
    311       self.LogFatal(msg)
    312 
    313   def LogErrorIf(self, condition, msg):
    314     if condition:
    315       self.LogError(msg)
    316 
    317   def LogWarningIf(self, condition, msg):
    318     if condition:
    319       self.LogWarning(msg)
    320 
    321   def LogCommandOutput(self, msg, print_to_console=True):
    322     self.LogMsg(self.stdout,
    323                 self._GetStdout(print_to_console),
    324                 msg,
    325                 flush=False)
    326 
    327   def LogCommandError(self, msg, print_to_console=True):
    328     self.LogMsg(self.stderr,
    329                 self._GetStderr(print_to_console),
    330                 msg,
    331                 flush=False)
    332 
    333   def Flush(self):
    334     print('MockLogger: Flushing cmdfd, stdout, stderr')
    335 
    336 
    337 main_logger = None
    338 
    339 
    340 def InitLogger(script_name, log_dir, print_console=True, mock=False):
    341   """Initialize a global logger. To be called only once."""
    342   # pylint: disable=global-statement
    343   global main_logger
    344   assert not main_logger, 'The logger has already been initialized'
    345   rootdir, basefilename = GetRoot(script_name)
    346   if not log_dir:
    347     log_dir = rootdir
    348   if not mock:
    349     main_logger = Logger(log_dir, basefilename, print_console)
    350   else:
    351     main_logger = MockLogger(log_dir, basefilename, print_console)
    352 
    353 
    354 def GetLogger(log_dir='', mock=False):
    355   if not main_logger:
    356     InitLogger(sys.argv[0], log_dir, mock=mock)
    357   return main_logger
    358 
    359 
    360 def HandleUncaughtExceptions(fun):
    361   """Catches all exceptions that would go outside decorated fun scope."""
    362 
    363   def _Interceptor(*args, **kwargs):
    364     try:
    365       return fun(*args, **kwargs)
    366     except StandardError:
    367       GetLogger().LogFatal('Uncaught exception:\n%s' % traceback.format_exc())
    368 
    369   return _Interceptor
    370