[CalendarServer-changes] [5236] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Wed Mar 3 16:34:21 PST 2010


Revision: 5236
          http://trac.macosforge.org/projects/calendarserver/changeset/5236
Author:   wsanchez at apple.com
Date:     2010-03-03 16:34:18 -0800 (Wed, 03 Mar 2010)
Log Message:
-----------
twistedcaldav.accesslog -> calendarserver.accesslog

Modified Paths:
--------------
    CalendarServer/trunk/calendarserver/tap/caldav.py
    CalendarServer/trunk/calendarserver/util.py

Added Paths:
-----------
    CalendarServer/trunk/calendarserver/accesslog.py

Removed Paths:
-------------
    CalendarServer/trunk/twistedcaldav/accesslog.py

Copied: CalendarServer/trunk/calendarserver/accesslog.py (from rev 5226, CalendarServer/trunk/twistedcaldav/accesslog.py)
===================================================================
--- CalendarServer/trunk/calendarserver/accesslog.py	                        (rev 0)
+++ CalendarServer/trunk/calendarserver/accesslog.py	2010-03-04 00:34:18 UTC (rev 5236)
@@ -0,0 +1,445 @@
+##
+# Copyright (c) 2006-2009 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+"""
+Access logs.
+"""
+
+__all__ = [
+    "DirectoryLogWrapperResource",
+    "RotatingFileAccessLoggingObserver",
+    "AMPCommonAccessLoggingObserver",
+    "AMPLoggingFactory",
+]
+
+import datetime
+import os
+import time
+
+from twisted.internet import protocol
+from twisted.protocols import amp
+from twext.web2 import iweb
+from twext.web2.dav import davxml
+from twext.web2.log import BaseCommonAccessLoggingObserver
+from twext.web2.log import LogWrapperResource
+
+from twext.python.log import Logger
+
+from twistedcaldav.config import config
+from twistedcaldav.directory.directory import DirectoryService
+
+log = Logger()
+
+class DirectoryLogWrapperResource(LogWrapperResource):
+    
+    def __init__(self, resource, directory):
+        super(DirectoryLogWrapperResource, self).__init__(resource)
+        
+        self.directory = directory
+        
+    def getDirectory(self):
+        return self.directory
+
+class CommonAccessLoggingObserverExtensions(BaseCommonAccessLoggingObserver):
+    """
+    A base class for our extension to the L{BaseCommonAccessLoggingObserver}
+    """
+
+    def emit(self, eventDict):
+
+        if eventDict.get("interface") is iweb.IRequest:
+            
+            if config.GlobalStatsLoggingFrequency is not 0: 
+                self.logGlobalHit()
+
+            request = eventDict["request"]
+            response = eventDict["response"]
+            loginfo = eventDict["loginfo"]
+
+            # Try to determine authentication and authorization identifiers
+            uid = "-"
+            if hasattr(request, "authnUser"):
+                if isinstance(request.authnUser.children[0], davxml.HRef):
+                    uidn = str(request.authnUser.children[0])
+                    uidz = None
+                    if hasattr(request, "authzUser") and str(request.authzUser.children[0]) != uidn:
+                        uidz = str(request.authzUser.children[0])
+                        
+                    def convertUIDtoShortName(uid):
+                        uid = uid.rstrip("/")
+                        uid = uid[uid.rfind("/") + 1:]
+                        record = request.site.resource.getDirectory().recordWithUID(uid)
+                        if record:
+                            if record.recordType == DirectoryService.recordType_users:
+                                return record.shortNames[0]
+                            else:
+                                return "(%s)%s" % (record.recordType, record.shortNames[0],)
+                        else:
+                            return uid
+                        
+                    uidn = convertUIDtoShortName(uidn)
+                    if uidz:
+                        uidz = convertUIDtoShortName(uidz)
+                        
+                    if uidn and uidz:
+                        uid = '"%s as %s"' % (uidn, uidz,)
+                    else:
+                        uid = uidn
+
+            #
+            # For some methods which basically allow you to tunnel a
+            # custom request (eg. REPORT, POST), the method name
+            # itself doesn't tell you much about what action is being
+            # requested.  This allows a method to tack a submethod
+            # attribute to the request, so we can provide a little
+            # more detail here.
+            #
+            if config.EnableExtendedAccessLog and hasattr(request, "submethod"):
+                method = "%s(%s)" % (request.method, request.submethod)
+            else:
+                method = request.method
+
+            # Standard Apache access log fields
+            format = (
+                '%(host)s - %(uid)s [%(date)s]'
+                ' "%(method)s %(uri)s HTTP/%(protocolVersion)s"'
+                ' %(statusCode)s %(bytesSent)d'
+                ' "%(referer)s" "%(userAgent)s"'
+            )
+
+            if config.EnableExtendedAccessLog:
+                formats = [
+                    format,
+                    # Performance monitoring extensions
+                    'i=%(serverInstance)s t=%(timeSpent).1f or=%(outstandingRequests)s',
+                ]
+                if hasattr(request, "extendedLogItems"):
+                    for k, v in request.extendedLogItems.iteritems():
+                        k = str(k).replace('"', "%22")
+                        v = str(v).replace('"', "%22")
+                        if " " in v:
+                            v = '"%s"' % (v,)
+                        formats.append("%s=%s" % (k, v))
+
+                fwdHeaders = request.headers.getRawHeaders("x-forwarded-for", "")
+                if fwdHeaders:
+                    # Limit each x-forwarded-header to 50 in case someone is
+                    # trying to overwhelm the logs
+                    forwardedFor = ",".join([hdr[:50] for hdr in fwdHeaders])
+                    forwardedFor = forwardedFor.replace(" ", "")
+                    formats.append("fwd=%(fwd)s")
+                else:
+                    forwardedFor = ""
+
+                format = " ".join(formats)
+
+            formatArgs = {
+                "host"                : request.remoteAddr.host,
+                "uid"                 : uid,
+                "date"                : self.logDateString(response.headers.getHeader("date", 0)),
+                "method"              : method,
+                "uri"                 : request.uri.replace('"', "%22"),
+                "protocolVersion"     : ".".join(str(x) for x in request.clientproto),
+                "statusCode"          : response.code,
+                "bytesSent"           : loginfo.bytesSent,
+                "referer"             : request.headers.getHeader("referer", "-"),
+                "userAgent"           : request.headers.getHeader("user-agent", "-"),
+                "serverInstance"      : config.LogID,
+                "timeSpent"           : (time.time() - request.initTime) * 1000,
+                "outstandingRequests" : request.chanRequest.channel.factory.outstandingRequests,
+                "fwd"                 : forwardedFor,
+            }
+            self.logMessage(format % formatArgs)
+
+        elif "overloaded" in eventDict:
+            overloaded = eventDict.get("overloaded")
+            format_str = '%s - - [%s] "???" 503 0 "-" "-" [0.0 ms]'
+            format_data = (
+                overloaded.transport.hostname,
+                self.logDateString(time.time()),
+            )
+            if config.EnableExtendedAccessLog:
+                format_str += " [%s %s]"
+                format_data += (
+                    overloaded.transport.server.port,
+                    overloaded.outstandingRequests,
+                )
+            self.logMessage(format_str % format_data)
+
+class RotatingFileAccessLoggingObserver(CommonAccessLoggingObserverExtensions):
+    """
+    Class to do "apache" style access logging to a rotating log file. The log
+    file is rotated after midnight each day.
+    """
+
+    def __init__(self, logpath):
+        self.logpath = logpath
+        self.globalHitCount = 0 
+        self.globalHitHistory = [] 
+        for i in range(0, config.GlobalStatsLoggingFrequency + 1): 
+            self.globalHitHistory.append({"time":int(time.time()), "hits":0})
+
+    def logMessage(self, message, allowrotate=True):
+        """
+        Log a message to the file and possibly rotate if date has changed.
+
+        @param message: C{str} for the message to log.
+        @param allowrotate: C{True} if log rotate allowed, C{False} to log to current file
+            without testing for rotation.
+        """
+
+        if self.shouldRotate() and allowrotate:
+            self.flush()
+            self.rotate()
+        self.f.write(message + "\n")
+
+    def rotateGlobalHitHistoryStats(self): 
+        """ 
+        Roll the global hit history array: push the current stats as 
+        the last element; pop the first (oldest) element and reschedule the task. 
+        """ 
+
+        self.globalHitHistory.append({"time":int(time.time()), "hits":self.globalHitCount}) 
+        del self.globalHitHistory[0] 
+        log.debug("rotateGlobalHitHistoryStats: %s" % (self.globalHitHistory,))
+        if config.GlobalStatsLoggingFrequency is not 0: 
+            self.reactor.callLater(
+                config.GlobalStatsLoggingPeriod * 60 / config.GlobalStatsLoggingFrequency, 
+                self.rotateGlobalHitHistoryStats
+            ) 
+
+    def start(self):
+        """
+        Start logging. Open the log file and log an "open" message.
+        """
+
+        super(RotatingFileAccessLoggingObserver, self).start()
+        self._open()
+        self.logMessage("Log opened - server start: [%s]." % (datetime.datetime.now().ctime(),))
+ 
+        # Need a reactor for the callLater() support for rotateGlobalHitHistoryStats() 
+        from twisted.internet import reactor 
+        self.reactor = reactor 
+        self.rotateGlobalHitHistoryStats() 
+
+    def stop(self):
+        """
+        Stop logging. Close the log file and log an "open" message.
+        """
+
+        self.logMessage("Log closed - server stop: [%s]." % (datetime.datetime.now().ctime(),), False)
+        super(RotatingFileAccessLoggingObserver, self).stop()
+        self._close()
+
+    def _open(self):
+        """
+        Open the log file.
+        """
+
+        self.f = open(self.logpath, "a", 1)
+        self.lastDate = self.toDate(os.stat(self.logpath)[8])
+
+    def _close(self):
+        """
+        Close the log file.
+        """
+
+        self.f.close()
+
+    def flush(self):
+        """
+        Flush the log file.
+        """
+
+        self.f.flush()
+
+    def shouldRotate(self):
+        """
+        Rotate when the date has changed since last write
+        """
+
+        if config.RotateAccessLog:
+            return self.toDate() > self.lastDate
+        else:
+            return False
+
+    def toDate(self, *args):
+        """
+        Convert a unixtime to (year, month, day) localtime tuple,
+        or return the current (year, month, day) localtime tuple.
+
+        This function primarily exists so you may overload it with
+        gmtime, or some cruft to make unit testing possible.
+        """
+
+        # primarily so this can be unit tested easily
+        return time.localtime(*args)[:3]
+
+    def suffix(self, tupledate):
+        """
+        Return the suffix given a (year, month, day) tuple or unixtime
+        """
+
+        try:
+            return "_".join(map(str, tupledate))
+        except:
+            # try taking a float unixtime
+            return "_".join(map(str, self.toDate(tupledate)))
+
+    def rotate(self):
+        """
+        Rotate the file and create a new one.
+
+        If it's not possible to open new logfile, this will fail silently,
+        and continue logging to old logfile.
+        """
+
+        newpath = "%s.%s" % (self.logpath, self.suffix(self.lastDate))
+        if os.path.exists(newpath):
+            log.msg("Cannot rotate log file to %s because it already exists." % (newpath,))
+            return
+        self.logMessage("Log closed - rotating: [%s]." % (datetime.datetime.now().ctime(),), False)
+        log.msg("Rotating log file to: %s" % (newpath,), system="Logging")
+        self.f.close()
+        os.rename(self.logpath, newpath)
+        self._open()
+        self.logMessage("Log opened - rotated: [%s]." % (datetime.datetime.now().ctime(),), False)
+
+    def logGlobalHit(self): 
+        """ 
+        Increment the service-global hit counter 
+        """ 
+
+        self.globalHitCount += 1 
+
+    def getGlobalHits(self): 
+        """ 
+        Return the global hit stats 
+        """ 
+
+        stats = '<?xml version="1.0" encoding="UTF-8"?><plist version="1.0">' 
+        stats += "<dict><key>totalHits</key><integer>%d</integer>" 
+        stats += "<key>recentHits</key><dict>" 
+        stats += "<key>count</key><integer>%d</integer>" 
+        stats += "<key>since</key><integer>%d</integer>" 
+        stats += "<key>period</key><integer>%d</integer>" 
+        stats += "<key>frequency</key><integer>%d</integer>" 
+        stats += "</dict></dict></plist>" 
+        return stats % (
+            self.globalHitCount,
+            self.globalHitCount - self.globalHitHistory[0]["hits"], 
+            self.globalHitHistory[0]["time"],
+            config.GlobalStatsLoggingPeriod,
+            config.GlobalStatsLoggingFrequency
+        ) 
+
+class LogMessage(amp.Command):
+    arguments = [("message", amp.String())]
+
+class LogGlobalHit(amp.Command): 
+    arguments = [] 
+
+class AMPCommonAccessLoggingObserver(CommonAccessLoggingObserverExtensions):
+    def __init__(self, mode, id):
+        self.mode = mode
+        self.id = id
+        self.protocol = None
+        self._buffer = []
+
+    def flushBuffer(self):
+        if self._buffer:
+            for msg in self._buffer:
+                self.logMessage(msg)
+
+    def start(self):
+        super(AMPCommonAccessLoggingObserver, self).start()
+
+        from twisted.internet import reactor
+
+        def _gotProtocol(proto):
+            self.protocol = proto
+            self.flushBuffer()
+
+        self.client = protocol.ClientCreator(reactor, amp.AMP)
+        if self.mode == "AF_UNIX":
+            d = self.client.connectUNIX(self.id)
+        else:
+            d = self.client.connectTCP("localhost", self.id)
+        d.addCallback(_gotProtocol)
+
+    def stop(self):
+        super(AMPCommonAccessLoggingObserver, self).stop()
+        self.client.disconnect()
+
+    def logMessage(self, message):
+        """
+        Log a message to the remote AMP Protocol
+        """
+        if self.protocol is not None:
+            # XXX: Yeah we're not waiting for anything to happen here.
+            #      but we will log an error.
+            if isinstance(message, unicode):
+                message = message.encode("utf-8")
+            d = self.protocol.callRemote(LogMessage, message=message)
+            d.addErrback(log.err)
+        else:
+            self._buffer.append(message)
+
+    def logGlobalHit(self): 
+        """ 
+        Log a server hit via the remote AMP Protocol 
+        """ 
+
+        if self.protocol is not None: 
+            d = self.protocol.callRemote(LogGlobalHit) 
+            d.addErrback(log.err) 
+        else: 
+            log.msg("logGlobalHit() only works with an AMP Protocol") 
+
+class AMPLoggingProtocol(amp.AMP):
+    """
+    A server side protocol for logging to the given observer.
+    """
+
+    def __init__(self, observer):
+        self.observer = observer
+
+        super(AMPLoggingProtocol, self).__init__()
+
+    def logMessage(self, message):
+        self.observer.logMessage(message)
+        return {}
+
+    LogMessage.responder(logMessage)
+
+    def logGlobalHit(self): 
+        self.observer.logGlobalHit() 
+        return {} 
+
+    LogGlobalHit.responder(logGlobalHit)
+
+class AMPLoggingFactory(protocol.ServerFactory):
+    def __init__(self, observer):
+        self.observer = observer
+
+    def doStart(self):
+        self.observer.start()
+
+    def doStop(self):
+        self.observer.stop()
+
+    def buildProtocol(self, addr):
+        return AMPLoggingProtocol(self.observer)

Modified: CalendarServer/trunk/calendarserver/tap/caldav.py
===================================================================
--- CalendarServer/trunk/calendarserver/tap/caldav.py	2010-03-04 00:14:48 UTC (rev 5235)
+++ CalendarServer/trunk/calendarserver/tap/caldav.py	2010-03-04 00:34:18 UTC (rev 5236)
@@ -60,9 +60,6 @@
     from version import version as getVersion
     version = "%s (%s)" % getVersion()
 
-from twistedcaldav.accesslog import AMPCommonAccessLoggingObserver
-from twistedcaldav.accesslog import AMPLoggingFactory
-from twistedcaldav.accesslog import RotatingFileAccessLoggingObserver
 from twistedcaldav.config import ConfigurationError
 from twistedcaldav.config import config
 from twistedcaldav.directory.principal import DirectoryPrincipalProvisioningResource
@@ -81,6 +78,9 @@
 except ImportError:
     NegotiateCredentialFactory = None
 
+from calendarserver.accesslog import AMPCommonAccessLoggingObserver
+from calendarserver.accesslog import AMPLoggingFactory
+from calendarserver.accesslog import RotatingFileAccessLoggingObserver
 from calendarserver.provision.root import RootResource
 from calendarserver.webadmin.resource import WebAdminResource
 from calendarserver.webcal.resource import WebCalendarResource

Modified: CalendarServer/trunk/calendarserver/util.py
===================================================================
--- CalendarServer/trunk/calendarserver/util.py	2010-03-04 00:14:48 UTC (rev 5235)
+++ CalendarServer/trunk/calendarserver/util.py	2010-03-04 00:34:18 UTC (rev 5236)
@@ -36,7 +36,6 @@
 from twext.python.log import Logger
 
 from twistedcaldav import memcachepool
-from twistedcaldav.accesslog import DirectoryLogWrapperResource
 from twistedcaldav.directory import augment, calendaruserproxy
 from twistedcaldav.directory.aggregate import AggregateDirectoryService
 from twistedcaldav.directory.calendaruserproxyloader import XMLCalendarUserProxyLoader
@@ -60,6 +59,7 @@
 except ImportError:
     NegotiateCredentialFactory = None
 
+from calendarserver.accesslog import DirectoryLogWrapperResource
 from calendarserver.provision.root import RootResource
 from calendarserver.webadmin.resource import WebAdminResource
 from calendarserver.webcal.resource import WebCalendarResource

Deleted: CalendarServer/trunk/twistedcaldav/accesslog.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/accesslog.py	2010-03-04 00:14:48 UTC (rev 5235)
+++ CalendarServer/trunk/twistedcaldav/accesslog.py	2010-03-04 00:34:18 UTC (rev 5236)
@@ -1,445 +0,0 @@
-##
-# Copyright (c) 2006-2009 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-##
-
-"""
-Access logs.
-"""
-
-__all__ = [
-    "DirectoryLogWrapperResource",
-    "RotatingFileAccessLoggingObserver",
-    "AMPCommonAccessLoggingObserver",
-    "AMPLoggingFactory",
-]
-
-import datetime
-import os
-import time
-
-from twisted.internet import protocol
-from twisted.protocols import amp
-from twext.web2 import iweb
-from twext.web2.dav import davxml
-from twext.web2.log import BaseCommonAccessLoggingObserver
-from twext.web2.log import LogWrapperResource
-
-from twext.python.log import Logger
-
-from twistedcaldav.config import config
-from twistedcaldav.directory.directory import DirectoryService
-
-log = Logger()
-
-class DirectoryLogWrapperResource(LogWrapperResource):
-    
-    def __init__(self, resource, directory):
-        super(DirectoryLogWrapperResource, self).__init__(resource)
-        
-        self.directory = directory
-        
-    def getDirectory(self):
-        return self.directory
-
-class CommonAccessLoggingObserverExtensions(BaseCommonAccessLoggingObserver):
-    """
-    A base class for our extension to the L{BaseCommonAccessLoggingObserver}
-    """
-
-    def emit(self, eventDict):
-
-        if eventDict.get("interface") is iweb.IRequest:
-            
-            if config.GlobalStatsLoggingFrequency is not 0: 
-                self.logGlobalHit()
-
-            request = eventDict["request"]
-            response = eventDict["response"]
-            loginfo = eventDict["loginfo"]
-
-            # Try to determine authentication and authorization identifiers
-            uid = "-"
-            if hasattr(request, "authnUser"):
-                if isinstance(request.authnUser.children[0], davxml.HRef):
-                    uidn = str(request.authnUser.children[0])
-                    uidz = None
-                    if hasattr(request, "authzUser") and str(request.authzUser.children[0]) != uidn:
-                        uidz = str(request.authzUser.children[0])
-                        
-                    def convertUIDtoShortName(uid):
-                        uid = uid.rstrip("/")
-                        uid = uid[uid.rfind("/") + 1:]
-                        record = request.site.resource.getDirectory().recordWithUID(uid)
-                        if record:
-                            if record.recordType == DirectoryService.recordType_users:
-                                return record.shortNames[0]
-                            else:
-                                return "(%s)%s" % (record.recordType, record.shortNames[0],)
-                        else:
-                            return uid
-                        
-                    uidn = convertUIDtoShortName(uidn)
-                    if uidz:
-                        uidz = convertUIDtoShortName(uidz)
-                        
-                    if uidn and uidz:
-                        uid = '"%s as %s"' % (uidn, uidz,)
-                    else:
-                        uid = uidn
-
-            #
-            # For some methods which basically allow you to tunnel a
-            # custom request (eg. REPORT, POST), the method name
-            # itself doesn't tell you much about what action is being
-            # requested.  This allows a method to tack a submethod
-            # attribute to the request, so we can provide a little
-            # more detail here.
-            #
-            if config.EnableExtendedAccessLog and hasattr(request, "submethod"):
-                method = "%s(%s)" % (request.method, request.submethod)
-            else:
-                method = request.method
-
-            # Standard Apache access log fields
-            format = (
-                '%(host)s - %(uid)s [%(date)s]'
-                ' "%(method)s %(uri)s HTTP/%(protocolVersion)s"'
-                ' %(statusCode)s %(bytesSent)d'
-                ' "%(referer)s" "%(userAgent)s"'
-            )
-
-            if config.EnableExtendedAccessLog:
-                formats = [
-                    format,
-                    # Performance monitoring extensions
-                    'i=%(serverInstance)s t=%(timeSpent).1f or=%(outstandingRequests)s',
-                ]
-                if hasattr(request, "extendedLogItems"):
-                    for k, v in request.extendedLogItems.iteritems():
-                        k = str(k).replace('"', "%22")
-                        v = str(v).replace('"', "%22")
-                        if " " in v:
-                            v = '"%s"' % (v,)
-                        formats.append("%s=%s" % (k, v))
-
-                fwdHeaders = request.headers.getRawHeaders("x-forwarded-for", "")
-                if fwdHeaders:
-                    # Limit each x-forwarded-header to 50 in case someone is
-                    # trying to overwhelm the logs
-                    forwardedFor = ",".join([hdr[:50] for hdr in fwdHeaders])
-                    forwardedFor = forwardedFor.replace(" ", "")
-                    formats.append("fwd=%(fwd)s")
-                else:
-                    forwardedFor = ""
-
-                format = " ".join(formats)
-
-            formatArgs = {
-                "host"                : request.remoteAddr.host,
-                "uid"                 : uid,
-                "date"                : self.logDateString(response.headers.getHeader("date", 0)),
-                "method"              : method,
-                "uri"                 : request.uri.replace('"', "%22"),
-                "protocolVersion"     : ".".join(str(x) for x in request.clientproto),
-                "statusCode"          : response.code,
-                "bytesSent"           : loginfo.bytesSent,
-                "referer"             : request.headers.getHeader("referer", "-"),
-                "userAgent"           : request.headers.getHeader("user-agent", "-"),
-                "serverInstance"      : config.LogID,
-                "timeSpent"           : (time.time() - request.initTime) * 1000,
-                "outstandingRequests" : request.chanRequest.channel.factory.outstandingRequests,
-                "fwd"                 : forwardedFor,
-            }
-            self.logMessage(format % formatArgs)
-
-        elif "overloaded" in eventDict:
-            overloaded = eventDict.get("overloaded")
-            format_str = '%s - - [%s] "???" 503 0 "-" "-" [0.0 ms]'
-            format_data = (
-                overloaded.transport.hostname,
-                self.logDateString(time.time()),
-            )
-            if config.EnableExtendedAccessLog:
-                format_str += " [%s %s]"
-                format_data += (
-                    overloaded.transport.server.port,
-                    overloaded.outstandingRequests,
-                )
-            self.logMessage(format_str % format_data)
-
-class RotatingFileAccessLoggingObserver(CommonAccessLoggingObserverExtensions):
-    """
-    Class to do "apache" style access logging to a rotating log file. The log
-    file is rotated after midnight each day.
-    """
-
-    def __init__(self, logpath):
-        self.logpath = logpath
-        self.globalHitCount = 0 
-        self.globalHitHistory = [] 
-        for i in range(0, config.GlobalStatsLoggingFrequency + 1): 
-            self.globalHitHistory.append({"time":int(time.time()), "hits":0})
-
-    def logMessage(self, message, allowrotate=True):
-        """
-        Log a message to the file and possibly rotate if date has changed.
-
-        @param message: C{str} for the message to log.
-        @param allowrotate: C{True} if log rotate allowed, C{False} to log to current file
-            without testing for rotation.
-        """
-
-        if self.shouldRotate() and allowrotate:
-            self.flush()
-            self.rotate()
-        self.f.write(message + "\n")
-
-    def rotateGlobalHitHistoryStats(self): 
-        """ 
-        Roll the global hit history array: push the current stats as 
-        the last element; pop the first (oldest) element and reschedule the task. 
-        """ 
-
-        self.globalHitHistory.append({"time":int(time.time()), "hits":self.globalHitCount}) 
-        del self.globalHitHistory[0] 
-        log.debug("rotateGlobalHitHistoryStats: %s" % (self.globalHitHistory,))
-        if config.GlobalStatsLoggingFrequency is not 0: 
-            self.reactor.callLater(
-                config.GlobalStatsLoggingPeriod * 60 / config.GlobalStatsLoggingFrequency, 
-                self.rotateGlobalHitHistoryStats
-            ) 
-
-    def start(self):
-        """
-        Start logging. Open the log file and log an "open" message.
-        """
-
-        super(RotatingFileAccessLoggingObserver, self).start()
-        self._open()
-        self.logMessage("Log opened - server start: [%s]." % (datetime.datetime.now().ctime(),))
- 
-        # Need a reactor for the callLater() support for rotateGlobalHitHistoryStats() 
-        from twisted.internet import reactor 
-        self.reactor = reactor 
-        self.rotateGlobalHitHistoryStats() 
-
-    def stop(self):
-        """
-        Stop logging. Close the log file and log an "open" message.
-        """
-
-        self.logMessage("Log closed - server stop: [%s]." % (datetime.datetime.now().ctime(),), False)
-        super(RotatingFileAccessLoggingObserver, self).stop()
-        self._close()
-
-    def _open(self):
-        """
-        Open the log file.
-        """
-
-        self.f = open(self.logpath, "a", 1)
-        self.lastDate = self.toDate(os.stat(self.logpath)[8])
-
-    def _close(self):
-        """
-        Close the log file.
-        """
-
-        self.f.close()
-
-    def flush(self):
-        """
-        Flush the log file.
-        """
-
-        self.f.flush()
-
-    def shouldRotate(self):
-        """
-        Rotate when the date has changed since last write
-        """
-
-        if config.RotateAccessLog:
-            return self.toDate() > self.lastDate
-        else:
-            return False
-
-    def toDate(self, *args):
-        """
-        Convert a unixtime to (year, month, day) localtime tuple,
-        or return the current (year, month, day) localtime tuple.
-
-        This function primarily exists so you may overload it with
-        gmtime, or some cruft to make unit testing possible.
-        """
-
-        # primarily so this can be unit tested easily
-        return time.localtime(*args)[:3]
-
-    def suffix(self, tupledate):
-        """
-        Return the suffix given a (year, month, day) tuple or unixtime
-        """
-
-        try:
-            return "_".join(map(str, tupledate))
-        except:
-            # try taking a float unixtime
-            return "_".join(map(str, self.toDate(tupledate)))
-
-    def rotate(self):
-        """
-        Rotate the file and create a new one.
-
-        If it's not possible to open new logfile, this will fail silently,
-        and continue logging to old logfile.
-        """
-
-        newpath = "%s.%s" % (self.logpath, self.suffix(self.lastDate))
-        if os.path.exists(newpath):
-            log.msg("Cannot rotate log file to %s because it already exists." % (newpath,))
-            return
-        self.logMessage("Log closed - rotating: [%s]." % (datetime.datetime.now().ctime(),), False)
-        log.msg("Rotating log file to: %s" % (newpath,), system="Logging")
-        self.f.close()
-        os.rename(self.logpath, newpath)
-        self._open()
-        self.logMessage("Log opened - rotated: [%s]." % (datetime.datetime.now().ctime(),), False)
-
-    def logGlobalHit(self): 
-        """ 
-        Increment the service-global hit counter 
-        """ 
-
-        self.globalHitCount += 1 
-
-    def getGlobalHits(self): 
-        """ 
-        Return the global hit stats 
-        """ 
-
-        stats = '<?xml version="1.0" encoding="UTF-8"?><plist version="1.0">' 
-        stats += "<dict><key>totalHits</key><integer>%d</integer>" 
-        stats += "<key>recentHits</key><dict>" 
-        stats += "<key>count</key><integer>%d</integer>" 
-        stats += "<key>since</key><integer>%d</integer>" 
-        stats += "<key>period</key><integer>%d</integer>" 
-        stats += "<key>frequency</key><integer>%d</integer>" 
-        stats += "</dict></dict></plist>" 
-        return stats % (
-            self.globalHitCount,
-            self.globalHitCount - self.globalHitHistory[0]["hits"], 
-            self.globalHitHistory[0]["time"],
-            config.GlobalStatsLoggingPeriod,
-            config.GlobalStatsLoggingFrequency
-        ) 
-
-class LogMessage(amp.Command):
-    arguments = [("message", amp.String())]
-
-class LogGlobalHit(amp.Command): 
-    arguments = [] 
-
-class AMPCommonAccessLoggingObserver(CommonAccessLoggingObserverExtensions):
-    def __init__(self, mode, id):
-        self.mode = mode
-        self.id = id
-        self.protocol = None
-        self._buffer = []
-
-    def flushBuffer(self):
-        if self._buffer:
-            for msg in self._buffer:
-                self.logMessage(msg)
-
-    def start(self):
-        super(AMPCommonAccessLoggingObserver, self).start()
-
-        from twisted.internet import reactor
-
-        def _gotProtocol(proto):
-            self.protocol = proto
-            self.flushBuffer()
-
-        self.client = protocol.ClientCreator(reactor, amp.AMP)
-        if self.mode == "AF_UNIX":
-            d = self.client.connectUNIX(self.id)
-        else:
-            d = self.client.connectTCP("localhost", self.id)
-        d.addCallback(_gotProtocol)
-
-    def stop(self):
-        super(AMPCommonAccessLoggingObserver, self).stop()
-        self.client.disconnect()
-
-    def logMessage(self, message):
-        """
-        Log a message to the remote AMP Protocol
-        """
-        if self.protocol is not None:
-            # XXX: Yeah we're not waiting for anything to happen here.
-            #      but we will log an error.
-            if isinstance(message, unicode):
-                message = message.encode("utf-8")
-            d = self.protocol.callRemote(LogMessage, message=message)
-            d.addErrback(log.err)
-        else:
-            self._buffer.append(message)
-
-    def logGlobalHit(self): 
-        """ 
-        Log a server hit via the remote AMP Protocol 
-        """ 
-
-        if self.protocol is not None: 
-            d = self.protocol.callRemote(LogGlobalHit) 
-            d.addErrback(log.err) 
-        else: 
-            log.msg("logGlobalHit() only works with an AMP Protocol") 
-
-class AMPLoggingProtocol(amp.AMP):
-    """
-    A server side protocol for logging to the given observer.
-    """
-
-    def __init__(self, observer):
-        self.observer = observer
-
-        super(AMPLoggingProtocol, self).__init__()
-
-    def logMessage(self, message):
-        self.observer.logMessage(message)
-        return {}
-
-    LogMessage.responder(logMessage)
-
-    def logGlobalHit(self): 
-        self.observer.logGlobalHit() 
-        return {} 
-
-    LogGlobalHit.responder(logGlobalHit)
-
-class AMPLoggingFactory(protocol.ServerFactory):
-    def __init__(self, observer):
-        self.observer = observer
-
-    def doStart(self):
-        self.observer.start()
-
-    def doStop(self):
-        self.observer.stop()
-
-    def buildProtocol(self, addr):
-        return AMPLoggingProtocol(self.observer)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20100303/d7de6ec5/attachment-0001.html>


More information about the calendarserver-changes mailing list