server: add --stats-file= scaffolding

no actual stats yet
This commit is contained in:
Brian Warner 2016-06-25 11:05:28 -07:00
parent cfcd74db4b
commit 7f389dc76e
4 changed files with 39 additions and 7 deletions

View File

@ -48,8 +48,13 @@ def server(ctx):
"--signal-error", is_flag=True, "--signal-error", is_flag=True,
help="force all clients to fail with a message", help="force all clients to fail with a message",
) )
@click.option(
"--stats-file", default=None, type=type(u""), metavar="stats.json",
help="periodically write stats to a file for monitoring tools like Munin",
)
@click.pass_obj @click.pass_obj
def start(cfg, signal_error, no_daemon, blur_usage, advertise_version, transit, rendezvous): def start(cfg, signal_error, no_daemon, blur_usage, advertise_version,
transit, rendezvous, stats_file):
""" """
Start a relay server Start a relay server
""" """
@ -60,6 +65,7 @@ def start(cfg, signal_error, no_daemon, blur_usage, advertise_version, transit,
cfg.transit = str(transit) cfg.transit = str(transit)
cfg.rendezvous = str(rendezvous) cfg.rendezvous = str(rendezvous)
cfg.signal_error = signal_error cfg.signal_error = signal_error
cfg.stats_file = stats_file
start_server(cfg) start_server(cfg)
@ -92,8 +98,13 @@ def start(cfg, signal_error, no_daemon, blur_usage, advertise_version, transit,
"--signal-error", is_flag=True, "--signal-error", is_flag=True,
help="force all clients to fail with a message", help="force all clients to fail with a message",
) )
@click.option(
"--stats-file", default=None, type=type(u""), metavar="stats.json",
help="periodically write stats to a file for monitoring tools like Munin",
)
@click.pass_obj @click.pass_obj
def restart(cfg, signal_error, no_daemon, blur_usage, advertise_version, transit, rendezvous): def restart(cfg, signal_error, no_daemon, blur_usage, advertise_version,
transit, rendezvous, stats_file):
""" """
Re-start a relay server Re-start a relay server
""" """
@ -104,6 +115,7 @@ def restart(cfg, signal_error, no_daemon, blur_usage, advertise_version, transit
cfg.transit = str(transit) cfg.transit = str(transit)
cfg.rendezvous = str(rendezvous) cfg.rendezvous = str(rendezvous)
cfg.signal_error = signal_error cfg.signal_error = signal_error
cfg.stats_file = stats_file
restart_server(cfg) restart_server(cfg)

View File

@ -15,6 +15,7 @@ class MyPlugin:
self.args.advertise_version, self.args.advertise_version,
"relay.sqlite", self.args.blur_usage, "relay.sqlite", self.args.blur_usage,
signal_error=self.args.signal_error, signal_error=self.args.signal_error,
stats_file=self.args.stats_file,
) )
class MyTwistdConfig(twistd.ServerOptions): class MyTwistdConfig(twistd.ServerOptions):

View File

@ -1,5 +1,5 @@
from __future__ import print_function, unicode_literals from __future__ import print_function, unicode_literals
import os, time, random, base64 import os, time, random, base64, json
from collections import namedtuple from collections import namedtuple
from twisted.python import log from twisted.python import log
from twisted.application import service, internet from twisted.application import service, internet
@ -478,15 +478,16 @@ class AppNamespace:
channel._shutdown() channel._shutdown()
class Rendezvous(service.MultiService): class Rendezvous(service.MultiService):
def __init__(self, db, welcome, blur_usage): def __init__(self, db, welcome, blur_usage, stats_file=None):
service.MultiService.__init__(self) service.MultiService.__init__(self)
self._db = db self._db = db
self._welcome = welcome self._welcome = welcome
self._blur_usage = blur_usage self._blur_usage = blur_usage
self._stats_file = stats_file
log_requests = blur_usage is None log_requests = blur_usage is None
self._log_requests = log_requests self._log_requests = log_requests
self._apps = {} self._apps = {}
t = internet.TimerService(EXPIRATION_CHECK_PERIOD, self.prune_all_apps) t = internet.TimerService(EXPIRATION_CHECK_PERIOD, self.timer)
t.setServiceParent(self) t.setServiceParent(self)
def get_welcome(self): def get_welcome(self):
@ -517,6 +518,10 @@ class Rendezvous(service.MultiService):
apps.add(row["app_id"]) apps.add(row["app_id"])
return apps return apps
def timer(self):
self.prune_all_apps()
self.dump_stats(now=time.time(), validity=EXPIRATION_CHECK_PERIOD+60)
def prune_all_apps(self, now=None, old=None): def prune_all_apps(self, now=None, old=None):
# As with AppNamespace.prune_old_mailboxes, we log for now. # As with AppNamespace.prune_old_mailboxes, we log for now.
log.msg("beginning app prune") log.msg("beginning app prune")
@ -528,6 +533,20 @@ class Rendezvous(service.MultiService):
app.prune(now, old) app.prune(now, old)
log.msg("app prune ends, %d apps" % len(self._apps)) log.msg("app prune ends, %d apps" % len(self._apps))
def dump_stats(self, now, validity):
if not self._stats_file:
return
tmpfn = self._stats_file + ".tmp"
data = {}
data["created"] = now
data["valid_until"] = now + validity
with open(tmpfn, "wb") as f:
json.dump(data, f, indent=1)
f.write("\n")
os.rename(tmpfn, self._stats_file)
def stopService(self): def stopService(self):
# This forcibly boots any clients that are still connected, which # This forcibly boots any clients that are still connected, which
# helps with unit tests that use threads for both clients. One client # helps with unit tests that use threads for both clients. One client

View File

@ -28,7 +28,7 @@ class PrivacyEnhancedSite(server.Site):
class RelayServer(service.MultiService): class RelayServer(service.MultiService):
def __init__(self, rendezvous_web_port, transit_port, def __init__(self, rendezvous_web_port, transit_port,
advertise_version, db_url=":memory:", blur_usage=None, advertise_version, db_url=":memory:", blur_usage=None,
signal_error=None): signal_error=None, stats_file=None):
service.MultiService.__init__(self) service.MultiService.__init__(self)
self._blur_usage = blur_usage self._blur_usage = blur_usage
@ -52,7 +52,7 @@ class RelayServer(service.MultiService):
if signal_error: if signal_error:
welcome["error"] = signal_error welcome["error"] = signal_error
rendezvous = Rendezvous(db, welcome, blur_usage) rendezvous = Rendezvous(db, welcome, blur_usage, stats_file)
rendezvous.setServiceParent(self) # for the pruning timer rendezvous.setServiceParent(self) # for the pruning timer
root = Root() root = Root()