#1
  1. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jun 2013
    Location
    Portland, Oregon
    Posts
    7
    Rep Power
    0

    How do I manage a logging Handler.


    I know this is probably basic Object handling, but I can't get my head around it.

    I have a module with multiple routines and I'm using the Python logging library to log to a file from many of them. I have it mostly working but I can't close the log file as I expect. I'm sure it is because of where I've defined my objects and classes. Here's a stripped down version of the module.

    Code:
    import ConfigParser, argparse, logging
    
    def# Global contructs needed by other functions.
    MyLog = logging.getLogger('ACLAConvert')
    parser = argparse.ArgumentParser(description='ACLA Patron Database Convertion')
    
    def main():
    
        try:
            if initializeLogging(args.loglev) != 0:
                raise RuntimeError('Failed to initialize logging. Aborting conversion.')
    
            MyLog.info('Begin ACLA Patron Database Conversion...')
    
    
            # do a bunch of other things.
    
            if cleanEnv(fnamein, gConfigParse) != 0:
                raise ValueError('Failed to clean up work files.')
    
        except ValueError as rterr:
            MyLog.error('ACLA Conversion failed. Error: %s',rterr)
            print >>sys.stderr, 'ACLA Conversion failed. Error:,',rterr
    
            msgtxt = 'ACLA Conversion failed. Error:' + str(rterr)+ '\r\n'
            rtnStat = sendEmailMsg(msgtxt, gConfigParse)
            if rtnStat != 0:
                MyLog.error('Failed to send email message. Return Status: %d',rtnStat)
            allStatus = 1
        except RuntimeError as rterr:
            print >>sys.stderr, 'ACLA Conversion failed. Error:,',rterr
            
        finally:
            fh.close()
    
        return allStatus
        
    
    def initializeLogging(pLogLevel):
        """ assuming loglevel is bound to the string value obtained from the
            command line argument. Convert to upper case to allow the user to
            specify --log=DEBUG or --log=debug
        """
        numeric_level = getattr(logging, pLogLevel.upper(), None)
        try:
            if not isinstance(numeric_level, int):
                raise ValueError('Invalid log level: %s, %s' % (pLogLevel, numeric_level))
    
            fh = logging.FileHandler('logfile.log')
    
            MyLog.setLevel(numeric_level)
            formatter = logging.Formatter('%(asctime)s - %(funcName)-15s - %(levelname)-7s - %(message)s')
            fh.setFormatter(formatter)
            MyLog.addHandler(fh)
    
        except ValueError  as err:
            print  >>sys.stderr, 'Failed to enable logging.',err
            return 1
    
        return 0
        
    if __name__ == '__main__':
        main()
    The way this is structured, the call fh.Close() fails because the fh is defined in the initializeLogging routine. I did have it defined as a global, but I need place it here (or someplace else) where I can add code to pass a different filename to the FileHandler() method name with a path that is derived from some other data.

    Questions:
    1. How to structure this to allow me to create a FileHandler where I determine the log file name at runtime depending on other data?
    2. How do I structure this so I close the FileHandler before exiting? I know files will be closed when the process exits, but in the Python Shell, the log file stays open when the module exits.
    3. Is there a better way overall that I should be structuring the logging?

    Thanks for your help,

    John
  2. #2
  3. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Dec 2012
    Posts
    114
    Rep Power
    3
    Look up contextlib and context managers. They are the correct solution when you want a function call to do something both before and after a block of code is executed.

    (Also, why are you using a C-style error code return value for one of your errors when you clearly know about exceptions and use them for everything else?)
  4. #3
  5. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jun 2013
    Location
    Portland, Oregon
    Posts
    7
    Rep Power
    0
    Originally Posted by Nyktos
    Look up contextlib and context managers. They are the correct solution when you want a function call to do something both before and after a block of code is executed.

    (Also, why are you using a C-style error code return value for one of your errors when you clearly know about exceptions and use them for everything else?)
    Maybe I'm not seeing the total picture. Or haven't completely made the transition to OOP as you noted in my code. The context managers seems to operate in conjunction with a WITH block. The places that I'm currently wanting to reference the fh=logging.FileHandler object don't seem to lend themselves to being structured within a WITH block.

    I decided to 'return fh' the object from the initializeLogging routine so the 'Main' code could reference it.

    You are correct about the return codes structure. I was reluctant to raise an exception that would cause an exit from the subroutine and the need to handling the exception in the Main line. But I see now that that works. There are other cases (that I didn't show) where I don't want the called routine to branch to the Main's main Exception handler. Of course, I can bracket those calls with a local try/exception block.
  6. #4
  7. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Dec 2012
    Posts
    114
    Rep Power
    3
    Originally Posted by DoctorBrown
    Maybe I'm not seeing the total picture. Or haven't completely made the transition to OOP as you noted in my code. The context managers seems to operate in conjunction with a WITH block. The places that I'm currently wanting to reference the fh=logging.FileHandler object don't seem to lend themselves to being structured within a WITH block.
    Why not?

    You're using a try/finally now. with is essentially the same as try except that the except or finally clauses are somewhere else in the code.
    Last edited by Nyktos; June 15th, 2013 at 12:16 PM.

IMN logo majestic logo threadwatch logo seochat tools logo