removed stratum nonce hacks
[p2pool.git] / wstools / logging.py
1 # Copyright (c) 2003, The Regents of the University of California,
2 # through Lawrence Berkeley National Laboratory (subject to receipt of
3 # any required approvals from the U.S. Dept. of Energy).  All rights
4 # reserved. 
5 #
6 """Logging"""
7 ident = "$Id$"
8 import os, sys
9
10 WARN = 1
11 DEBUG = 2
12
13
14 class ILogger:
15     '''Logger interface, by default this class
16     will be used and logging calls are no-ops.
17     '''
18     level = 0
19     def __init__(self, msg):
20         return
21     def warning(self, *args, **kw):
22         return
23     def debug(self, *args, **kw):
24         return
25     def error(self, *args, **kw):
26         return
27     def setLevel(cls, level):
28         cls.level = level
29     setLevel = classmethod(setLevel)
30     
31     debugOn = lambda self: self.level >= DEBUG
32     warnOn = lambda self: self.level >= WARN
33     
34
35 class BasicLogger(ILogger):
36     last = ''
37     
38     def __init__(self, msg, out=sys.stdout):
39         self.msg, self.out = msg, out
40
41     def warning(self, msg, *args, **kw):
42         if self.warnOn() is False: return
43         if BasicLogger.last != self.msg:
44             BasicLogger.last = self.msg
45             print >>self, "---- ", self.msg, " ----"
46         print >>self, "    %s  " %self.WARN,
47         print >>self, msg %args
48     WARN = '[WARN]'
49     def debug(self, msg, *args, **kw):
50         if self.debugOn() is False: return
51         if BasicLogger.last != self.msg:
52             BasicLogger.last = self.msg
53             print >>self, "---- ", self.msg, " ----"
54         print >>self, "    %s  " %self.DEBUG,
55         print >>self, msg %args
56     DEBUG = '[DEBUG]'
57     def error(self, msg, *args, **kw):
58         if BasicLogger.last != self.msg:
59             BasicLogger.last = self.msg
60             print >>self, "---- ", self.msg, " ----"
61         print >>self, "    %s  " %self.ERROR,
62         print >>self, msg %args
63     ERROR = '[ERROR]'
64
65     def write(self, *args):
66         '''Write convenience function; writes strings.
67         '''
68         for s in args: self.out.write(s)
69         event = ''.join(*args)
70
71
72 _LoggerClass = BasicLogger
73
74 class GridLogger(ILogger):
75     def debug(self, msg, *args, **kw):
76         kw['component'] = self.msg
77         gridLog(event=msg %args, level='DEBUG', **kw)
78
79     def warning(self, msg, *args, **kw):
80         kw['component'] = self.msg
81         gridLog(event=msg %args, level='WARNING', **kw)
82
83     def error(self, msg, *args, **kw):
84         kw['component'] = self.msg
85         gridLog(event=msg %args, level='ERROR', **kw)
86
87
88
89 # Registry of send functions for gridLog
90
91 GLRegistry = {}
92
93 class GLRecord(dict):
94     """Grid Logging Best Practices Record, Distributed Logging Utilities
95
96     The following names are reserved:
97
98     event -- log event name
99         Below is EBNF for the event name part of a log message.
100             name        = <nodot> ( "." <name> )? 
101             nodot       = {RFC3896-chars except "."}
102
103         Suffixes:
104             start: Immediately before the first action in a task.
105             end: Immediately after the last action in a task (that succeeded).
106             error: an error condition that does not correspond to an end event.
107
108     ts -- timestamp
109     level -- logging level (see levels below)
110     status -- integer status code
111     gid -- global grid identifier 
112     gid, cgid -- parent/child identifiers
113     prog -- program name
114
115
116     More info: http://www.cedps.net/wiki/index.php/LoggingBestPractices#Python
117
118     reserved -- list of reserved names, 
119     omitname -- list of reserved names, output only values ('ts', 'event',)
120     levels -- dict of levels and description
121     """
122     reserved = ('ts', 'event', 'level', 'status', 'gid', 'prog')
123     omitname = ()
124     levels = dict(FATAL='Component cannot continue, or system is unusable.',
125         ALERT='Action must be taken immediately.',
126         CRITICAL='Critical conditions (on the system).',
127         ERROR='Errors in the component; not errors from elsewhere.',
128         WARNING='Problems that are recovered from, usually.',
129         NOTICE='Normal but significant condition.',
130         INFO='Informational messages that would be useful to a deployer or administrator.',
131         DEBUG='Lower level information concerning program logic decisions, internal state, etc.',
132         TRACE='Finest granularity, similar to "stepping through" the component or system.',
133     )
134
135     def __init__(self, date=None, **kw):
136         kw['ts'] = date or self.GLDate()
137         kw['gid'] = kw.get('gid') or os.getpid()
138         dict.__init__(self, kw)
139
140     def __str__(self):
141         """
142         """
143         from cStringIO import StringIO
144         s = StringIO(); n = " "
145         reserved = self.reserved; omitname = self.omitname; levels = self.levels
146
147         for k in ( list(filter(lambda i: self.has_key(i), reserved)) + 
148             list(filter(lambda i: i not in reserved, self.keys()))
149         ):
150             v = self[k]
151             if k in omitname: 
152                 s.write( "%s " %self.format[type(v)](v) )
153                 continue
154
155             if k == reserved[2] and v not in levels:
156                 pass
157
158             s.write( "%s=%s " %(k, self.format[type(v)](v) ) )
159
160         s.write("\n")
161         return s.getvalue()
162
163     class GLDate(str):
164         """Grid logging Date Format
165         all timestamps should all be in the same time zone (UTC). 
166         Grid timestamp value format that is a highly readable variant of the ISO8601 time standard [1]:
167
168         YYYY-MM-DDTHH:MM:SS.SSSSSSZ 
169
170         """
171         def __new__(self, args=None):
172             """args -- datetime (year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])
173             """
174             import datetime
175             args = args or datetime.datetime.utcnow()
176             l = (args.year, args.month, args.day, args.hour, args.minute, args.second, 
177                  args.microsecond, args.tzinfo or 'Z')
178
179             return str.__new__(self, "%04d-%02d-%02dT%02d:%02d:%02d.%06d%s" %l)
180
181     format = { int:str, float:lambda x: "%lf" % x, long:str, str:lambda x:x,
182         unicode:str, GLDate:str, }
183
184
185 def gridLog(**kw):
186     """Send GLRecord, Distributed Logging Utilities
187     If the scheme is passed as a keyword parameter
188     the value is expected to be a callable function
189     that takes 2 parameters: url, outputStr
190
191     GRIDLOG_ON   -- turn grid logging on
192     GRIDLOG_DEST -- provide URL destination
193     """
194     import os
195
196     if not bool( int(os.environ.get('GRIDLOG_ON', 0)) ):
197         return
198
199     url = os.environ.get('GRIDLOG_DEST')
200     if url is None: 
201         return
202
203     ## NOTE: urlparse problem w/customized schemes 
204     try:
205         scheme = url[:url.find('://')]
206         send = GLRegistry[scheme]
207         send( url, str(GLRecord(**kw)), )
208     except Exception, ex:
209         print >>sys.stderr, "*** gridLog failed -- %s" %(str(kw))
210
211
212 def sendUDP(url, outputStr):
213     from socket import socket, AF_INET, SOCK_DGRAM
214     idx1 = url.find('://') + 3; idx2 = url.find('/', idx1)
215     if idx2 < idx1: idx2 = len(url)
216     netloc = url[idx1:idx2]
217     host,port = (netloc.split(':')+[80])[0:2]
218     socket(AF_INET, SOCK_DGRAM).sendto( outputStr, (host,int(port)), )
219
220 def writeToFile(url, outputStr):
221     print >> open(url.split('://')[1], 'a+'), outputStr
222
223 GLRegistry["gridlog-udp"] = sendUDP
224 GLRegistry["file"] = writeToFile
225
226
227 def setBasicLogger():
228     '''Use Basic Logger. 
229     '''
230     setLoggerClass(BasicLogger)
231     BasicLogger.setLevel(0)
232
233 def setGridLogger():
234     '''Use GridLogger for all logging events.
235     '''
236     setLoggerClass(GridLogger)
237
238 def setBasicLoggerWARN():
239     '''Use Basic Logger.
240     '''
241     setLoggerClass(BasicLogger)
242     BasicLogger.setLevel(WARN)
243
244 def setBasicLoggerDEBUG():
245     '''Use Basic Logger.
246     '''
247     setLoggerClass(BasicLogger)
248     BasicLogger.setLevel(DEBUG)
249
250 def setLoggerClass(loggingClass):
251     '''Set Logging Class.
252     '''
253
254 def setLoggerClass(loggingClass):
255     '''Set Logging Class.
256     '''
257     assert issubclass(loggingClass, ILogger), 'loggingClass must subclass ILogger'
258     global _LoggerClass
259     _LoggerClass = loggingClass
260
261 def setLevel(level=0):
262     '''Set Global Logging Level.
263     '''
264     ILogger.level = level
265
266 def getLevel():
267     return ILogger.level
268
269 def getLogger(msg):
270     '''Return instance of Logging class.
271     '''
272     return _LoggerClass(msg)
273
274