[CalendarServer-changes] [9767] CalendarServer/trunk
source_changes at macosforge.org
source_changes at macosforge.org
Tue Sep 4 09:14:09 PDT 2012
Revision: 9767
http://trac.macosforge.org/projects/calendarserver/changeset/9767
Author: cdaboo at apple.com
Date: 2012-09-04 09:14:07 -0700 (Tue, 04 Sep 2012)
Log Message:
-----------
Add option to have stats logged via TCP as well as unix socket. Modify the readStats tool to be able to read from multiple
tcp sockets and print a summary of all hosts.
Modified Paths:
--------------
CalendarServer/trunk/calendarserver/tap/caldav.py
CalendarServer/trunk/contrib/tools/readStats.py
CalendarServer/trunk/twistedcaldav/stdconfig.py
Modified: CalendarServer/trunk/calendarserver/tap/caldav.py
===================================================================
--- CalendarServer/trunk/calendarserver/tap/caldav.py 2012-09-04 15:24:11 UTC (rev 9766)
+++ CalendarServer/trunk/calendarserver/tap/caldav.py 2012-09-04 16:14:07 UTC (rev 9767)
@@ -1252,12 +1252,20 @@
# Start listening on the stats socket, for administrators to inspect
# the current stats on the server.
- stats = CalDAVStatisticsServer(logger)
- statsService = GroupOwnedUNIXServer(
- gid, config.GlobalStatsSocket, stats, mode=0660
- )
- statsService.setName("stats")
- statsService.setServiceParent(s)
+ if config.Stats.EnableUnixStatsSocket:
+ stats = CalDAVStatisticsServer(logger)
+ statsService = GroupOwnedUNIXServer(
+ gid, config.Stats.UnixStatsSocket, stats, mode=0660
+ )
+ statsService.setName("unix-stats")
+ statsService.setServiceParent(s)
+ if config.Stats.EnableTCPStatsSocket:
+ stats = CalDAVStatisticsServer(logger)
+ statsService = TCPServer(
+ config.Stats.TCPStatsPort, stats, interface=""
+ )
+ statsService.setName("tcp-stats")
+ statsService.setServiceParent(s)
# Optionally enable Manhole access
if config.Manhole.Enabled:
@@ -1320,7 +1328,7 @@
def deleteStaleSocketFiles(self):
# Check all socket files we use.
- for checkSocket in [config.ControlSocket, config.GlobalStatsSocket] :
+ for checkSocket in [config.ControlSocket, config.Stats.UnixStatsSocket] :
# See if the file exists.
if (os.path.exists(checkSocket)):
Modified: CalendarServer/trunk/contrib/tools/readStats.py
===================================================================
--- CalendarServer/trunk/contrib/tools/readStats.py 2012-09-04 15:24:11 UTC (rev 9766)
+++ CalendarServer/trunk/contrib/tools/readStats.py 2012-09-04 16:14:07 UTC (rev 9767)
@@ -30,23 +30,42 @@
def safeDivision(value, total, factor=1):
return value * factor / total if total else 0
-def readSock(sockname):
- s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
- s.connect(sockname)
- data = ""
- while True:
- d = s.recv(1024)
- if d:
- data += d
- else:
- break
- s.close()
+def readSock(sockname, useTCP):
+ try:
+ s = socket.socket(socket.AF_INET if useTCP else socket.AF_UNIX, socket.SOCK_STREAM)
+ s.connect(sockname)
+ data = ""
+ while True:
+ d = s.recv(1024)
+ if d:
+ data += d
+ else:
+ break
+ s.close()
+ data = json.loads(data)
+ except socket.error:
+ data = {"Failed": "Unable to read statistics from server: %s" % (sockname,)}
+ data["Server"] = sockname
return data
-def printStats(data):
+def printStats(stats):
+ if len(stats) == 1 and False:
+ if "Failed" in stats[0]:
+ printFailedStats(stats[0]["Failed"])
+ else:
+ try:
+ printStat(stats[0])
+ except KeyError, e:
+ printFailedStats("Unable to find key '%s' in statistics from server socket" % (e,))
+ sys.exit(1)
+
+ else:
+ printMultipleStats(stats)
+
+def printStat(stats):
- stats = json.loads(data)
print "- " * 40
+ print "Server: %s" % (stats["Server"],)
print datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S")
print "Service Uptime: %s" % (datetime.timedelta(seconds=(int(time.time() - stats["System"]["start time"]))),)
if stats["System"]["cpu count"] > 0:
@@ -66,6 +85,42 @@
printRequestSummary(stats)
printHistogramSummary(stats["5 Minutes"])
+def printMultipleStats(stats):
+
+ labels = serverLabels(stats)
+
+ print "- " * 40
+ print "Servers: %s" % (", ".join(labels),)
+
+ print datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S")
+
+ times = []
+ for stat in stats:
+ try:
+ t = str(datetime.timedelta(seconds=int(time.time() - stat["System"]["start time"])))
+ except KeyError:
+ t = "-"
+ times.append(t)
+ print "Service Uptime: %s" % (", ".join(times),)
+
+ cpus = []
+ memories = []
+ for stat in stats:
+ if stat["System"]["cpu count"] > 0:
+ cpus.append("%.1f%%" % (stat["System"]["cpu use"],))
+ memories.append("%.1f%%" % (stat["System"]["memory percent"],))
+ else:
+ cpus.append("-")
+ memories("-")
+ print "Current CPU: %s" % (", ".join(cpus),)
+ print "Current Memory Used: %s" % (", ".join(memories),)
+ print
+ printMultiRequestSummary(stats, labels, ("5 Minutes", 5*60,))
+ printMultiHistogramSummary(stats, "5 Minutes")
+
+def serverLabels(stats):
+ return [str(stat["Server"]) for stat in stats]
+
def printFailedStats(message):
print "- " * 40
@@ -114,6 +169,57 @@
table.printTable(os=os)
print os.getvalue()
+def printMultiRequestSummary(stats, labels, index):
+ table = tables.Table()
+ table.addHeader(
+ ("Server", "Requests", "Av. Requests", "Av. Response", "Av. Response", "Max. Response", "Slot", "CPU", "500's"),
+ )
+ table.addHeader(
+ ( "", "", "per second", "(ms)", "no write(ms)", "(ms)", "Average", "Average", ""),
+ )
+ table.setDefaultColumnFormats(
+ (
+ tables.Table.ColumnFormat("%s", tables.Table.ColumnFormat.LEFT_JUSTIFY),
+ tables.Table.ColumnFormat("%d", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
+ tables.Table.ColumnFormat("%.1f", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
+ tables.Table.ColumnFormat("%.1f", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
+ tables.Table.ColumnFormat("%.1f", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
+ tables.Table.ColumnFormat("%.1f", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
+ tables.Table.ColumnFormat("%.2f", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
+ tables.Table.ColumnFormat("%.1f%%", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
+ tables.Table.ColumnFormat("%d", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
+ )
+ )
+
+ key, seconds = index
+ totals = ["Overall:", 0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0]
+ for ctr, stat in enumerate(stats):
+
+ stat = stat[key]
+
+ col = []
+ col.append(labels[ctr])
+ col.append(stat["requests"])
+ col.append(safeDivision(float(stat["requests"]), seconds))
+ col.append(safeDivision(stat["t"], stat["requests"]))
+ col.append(safeDivision(stat["t"] - stat["t-resp-wr"], stat["requests"]))
+ col.append(stat["T-MAX"])
+ col.append(safeDivision(float(stat["slots"]), stat["requests"]))
+ col.append(safeDivision(stat["cpu"], stat["requests"]))
+ col.append(stat["500"])
+ table.addRow(col)
+ for item in xrange(1, len(col)):
+ totals[item] += col[item]
+
+ for item in (2, 3, 4, 6, 7):
+ totals[item] /= len(stats)
+
+ table.addFooter(totals)
+
+ os = StringIO()
+ table.printTable(os=os)
+ print os.getvalue()
+
def printHistogramSummary(stat):
print "5 minute average response histogram"
@@ -152,7 +258,58 @@
table.printTable(os=os)
print os.getvalue()
+def printMultiHistogramSummary(stats, index):
+ # Totals first
+ keys = ("requests", "<10ms", "10ms<->100ms", "100ms<->1s", "1s<->10s", "10s<->30s", "30s<->60s", ">60s", "Over 1s", "Over 10s",)
+ totals = {
+ "T" : dict([(k, 0) for k in keys]),
+ "T-RESP-WR": dict([(k, 0) for k in keys]),
+ }
+
+ for stat in stats:
+ for i in ("T", "T-RESP-WR",):
+ totals[i][keys[0]] += stat[index][keys[0]]
+ for k in keys[1:]:
+ totals[i][k] += stat[index][i][k]
+
+ print "5 minute average response histogram"
+ table = tables.Table()
+ table.addHeader(
+ ("", "<10ms", "10ms<->100ms", "100ms<->1s", "1s<->10s", "10s<->30s", "30s<->60s", ">60s", "Over 1s", "Over 10s"),
+ )
+ table.setDefaultColumnFormats(
+ (
+ tables.Table.ColumnFormat("%s", tables.Table.ColumnFormat.CENTER_JUSTIFY),
+ tables.Table.ColumnFormat("%d (%.1f%%)", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
+ tables.Table.ColumnFormat("%d (%.1f%%)", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
+ tables.Table.ColumnFormat("%d (%.1f%%)", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
+ tables.Table.ColumnFormat("%d (%.1f%%)", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
+ tables.Table.ColumnFormat("%d (%.1f%%)", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
+ tables.Table.ColumnFormat("%d (%.1f%%)", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
+ tables.Table.ColumnFormat("%d (%.1f%%)", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
+ tables.Table.ColumnFormat("%.1f%%", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
+ tables.Table.ColumnFormat("%.1f%%", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
+ )
+ )
+ for i in ("T", "T-RESP-WR",):
+ table.addRow((
+ "Overall Response" if i == "T" else "Response without Write",
+ (totals[i]["<10ms"], safeDivision(totals[i]["<10ms"], totals[i]["requests"], 100.0)),
+ (totals[i]["10ms<->100ms"], safeDivision(totals[i]["10ms<->100ms"], totals[i]["requests"], 100.0)),
+ (totals[i]["100ms<->1s"], safeDivision(totals[i]["100ms<->1s"], totals[i]["requests"], 100.0)),
+ (totals[i]["1s<->10s"], safeDivision(totals[i]["1s<->10s"], totals[i]["requests"], 100.0)),
+ (totals[i]["10s<->30s"], safeDivision(totals[i]["10s<->30s"], totals[i]["requests"], 100.0)),
+ (totals[i]["30s<->60s"], safeDivision(totals[i]["30s<->60s"], totals[i]["requests"], 100.0)),
+ (totals[i][">60s"], safeDivision(totals[i][">60s"], totals[i]["requests"], 100.0)),
+ safeDivision(totals[i]["Over 1s"], totals[i]["requests"], 100.0),
+ safeDivision(totals[i]["Over 10s"], totals[i]["requests"], 100.0),
+ ))
+ os = StringIO()
+ table.printTable(os=os)
+ print os.getvalue()
+
+
def usage(error_msg=None):
if error_msg:
print error_msg
@@ -162,6 +319,7 @@
-h Print this help and exit
-s Name of local socket to read from
-t Delay in seconds between each sample [10 seconds]
+ --tcp host:port Use TCP connection with host:port
Description:
This utility will print a summary of statistics read from a
@@ -177,25 +335,22 @@
if __name__ == '__main__':
delay = 10
- sockname = "data/Logs/state/caldavd-stats.sock"
+ servers = ("data/Logs/state/caldavd-stats.sock",)
+ useTCP = False
- options, args = getopt.getopt(sys.argv[1:], "hs:t:", [])
+ options, args = getopt.getopt(sys.argv[1:], "hs:t:", ["tcp=",])
for option, value in options:
if option == "-h":
usage()
elif option == "-s":
- sockname = value
+ servers = value.split(",")
elif option == "-t":
delay = int(value)
+ elif option == "--tcp":
+ servers = [(host, int(port),) for host, port in [server.split(":") for server in value.split(",")]]
+ useTCP = True
while True:
- try:
- printStats(readSock(sockname))
- except socket.error:
- printFailedStats("Unable to read statistics from server socket: %s" % (sockname,))
- except KeyError, e:
- printFailedStats("Unable to find key '%s' in statistics from server socket" % (e,))
- sys.exit(1)
-
+ printStats([readSock(server, useTCP) for server in servers])
time.sleep(delay)
Modified: CalendarServer/trunk/twistedcaldav/stdconfig.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/stdconfig.py 2012-09-04 15:24:11 UTC (rev 9766)
+++ CalendarServer/trunk/twistedcaldav/stdconfig.py 2012-09-04 16:14:07 UTC (rev 9767)
@@ -446,7 +446,12 @@
"AccountingPrincipals": [],
"AccountingLogRoot" : "accounting",
- "GlobalStatsSocket" : "caldavd-stats.sock",
+ "Stats" : {
+ "EnableUnixStatsSocket" : False,
+ "UnixStatsSocket" : "caldavd-stats.sock",
+ "EnableTCPStatsSocket" : False,
+ "TCPStatsPort" : 8100,
+ },
"LogDatabase" : {
"LabelsInSQL" : False,
@@ -1027,7 +1032,7 @@
("LogRoot", ("LogDatabase", "StatisticsLogFile",)),
("LogRoot", "AccountingLogRoot"),
("RunRoot", "PIDFile"),
- ("RunRoot", "GlobalStatsSocket"),
+ ("RunRoot", ("Stats", "UnixStatsSocket",)),
("RunRoot", "ControlSocket"),
]
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20120904/b3216554/attachment-0001.html>
More information about the calendarserver-changes
mailing list