Merge pull request #196 from albertosottile/master

Allow collection of server statistical data
This commit is contained in:
Etoh 2018-07-28 18:56:39 +01:00 committed by GitHub
commit b3a7ed836c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 81 additions and 12 deletions

View File

@ -53,6 +53,7 @@ DIFFERENT_DURATION_THRESHOLD = 2.5
PROTOCOL_TIMEOUT = 12.5 PROTOCOL_TIMEOUT = 12.5
RECONNECT_RETRIES = 999 RECONNECT_RETRIES = 999
SERVER_STATE_INTERVAL = 1 SERVER_STATE_INTERVAL = 1
SERVER_STATS_SNAPSHOT_INTERVAL = 3600
WARNING_OSD_MESSAGES_LOOP_INTERVAL = 1 WARNING_OSD_MESSAGES_LOOP_INTERVAL = 1
AUTOPLAY_DELAY = 3.0 AUTOPLAY_DELAY = 3.0
DO_NOT_RESET_POSITION_THRESHOLD = 1.0 DO_NOT_RESET_POSITION_THRESHOLD = 1.0

View File

@ -434,7 +434,8 @@ de = {
"server-motd-argument": "Pfad zur Datei, von der die Nachricht des Tages geladen wird", "server-motd-argument": "Pfad zur Datei, von der die Nachricht des Tages geladen wird",
"server-chat-argument": "Should chat be disabled?", # TODO: Translate "server-chat-argument": "Should chat be disabled?", # TODO: Translate
"server-chat-maxchars-argument": "Maximum number of characters in a chat message (default is {})", # TODO: Translate "server-chat-maxchars-argument": "Maximum number of characters in a chat message (default is {})", # TODO: Translate
"server-maxusernamelength-argument": "Maximum number of charactrs in a username (default is {})", # TODO: Translate "server-maxusernamelength-argument": "Maximum number of characters in a username (default is {})", # TODO: Translate
"server-stats-db-file-argument": "Enable server stats using the SQLite db file provided", # TODO: Translate
"server-messed-up-motd-unescaped-placeholders": "Die Nachricht des Tages hat unmaskierte Platzhalter. Alle $-Zeichen sollten verdoppelt werden ($$).", "server-messed-up-motd-unescaped-placeholders": "Die Nachricht des Tages hat unmaskierte Platzhalter. Alle $-Zeichen sollten verdoppelt werden ($$).",
"server-messed-up-motd-too-long": "Die Nachricht des Tages ist zu lang - Maximal {} Zeichen, aktuell {}.", "server-messed-up-motd-too-long": "Die Nachricht des Tages ist zu lang - Maximal {} Zeichen, aktuell {}.",

View File

@ -437,8 +437,9 @@ en = {
"server-disable-ready-argument": "disable readiness feature", "server-disable-ready-argument": "disable readiness feature",
"server-motd-argument": "path to file from which motd will be fetched", "server-motd-argument": "path to file from which motd will be fetched",
"server-chat-argument": "Should chat be disabled?", "server-chat-argument": "Should chat be disabled?",
"server-chat-maxchars-argument": "Maximum number of characters in a chat message (default is {})", # Default number of characters "server-chat-maxchars-argument": "Maximum number of characters in a chat message (default is {})", # Default number of characters
"server-maxusernamelength-argument": "Maximum number of charactrs in a username (default is {})", "server-maxusernamelength-argument": "Maximum number of characters in a username (default is {})",
"server-stats-db-file-argument": "Enable server stats using the SQLite db file provided",
"server-messed-up-motd-unescaped-placeholders": "Message of the Day has unescaped placeholders. All $ signs should be doubled ($$).", "server-messed-up-motd-unescaped-placeholders": "Message of the Day has unescaped placeholders. All $ signs should be doubled ($$).",
"server-messed-up-motd-too-long": "Message of the Day is too long - maximum of {} chars, {} given.", "server-messed-up-motd-too-long": "Message of the Day is too long - maximum of {} chars, {} given.",

View File

@ -437,8 +437,9 @@ it = {
"server-disable-ready-argument": "disabilita la funzionalità \"pronto\"", "server-disable-ready-argument": "disabilita la funzionalità \"pronto\"",
"server-motd-argument": "percorso del file da cui verrà letto il messaggio del giorno", "server-motd-argument": "percorso del file da cui verrà letto il messaggio del giorno",
"server-chat-argument": "abilita o disabilita la chat", "server-chat-argument": "abilita o disabilita la chat",
"server-chat-maxchars-argument": "Numero massimo di caratteri in un messaggio di chat (default è {})", # Default number of characters "server-chat-maxchars-argument": "Numero massimo di caratteri in un messaggio di chat (default è {})", # Default number of characters
"server-maxusernamelength-argument": "Maximum number of charactrs in a username (default is {})", # TODO: Translate "server-maxusernamelength-argument": "Numero massimo di caratteri in un nome utente (default è {})",
"server-stats-db-file-argument": "Abilita la raccolta dei dati statistici nel file SQLite indicato",
"server-messed-up-motd-unescaped-placeholders": "Il messaggio del giorno ha dei caratteri non 'escaped'. Tutti i simboli $ devono essere doppi ($$).", "server-messed-up-motd-unescaped-placeholders": "Il messaggio del giorno ha dei caratteri non 'escaped'. Tutti i simboli $ devono essere doppi ($$).",
"server-messed-up-motd-too-long": "Il messaggio del giorno è troppo lungo - numero massimo di caratteri è {}, {} trovati.", "server-messed-up-motd-too-long": "Il messaggio del giorno è troppo lungo - numero massimo di caratteri è {}, {} trovati.",

View File

@ -440,9 +440,10 @@ ru = {
"server-motd-argument": "путь к файлу, из которого будет извлекаться MOTD-сообщение", "server-motd-argument": "путь к файлу, из которого будет извлекаться MOTD-сообщение",
"server-chat-argument": "Should chat be disabled?", # TODO: Translate "server-chat-argument": "Should chat be disabled?", # TODO: Translate
"server-chat-maxchars-argument": "Maximum number of characters in a chat message (default is {})", # TODO: Translate "server-chat-maxchars-argument": "Maximum number of characters in a chat message (default is {})", # TODO: Translate
"server-maxusernamelength-argument": "Maximum number of charactrs in a username (default is {})", # TODO: Translate "server-maxusernamelength-argument": "Maximum number of characters in a username (default is {})", # TODO: Translate
"server-messed-up-motd-unescaped-placeholders": "MOTD-сообщение содержит неэкранированные спец.символы. Все знаки $ должны быть продублированы ($$).", "server-stats-db-file-argument": "Enable server stats using the SQLite db file provided", # TODO: Translate
"server-messed-up-motd-too-long": "MOTD-сообщение слишком длинное: максимальная длина - {} символ(ов), текущая длина - {} символ(ов).", "server-messed-up-motd-unescaped-placeholders" : "MOTD-сообщение содержит неэкранированные спец.символы. Все знаки $ должны быть продублированы ($$).",
"server-messed-up-motd-too-long" : "MOTD-сообщение слишком длинное: максимальная длина - {} символ(ов), текущая длина - {} символ(ов).",
# Server errors # Server errors
"unknown-command-server-error": "Неизвестная команда: {}", # message "unknown-command-server-error": "Неизвестная команда: {}", # message

View File

@ -1,4 +1,3 @@
import argparse import argparse
import codecs import codecs
import hashlib import hashlib
@ -7,6 +6,7 @@ import random
import time import time
from string import Template from string import Template
from twisted.enterprise import adbapi
from twisted.internet import task, reactor from twisted.internet import task, reactor
from twisted.internet.protocol import Factory from twisted.internet.protocol import Factory
@ -18,11 +18,12 @@ from syncplay.utils import RoomPasswordProvider, NotControlledRoom, RandomString
class SyncFactory(Factory): class SyncFactory(Factory):
def __init__(self, password='', motdFilePath=None, isolateRooms=False, salt=None, def __init__(self, port='', password='', motdFilePath=None, isolateRooms=False, salt=None,
disableReady=False, disableChat=False, maxChatMessageLength=constants.MAX_CHAT_MESSAGE_LENGTH, disableReady=False, disableChat=False, maxChatMessageLength=constants.MAX_CHAT_MESSAGE_LENGTH,
maxUsernameLength=constants.MAX_USERNAME_LENGTH): maxUsernameLength=constants.MAX_USERNAME_LENGTH, statsDbFile=None):
self.isolateRooms = isolateRooms self.isolateRooms = isolateRooms
print(getMessage("welcome-server-notification").format(syncplay.version)) print(getMessage("welcome-server-notification").format(syncplay.version))
self.port = port
if password: if password:
password = hashlib.md5(password).hexdigest() password = hashlib.md5(password).hexdigest()
self.password = password self.password = password
@ -39,6 +40,13 @@ class SyncFactory(Factory):
self._roomManager = RoomManager() self._roomManager = RoomManager()
else: else:
self._roomManager = PublicRoomManager() self._roomManager = PublicRoomManager()
if statsDbFile is not None:
self._statsDbHandle = DBManager(statsDbFile)
self._statsRecorder = StatsRecorder(self._statsDbHandle, self._roomManager)
statsDelay = 5*(int(self.port)%10 + 1)
self._statsRecorder.startRecorder(statsDelay)
else:
self._statsDbHandle = None
def buildProtocol(self, addr): def buildProtocol(self, addr):
return SyncServerProtocol(self) return SyncServerProtocol(self)
@ -186,6 +194,55 @@ class SyncFactory(Factory):
watcher.setPlaylistIndex(room.getName(), room.getPlaylistIndex()) watcher.setPlaylistIndex(room.getName(), room.getPlaylistIndex())
class StatsRecorder(object):
def __init__(self, dbHandle, roomManager):
self._dbHandle = dbHandle
self._roomManagerHandle = roomManager
def startRecorder(self, delay):
try:
self._dbHandle.connect()
reactor.callLater(delay, self._scheduleClientSnapshot)
except:
print("--- Error in initializing the stats database. Server Stats not enabled. ---")
def _scheduleClientSnapshot(self):
self._clientSnapshotTimer = task.LoopingCall(self._runClientSnapshot)
self._clientSnapshotTimer.start(constants.SERVER_STATS_SNAPSHOT_INTERVAL)
def _runClientSnapshot(self):
try:
snapshotTime = int(time.time())
rooms = self._roomManagerHandle.exportRooms()
for room in rooms.values():
for watcher in room.getWatchers():
self._dbHandle.addVersionLog(snapshotTime, watcher.getVersion())
except:
pass
class DBManager(object):
def __init__(self, dbpath):
self._dbPath = dbpath
self._connection = None
def __del__(self):
if self._connection is not None:
self._connection.close()
def connect(self):
self._connection = adbapi.ConnectionPool("sqlite3", self._dbPath, check_same_thread=False)
self._createSchema()
def _createSchema(self):
initQuery = 'create table if not exists clients_snapshots (snapshot_time integer, version string)'
self._connection.runQuery(initQuery)
def addVersionLog(self, timestamp, version):
content = (timestamp, version, )
self._connection.runQuery("INSERT INTO clients_snapshots VALUES (?, ?)", content)
class RoomManager(object): class RoomManager(object):
def __init__(self): def __init__(self):
self._rooms = {} self._rooms = {}
@ -245,6 +302,9 @@ class RoomManager(object):
username += '_' username += '_'
return username return username
def exportRooms(self):
return self._rooms
class PublicRoomManager(RoomManager): class PublicRoomManager(RoomManager):
def broadcast(self, sender, what): def broadcast(self, sender, what):
@ -562,3 +622,4 @@ class ConfigurationGetter(object):
self._argparser.add_argument('--motd-file', metavar='file', type=str, nargs='?', help=getMessage("server-motd-argument")) self._argparser.add_argument('--motd-file', metavar='file', type=str, nargs='?', help=getMessage("server-motd-argument"))
self._argparser.add_argument('--max-chat-message-length', metavar='maxChatMessageLength', type=int, nargs='?', help=getMessage("server-chat-maxchars-argument").format(constants.MAX_CHAT_MESSAGE_LENGTH)) self._argparser.add_argument('--max-chat-message-length', metavar='maxChatMessageLength', type=int, nargs='?', help=getMessage("server-chat-maxchars-argument").format(constants.MAX_CHAT_MESSAGE_LENGTH))
self._argparser.add_argument('--max-username-length', metavar='maxUsernameLength', type=int, nargs='?', help=getMessage("server-maxusernamelength-argument").format(constants.MAX_USERNAME_LENGTH)) self._argparser.add_argument('--max-username-length', metavar='maxUsernameLength', type=int, nargs='?', help=getMessage("server-maxusernamelength-argument").format(constants.MAX_USERNAME_LENGTH))
self._argparser.add_argument('--stats-db-file', metavar='file', type=str, nargs='?', help=getMessage("server-stats-db-file-argument"))

View File

@ -22,11 +22,14 @@ if __name__ == '__main__':
reactor.listenTCP( reactor.listenTCP(
int(args.port), int(args.port),
SyncFactory( SyncFactory(
args.port,
args.password, args.password,
args.motd_file, args.motd_file,
args.isolate_rooms, args.isolate_rooms,
args.salt, args.salt,
args.disable_ready, args.disable_ready,
args.disable_chat, args.disable_chat,
args.max_chat_message_length)) args.max_chat_message_length,
args.max_username_length,
args.stats_db_file))
reactor.run() reactor.run()