root / trunk / twisted / web2 / log.py

Revision 17544, 5.8 kB (checked in by dreid, 3 years ago)

Docstrings for various userfacing methods and classes.

This also contains a few extra lines for readability sake.

Reviewer: MFen
Author: dreid
Fixes #1919

Line 
1 # -*- test-case-name: twisted.web2.test.test_log -*-
2 # Copyright (c) 2001-2004 Twisted Matrix Laboratories.
3 # See LICENSE for details.
4
5 """Logging tools. This is still in flux (even moreso than the rest of web2)."""
6
7 import time
8 from twisted.python import log as tlog
9 from twisted.internet import defer
10 from twisted.web2 import iweb, stream, resource
11 from zope.interface import implements, Attribute, Interface
12
13 class _LogByteCounter(object):
14     implements(stream.IByteStream)
15    
16     def __init__(self, stream, done):
17         self.stream=stream
18         self.done=done
19         self.len=0
20        
21     length=property(lambda self: self.stream.length)
22    
23     def _callback(self, data):
24         if data is None:
25             if self.done:
26                 done=self.done; self.done=None
27                 done(True, self.len)
28         else:
29             self.len += len(data)
30         return data
31    
32     def read(self):
33         data = self.stream.read()
34         if isinstance(data, defer.Deferred):
35             return data.addCallback(self._callback)
36         return self._callback(data)
37    
38     def close(self):
39         if self.done:
40             done=self.done; self.done=None
41             done(False, self.len)
42         self.stream.close()
43
44    
45 class ILogInfo(Interface):
46     """Auxilliary information about the response useful for logging."""
47    
48     bytesSent=Attribute("Number of bytes sent.")
49     responseCompleted=Attribute("Whether or not the response was completed.")
50     secondsTaken=Attribute("Number of seconds taken to serve the request.")
51     startTime=Attribute("Time at which the request started")
52
53    
54 class LogInfo(object):
55     implements(ILogInfo)
56
57     responseCompleted=None
58     secondsTaken=None
59     bytesSent=None
60     startTime=None
61
62    
63 def logFilter(request, response, startTime=None):
64     if startTime is None:
65         startTime = time.time()
66        
67     def _log(success, length):
68         loginfo=LogInfo()
69         loginfo.bytesSent=length
70         loginfo.responseCompleted=success
71         loginfo.secondsTaken=time.time()-startTime
72        
73         tlog.msg(interface=iweb.IRequest, request=request, response=response,
74                  loginfo=loginfo)
75         # Or just...
76         # ILogger(ctx).log(...) ?
77
78     if response.stream:
79         response.stream=_LogByteCounter(response.stream, _log)
80     else:
81         _log(True, 0)
82
83     return response
84
85 logFilter.handleErrors = True
86
87
88 class LogWrapperResource(resource.WrapperResource):
89     def hook(self, request):
90         # Insert logger
91         request.addResponseFilter(logFilter, atEnd=True)
92
93 monthname = [None, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
94              'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
95
96
97 class BaseCommonAccessLoggingObserver(object):
98     """An abstract Twisted-based logger for creating access logs.
99
100     Derived implementations of this class *must* implement the
101     ``logMessage(message)`` method, which will send the message to an actual
102     log/file or stream.
103     """
104
105     logFormat = '%s - %s [%s] "%s" %s %d "%s" "%s"'
106     def logMessage(self, message):
107         raise NotImplemented, 'You must provide an implementation.'
108
109     def computeTimezoneForLog(self, tz):
110         if tz > 0:
111             neg = 1
112         else:
113             neg = 0
114             tz = -tz
115         h, rem = divmod(tz, 3600)
116         m, rem = divmod(rem, 60)
117         if neg:
118             return '-%02d%02d' % (h, m)
119         else:
120             return '+%02d%02d' % (h, m)
121
122     tzForLog = None
123     tzForLogAlt = None
124
125     def logDateString(self, when):
126         logtime = time.localtime(when)
127         Y, M, D, h, m, s = logtime[:6]
128        
129         if not time.daylight:
130             tz = self.tzForLog
131             if tz is None:
132                 tz = self.computeTimezoneForLog(time.timezone)
133                 self.tzForLog = tz
134         else:
135             tz = self.tzForLogAlt
136             if tz is None:
137                 tz = self.computeTimezoneForLog(time.altzone)
138                 self.tzForLogAlt = tz
139
140         return '%02d/%s/%02d:%02d:%02d:%02d %s' % (
141             D, monthname[M], Y, h, m, s, tz)
142
143     def emit(self, eventDict):
144         if eventDict.get('interface') is not iweb.IRequest:
145             return
146
147         request = eventDict['request']
148         response = eventDict['response']
149         loginfo = eventDict['loginfo']
150         firstLine = '%s %s HTTP/%s' %(
151             request.method,
152             request.uri,
153             '.'.join([str(x) for x in request.clientproto]))
154        
155         self.logMessage(
156             '%s - %s [%s] "%s" %s %d "%s" "%s"' %(
157                 request.remoteAddr.host,
158                 # XXX: Where to get user from?
159                 "-",
160                 self.logDateString(
161                     response.headers.getHeader('date', 0)),
162                 firstLine,
163                 response.code,
164                 loginfo.bytesSent,
165                 request.headers.getHeader('referer', '-'),
166                 request.headers.getHeader('user-agent', '-')
167                 )
168             )
169
170     def start(self):
171         """Start observing log events."""
172         tlog.addObserver(self.emit)
173
174     def stop(self):
175         """Stop observing log events."""
176         tlog.removeObserver(self.emit)
177
178
179 class FileAccessLoggingObserver(BaseCommonAccessLoggingObserver):
180     """I log requests to a single logfile
181     """
182    
183     def __init__(self, logpath):
184         self.logpath = logpath
185                
186     def logMessage(self, message):
187         self.f.write(message + '\n')
188
189     def start(self):
190         super(FileAccessLoggingObserver, self).start()
191         self.f = open(self.logpath, 'a', 1)
192        
193     def stop(self):
194         super(FileAccessLoggingObserver, self).stop()
195         self.f.close()
196
197                
198 class DefaultCommonAccessLoggingObserver(BaseCommonAccessLoggingObserver):
199     """Log requests to default twisted logfile."""
200     def logMessage(self, message):
201         tlog.msg(message)
Note: See TracBrowser for help on using the browser.