| #!/usr/bin/python |
| |
| #---------------------------------------------------------------------- |
| # Copyright (c) 2008 Board of Trustees, Princeton University |
| # |
| # Permission is hereby granted, free of charge, to any person obtaining |
| # a copy of this software and/or hardware specification (the "Work") to |
| # deal in the Work without restriction, including without limitation the |
| # rights to use, copy, modify, merge, publish, distribute, sublicense, |
| # and/or sell copies of the Work, and to permit persons to whom the Work |
| # is furnished to do so, subject to the following conditions: |
| # |
| # The above copyright notice and this permission notice shall be |
| # included in all copies or substantial portions of the Work. |
| # |
| # THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
| # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
| # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
| # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| # OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS |
| # IN THE WORK. |
| #---------------------------------------------------------------------- |
| |
| import os, sys |
| import traceback |
| import logging, logging.handlers |
| |
| CRITICAL=logging.CRITICAL |
| ERROR=logging.ERROR |
| WARNING=logging.WARNING |
| INFO=logging.INFO |
| DEBUG=logging.DEBUG |
| |
| # a logger that can handle tracebacks |
| class Logger: |
| def __init__ (self,logfile=None,loggername=None,level=logging.INFO): |
| # default is to locate loggername from the logfile if avail. |
| if not logfile: |
| #loggername='console' |
| #handler=logging.StreamHandler() |
| #handler.setFormatter(logging.Formatter("%(levelname)s %(message)s")) |
| logfile = "/var/log/planetstack.log" |
| |
| if not loggername: |
| loggername=os.path.basename(logfile) |
| try: |
| handler=logging.handlers.RotatingFileHandler(logfile,maxBytes=1000000, backupCount=5) |
| except IOError: |
| # This is usually a permissions error becaue the file is |
| # owned by root, but httpd is trying to access it. |
| tmplogfile=os.getenv("TMPDIR", "/tmp") + os.path.sep + os.path.basename(logfile) |
| # In strange uses, 2 users on same machine might use same code, |
| # meaning they would clobber each others files |
| # We could (a) rename the tmplogfile, or (b) |
| # just log to the console in that case. |
| # Here we default to the console. |
| if os.path.exists(tmplogfile) and not os.access(tmplogfile,os.W_OK): |
| loggername = loggername + "-console" |
| handler = logging.StreamHandler() |
| else: |
| handler=logging.handlers.RotatingFileHandler(tmplogfile,maxBytes=1000000, backupCount=5) |
| handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")) |
| self.logger=logging.getLogger(loggername) |
| self.logger.setLevel(level) |
| # check if logger already has the handler we're about to add |
| handler_exists = False |
| for l_handler in self.logger.handlers: |
| if l_handler.baseFilename == handler.baseFilename and \ |
| l_handler.level == handler.level: |
| handler_exists = True |
| |
| if not handler_exists: |
| self.logger.addHandler(handler) |
| |
| self.loggername=loggername |
| |
| def setLevel(self,level): |
| self.logger.setLevel(level) |
| |
| # shorthand to avoid having to import logging all over the place |
| def setLevelDebug(self): |
| self.logger.setLevel(logging.DEBUG) |
| |
| def debugEnabled (self): |
| return self.logger.getEffectiveLevel() == logging.DEBUG |
| |
| # define a verbose option with s/t like |
| # parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0) |
| # and pass the coresponding options.verbose to this method to adjust level |
| def setLevelFromOptVerbose(self,verbose): |
| if verbose==0: |
| self.logger.setLevel(logging.WARNING) |
| elif verbose==1: |
| self.logger.setLevel(logging.INFO) |
| elif verbose>=2: |
| self.logger.setLevel(logging.DEBUG) |
| # in case some other code needs a boolean |
| def getBoolVerboseFromOpt(self,verbose): |
| return verbose>=1 |
| def getBoolDebugFromOpt(self,verbose): |
| return verbose>=2 |
| |
| #################### |
| def info(self, msg): |
| self.logger.info(msg) |
| |
| def debug(self, msg): |
| self.logger.debug(msg) |
| |
| def warn(self, msg): |
| self.logger.warn(msg) |
| |
| # some code is using logger.warn(), some is using logger.warning() |
| def warning(self, msg): |
| self.logger.warning(msg) |
| |
| def error(self, msg): |
| self.logger.error(msg) |
| |
| def critical(self, msg): |
| self.logger.critical(msg) |
| |
| # logs an exception - use in an except statement |
| def log_exc(self,message): |
| self.error("%s BEG TRACEBACK"%message+"\n"+traceback.format_exc().strip("\n")) |
| self.error("%s END TRACEBACK"%message) |
| |
| def log_exc_critical(self,message): |
| self.critical("%s BEG TRACEBACK"%message+"\n"+traceback.format_exc().strip("\n")) |
| self.critical("%s END TRACEBACK"%message) |
| |
| # for investigation purposes, can be placed anywhere |
| def log_stack(self,message): |
| to_log="".join(traceback.format_stack()) |
| self.info("%s BEG STACK"%message+"\n"+to_log) |
| self.info("%s END STACK"%message) |
| |
| def enable_console(self, stream=sys.stdout): |
| formatter = logging.Formatter("%(message)s") |
| handler = logging.StreamHandler(stream) |
| handler.setFormatter(formatter) |
| self.logger.addHandler(handler) |
| |
| |
| info_logger = Logger(loggername='info', level=logging.INFO) |
| debug_logger = Logger(loggername='debug', level=logging.DEBUG) |
| warn_logger = Logger(loggername='warning', level=logging.WARNING) |
| error_logger = Logger(loggername='error', level=logging.ERROR) |
| critical_logger = Logger(loggername='critical', level=logging.CRITICAL) |
| logger = info_logger |
| ######################################## |
| import time |
| |
| def profile(logger): |
| """ |
| Prints the runtime of the specified callable. Use as a decorator, e.g., |
| |
| @profile(logger) |
| def foo(...): |
| ... |
| """ |
| def logger_profile(callable): |
| def wrapper(*args, **kwds): |
| start = time.time() |
| result = callable(*args, **kwds) |
| end = time.time() |
| args = map(str, args) |
| args += ["%s = %s" % (name, str(value)) for (name, value) in kwds.iteritems()] |
| # should probably use debug, but then debug is not always enabled |
| logger.info("PROFILED %s (%s): %.02f s" % (callable.__name__, ", ".join(args), end - start)) |
| return result |
| return wrapper |
| return logger_profile |
| |
| |
| if __name__ == '__main__': |
| print 'testing logging into logger.log' |
| logger1=Logger('logger.log', loggername='std(info)') |
| logger2=Logger('logger.log', loggername='error', level=logging.ERROR) |
| logger3=Logger('logger.log', loggername='debug', level=logging.DEBUG) |
| |
| for (logger,msg) in [ (logger1,"std(info)"),(logger2,"error"),(logger3,"debug")]: |
| |
| print "====================",msg, logger.logger.handlers |
| |
| logger.enable_console() |
| logger.critical("logger.critical") |
| logger.error("logger.error") |
| logger.warn("logger.warning") |
| logger.info("logger.info") |
| logger.debug("logger.debug") |
| logger.setLevel(logging.DEBUG) |
| logger.debug("logger.debug again") |
| |
| @profile(logger) |
| def sleep(seconds = 1): |
| time.sleep(seconds) |
| |
| logger.info('console.info') |
| sleep(0.5) |
| logger.setLevel(logging.DEBUG) |
| sleep(0.25) |
| |