import repository from arizona
[raven.git] / our-sfa-build / sfa / util / sfalogging.py
1 #!/usr/bin/python
2
3 import os, sys
4 import traceback
5 import logging, logging.handlers
6
7 CRITICAL=logging.CRITICAL
8 ERROR=logging.ERROR
9 WARNING=logging.WARNING
10 INFO=logging.INFO
11 DEBUG=logging.DEBUG
12
13 # a logger that can handle tracebacks 
14 class _SfaLogger:
15     def __init__ (self,logfile=None,loggername=None,level=logging.INFO):
16         # default is to locate loggername from the logfile if avail.
17         if not logfile:
18             loggername='console'
19             handler=logging.StreamHandler()
20             handler.setFormatter(logging.Formatter("%(levelname)s %(message)s"))
21         else:
22             if not loggername:
23                 loggername=os.path.basename(logfile)
24             try:
25                 handler=logging.handlers.RotatingFileHandler(logfile,maxBytes=1000000, backupCount=5) 
26             except IOError:
27                 # This is usually a permissions error becaue the file is
28                 # owned by root, but httpd is trying to access it.
29                 tmplogfile=os.getenv("TMPDIR", "/tmp") + os.path.sep + os.path.basename(logfile)
30                 # In strange uses, 2 users on same machine might use same code,
31                 # meaning they would clobber each others files
32                 # We could (a) rename the tmplogfile, or (b)
33                 # just log to the console in that case.
34                 # Here we default to the console.
35                 if os.path.exists(tmplogfile) and not os.access(tmplogfile,os.W_OK):
36                     loggername = loggername + "-console"
37                     handler = logging.StreamHandler()
38                 else:
39                     handler=logging.handlers.RotatingFileHandler(tmplogfile,maxBytes=1000000, backupCount=5) 
40             handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
41
42         self.logger=logging.getLogger(loggername)
43         self.logger.setLevel(level)
44         self.logger.addHandler(handler)
45         self.loggername=loggername
46
47     def setLevel(self,level):
48         self.logger.setLevel(level)
49
50     # shorthand to avoid having to import logging all over the place
51     def setLevelDebug(self):
52         self.logger.setLevel(logging.DEBUG)
53
54     # define a verbose option with s/t like
55     # parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0)
56     # and pass the coresponding options.verbose to this method to adjust level
57     def setLevelFromOptVerbose(self,verbose):
58         if verbose==0:
59             self.logger.setLevel(logging.WARNING)
60         elif verbose==1:
61             self.logger.setLevel(logging.INFO)
62         elif verbose==2:
63             self.logger.setLevel(logging.DEBUG)
64
65     ####################
66     def wrap(fun):
67         def wrapped(self,msg,*args,**kwds):
68             native=getattr(self.logger,fun.__name__)
69             return native(msg,*args,**kwds)
70         #wrapped.__doc__=native.__doc__
71         return wrapped
72
73     @wrap
74     def critical(): pass
75     @wrap
76     def error(): pass
77     @wrap
78     def warning(): pass
79     @wrap
80     def info(): pass
81     @wrap
82     def debug(): pass
83     
84     # logs an exception - use in an except statement
85     def log_exc(self,message):
86         self.error("%s BEG TRACEBACK"%message+"\n"+traceback.format_exc().strip("\n"))
87         self.error("%s END TRACEBACK"%message)
88     
89     def log_exc_critical(self,message):
90         self.critical("%s BEG TRACEBACK"%message+"\n"+traceback.format_exc().strip("\n"))
91         self.critical("%s END TRACEBACK"%message)
92     
93     # for investigation purposes, can be placed anywhere
94     def log_stack(self,message):
95         to_log="".join(traceback.format_stack())
96         self.debug("%s BEG STACK"%message+"\n"+to_log)
97         self.debug("%s END STACK"%message)
98
99 ####################
100 # import-related operations go in this file
101 _import_logger=_SfaLogger(logfile='/var/log/sfa_import.log')
102 # servers log into /var/log/sfa.log
103 _server_logger=_SfaLogger(logfile='/var/log/sfa.log')
104 # clients use the console
105 _console_logger=_SfaLogger()
106
107 # default is to use the server-side logger
108 _the_logger=_server_logger
109
110 # clients would change the default by issuing one of these call
111 def sfa_logger_goes_to_console():
112     current_module=sys.modules[globals()['__name__']]
113     current_module._the_logger=_console_logger
114
115 # clients would change the default by issuing one of these call
116 def sfa_logger_goes_to_import():
117     current_module=sys.modules[globals()['__name__']]
118     current_module._logger=_import_logger
119
120 # this is how to retrieve the 'right' logger
121 def sfa_logger():
122     return _the_logger
123
124 ########################################
125 import time
126
127 def profile(logger):
128     """
129     Prints the runtime of the specified callable. Use as a decorator, e.g.,
130     
131     @profile(logger)
132     def foo(...):
133         ...
134     """
135     def logger_profile(callable):
136         def wrapper(*args, **kwds):
137             start = time.time()
138             result = callable(*args, **kwds)
139             end = time.time()
140             args = map(str, args)
141             args += ["%s = %s" % (name, str(value)) for (name, value) in kwds.items()]
142             # should probably use debug, but then debug is not always enabled
143             logger.info("PROFILED %s (%s): %.02f s" % (callable.__name__, ", ".join(args), end - start))
144             return result
145         return wrapper
146     return logger_profile
147
148
149 if __name__ == '__main__': 
150     print 'testing sfalogging into logger.log'
151     logger=_SfaLogger('logger.log')
152     logger.critical("logger.critical")
153     logger.error("logger.error")
154     logger.warning("logger.warning")
155     logger.info("logger.info")
156     logger.debug("logger.debug")
157     logger.setLevel(logging.DEBUG)
158     logger.debug("logger.debug again")
159     
160     sfa_logger_goes_to_console()
161     my_logger=sfa_logger()
162     my_logger.info("redirected to console")
163
164     @profile(my_logger)
165     def sleep(seconds = 1):
166         time.sleep(seconds)
167
168     my_logger.info('console.info')
169     sleep(0.5)
170     my_logger.setLevel(logging.DEBUG)
171     sleep(0.25)
172