[CalendarServer-changes] [6686] CalendarServer/trunk/contrib/tools

source_changes at macosforge.org source_changes at macosforge.org
Mon Dec 13 09:29:04 PST 2010


Revision: 6686
          http://trac.macosforge.org/projects/calendarserver/changeset/6686
Author:   exarkun at twistedmatrix.com
Date:     2010-12-13 09:29:00 -0800 (Mon, 13 Dec 2010)
Log Message:
-----------
Report some information about how many non-self calendar home propfinds users do

Modified Paths:
--------------
    CalendarServer/trunk/contrib/tools/protocolanalysis.py

Added Paths:
-----------
    CalendarServer/trunk/contrib/tools/test_protocolanalysis.py

Modified: CalendarServer/trunk/contrib/tools/protocolanalysis.py
===================================================================
--- CalendarServer/trunk/contrib/tools/protocolanalysis.py	2010-12-10 19:12:56 UTC (rev 6685)
+++ CalendarServer/trunk/contrib/tools/protocolanalysis.py	2010-12-13 17:29:00 UTC (rev 6686)
@@ -81,6 +81,21 @@
     (  None, "(l):120s+"),
 )
 
+userInteractionCountBuckets = (
+    (   0, "(a):0"),
+    (   1, "(b):1"),
+    (   2, "(c):2"),
+    (   3, "(d):3"),
+    (   4, "(e):4"),
+    (   5, "(f):5"),
+    (  10, "(g):6-10"),
+    (  15, "(h):11-15"),
+    (  20, "(i):16-20"),
+    (  30, "(j):21-30"),
+    (  50, "(k):31-50"),
+    (None, "(l):51+"),
+)
+
 httpMethods = set((
     "ACL",
     "BIND",
@@ -103,6 +118,20 @@
 
 class CalendarServerLogAnalyzer(object):
     
+    """
+    @ivar resolutionMinutes: The number of minutes long a statistics
+        bucket will be.  For example, if this is C{5}, then all data
+        points less than 5 will be placed into the first bucket; data
+        points greater than or equal to 5 and less than 10 will be
+        placed into the second bucket, and so on.
+
+    @ivar timeBucketCount: The number of statistics buckets of length
+        C{resolutionMinutes} needed to hold one day of data.
+
+    @ivar hourlyTotals: A C{list} of length C{timeBucketCount} holding ...
+
+    """
+
     class LogLine(object):
         
         def __init__(self, userid, logDateTime, logTime, method, uri, status, bytes, referer, client, extended):
@@ -181,11 +210,12 @@
         self.userCounts = collections.defaultdict(int)
         self.userResponseTimes = collections.defaultdict(float)
 
+        self.otherUserCalendarRequests = {}
+
         self.currentLine = None
         self.linesRead = 0
 
     def analyzeLogFile(self, logFilePath):
-        
         fpath = os.path.expanduser(logFilePath)
         if fpath.endswith(".gz"):
             f = GzipFile(fpath)
@@ -198,7 +228,6 @@
         try:
             ctr = 0
             for line in f:
-                
                 ctr += 1
                 if ctr <= self.linesRead:
                     continue
@@ -225,7 +254,6 @@
 #                if hourFromStart > 1:
 #                    break
                 if hourFromStart > lastHourFromStart:
-                    print logHour
                     lastHourFromStart = hourFromStart
                 if hourFromStart < self.startHourFromStart:
                     continue
@@ -352,6 +380,9 @@
 
                 self.userAnalysis(adjustedMethod)
 
+                # Look at interactions between different users
+                self.userInteractionAnalysis(adjustedMethod)
+
         except Exception:
             print line
             raise
@@ -656,7 +687,31 @@
         responseTime = float(self.currentLine.extended.get("t", 0.0))
         self.userCounts["%s:%s" % (self.currentLine.userid, self.getClientAdjustedName(),)] += 1
         self.userResponseTimes["%s:%s" % (self.currentLine.userid, self.getClientAdjustedName(),)] += responseTime
-        
+
+
+    def summarizeUserInteraction(self, adjustedMethod):
+        summary = {}
+        otherData = self.otherUserCalendarRequests.get(adjustedMethod, {})
+        for user, others in otherData.iteritems():
+            bucket = self.getCountBucket(len(others), userInteractionCountBuckets)
+            summary[bucket] = summary.get(bucket, 0) + 1
+        return summary
+
+
+    def userInteractionAnalysis(self, adjustedMethod):
+        """
+        If the current line is a record of one user accessing another
+        user's data, update C{self.otherUserCalendarRequests} to
+        account for it.
+        """
+        forMethod = self.otherUserCalendarRequests.setdefault(adjustedMethod, {})
+        others = forMethod.setdefault(self.currentLine.userid, set())
+        segments = self.currentLine.uri.split('/')
+        if segments[:3] == ['', 'calendars', '__uids__']:
+            if segments[3:] != [self.currentLine.userid, '']:
+                others.add(segments[3])
+
+
     def printAll(self, doTabs):
 
         self.printInfo(doTabs)
@@ -718,6 +773,9 @@
         print "URI Counts"
         self.printURICounts(doTabs)
 
+        print "User Interaction Counts"
+        self.printUserInteractionCounts(doTabs)
+
         #print "User Weights (top 100)"
         #self.printUserWeights(doTabs)
 
@@ -1159,6 +1217,20 @@
         table.printTabDelimitedData() if doTabs else table.printTable()
         print ""
 
+    def printUserInteractionCounts(self, doTabs):
+        table = tables.Table()
+        table.setDefaultColumnFormats((
+                tables.Table.ColumnFormat("%s", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
+                tables.Table.ColumnFormat("%s", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
+                ))
+        table.addHeader(("# users accessed", "# of users"))
+        for k, v in sorted(self.summarizeUserInteraction("PROPFIND Calendar Home").iteritems()):
+            # Chop off the "(a):" part.
+            table.addRow((k[4:], str(v)))
+        table.printTabDelimitedData() if doTabs else table.printTable()
+        print ""
+
+
 class TablePrinter(object):
     
     @classmethod

Added: CalendarServer/trunk/contrib/tools/test_protocolanalysis.py
===================================================================
--- CalendarServer/trunk/contrib/tools/test_protocolanalysis.py	                        (rev 0)
+++ CalendarServer/trunk/contrib/tools/test_protocolanalysis.py	2010-12-13 17:29:00 UTC (rev 6686)
@@ -0,0 +1,68 @@
+##
+# Copyright (c) 2009-2010 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.
+##
+
+from twisted.python.filepath import FilePath
+from twisted.trial.unittest import TestCase
+
+from protocolanalysis import CalendarServerLogAnalyzer
+
+class UserInteractionTests(TestCase):
+    """
+    Tests for analysis of the way users interact with each other, done
+    by CalendarServerLogAnalyzer.
+    """
+    def test_propfindOtherCalendar(self):
+        """
+        L{CalendarServerLogAnalyzer}'s C{otherUserCalendarRequests}
+        attribute is populated with data about the frequency with
+        which users access a calendar belonging to a different user.
+
+        The C{"PROPFIND Calendar Home"} key is associated with a
+        C{dict} with count bucket labels as keys and counts of how
+        many users PROPFINDs on calendars belonging to a number of
+        other users which falls into that bucket.
+        """
+        format = (
+            '17.128.126.80 - %(user)s [27/Sep/2010:05:13:17 +0000] '
+            '"PROPFIND /calendars/__uids__/%(other)s/ HTTP/1.1" 207 '
+            '21274 "-" "DAVKit/4.0.3 (732); CalendarStore/4.0.3 (991); '
+            'iCal/4.0.3 (1388); Mac OS X/10.6.4 (10F569)" i=16 t=199.1 or=2\n')
+        path = FilePath(self.mktemp())
+        path.setContent(
+            # A user accessing his own calendar
+            format % dict(user="user01", other="user01") +
+            
+            # A user accessing the calendar of one other person
+            format % dict(user="user02", other="user01") +
+
+            # A user accessing the calendar of one other person twice
+            format % dict(user="user03", other="user01") +
+            format % dict(user="user03", other="user01") +
+
+            # A user accessing the calendars of two other people
+            format % dict(user="user04", other="user01") +
+            format % dict(user="user04", other="user02") +
+
+            # Another user accessing the calendars of two other people
+            format % dict(user="user05", other="user03") +
+            format % dict(user="user05", other="user04"))
+
+        analyzer = CalendarServerLogAnalyzer(startHour=22, endHour=24)
+        analyzer.analyzeLogFile(path.path)
+
+        self.assertEquals(
+            analyzer.summarizeUserInteraction("PROPFIND Calendar Home"),
+            {"(a):0": 1, "(b):1": 2, "(c):2": 2})
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20101213/da9bd44d/attachment.html>


More information about the calendarserver-changes mailing list