How to duplicate sys.stdout to a log file?

前端 未结 17 926
醉酒成梦
醉酒成梦 2020-11-22 06:54

Edit: Since it appears that there\'s either no solution, or I\'m doing something so non-standard that nobody knows - I\'ll revise my question to also ask: What is the best w

17条回答
  •  旧时难觅i
    2020-11-22 07:50

    I'm writing a script to run cmd-line scripts. ( Because in some cases, there just is no viable substitute for a Linux command -- such as the case of rsync. )

    What I really wanted was to use the default python logging mechanism in every case where it was possible to do so, but to still capture any error when something went wrong that was unanticipated.

    This code seems to do the trick. It may not be particularly elegant or efficient ( although it doesn't use string+=string, so at least it doesn't have that particular potential bottle- neck ). I'm posting it in case it gives someone else any useful ideas.

    import logging
    import os, sys
    import datetime
    
    # Get name of module, use as application name
    try:
      ME=os.path.split(__file__)[-1].split('.')[0]
    except:
      ME='pyExec_'
    
    LOG_IDENTIFIER="uuu___( o O )___uuu "
    LOG_IDR_LENGTH=len(LOG_IDENTIFIER)
    
    class PyExec(object):
    
      # Use this to capture all possible error / output to log
      class SuperTee(object):
          # Original reference: http://mail.python.org/pipermail/python-list/2007-May/442737.html
          def __init__(self, name, mode):
              self.fl = open(name, mode)
              self.fl.write('\n')
              self.stdout = sys.stdout
              self.stdout.write('\n')
              self.stderr = sys.stderr
    
              sys.stdout = self
              sys.stderr = self
    
          def __del__(self):
              self.fl.write('\n')
              self.fl.flush()
              sys.stderr = self.stderr
              sys.stdout = self.stdout
              self.fl.close()
    
          def write(self, data):
              # If the data to write includes the log identifier prefix, then it is already formatted
              if data[0:LOG_IDR_LENGTH]==LOG_IDENTIFIER:
                self.fl.write("%s\n" % data[LOG_IDR_LENGTH:])
                self.stdout.write(data[LOG_IDR_LENGTH:])
    
              # Otherwise, we can give it a timestamp
              else:
    
                timestamp=str(datetime.datetime.now())
                if 'Traceback' == data[0:9]:
                  data='%s: %s' % (timestamp, data)
                  self.fl.write(data)
                else:
                  self.fl.write(data)
    
                self.stdout.write(data)
    
    
      def __init__(self, aName, aCmd, logFileName='', outFileName=''):
    
        # Using name for 'logger' (context?), which is separate from the module or the function
        baseFormatter=logging.Formatter("%(asctime)s \t %(levelname)s \t %(name)s:%(module)s:%(lineno)d \t %(message)s")
        errorFormatter=logging.Formatter(LOG_IDENTIFIER + "%(asctime)s \t %(levelname)s \t %(name)s:%(module)s:%(lineno)d \t %(message)s")
    
        if logFileName:
          # open passed filename as append
          fl=logging.FileHandler("%s.log" % aName)
        else:
          # otherwise, use log filename as a one-time use file
          fl=logging.FileHandler("%s.log" % aName, 'w')
    
        fl.setLevel(logging.DEBUG)
        fl.setFormatter(baseFormatter)
    
        # This will capture stdout and CRITICAL and beyond errors
    
        if outFileName:
          teeFile=PyExec.SuperTee("%s_out.log" % aName)
        else:
          teeFile=PyExec.SuperTee("%s_out.log" % aName, 'w')
    
        fl_out=logging.StreamHandler( teeFile )
        fl_out.setLevel(logging.CRITICAL)
        fl_out.setFormatter(errorFormatter)
    
        # Set up logging
        self.log=logging.getLogger('pyExec_main')
        log=self.log
    
        log.addHandler(fl)
        log.addHandler(fl_out)
    
        print "Test print statement."
    
        log.setLevel(logging.DEBUG)
    
        log.info("Starting %s", ME)
        log.critical("Critical.")
    
        # Caught exception
        try:
          raise Exception('Exception test.')
        except Exception,e:
          log.exception(str(e))
    
        # Uncaught exception
        a=2/0
    
    
    PyExec('test_pyExec',None)
    

    Obviously, if you're not as subject to whimsy as I am, replace LOG_IDENTIFIER with another string that you're not like to ever see someone write to a log.

提交回复
热议问题