conference-stats/jitsi-log.py

274 lines
8.4 KiB
Python
Raw Normal View History

2020-06-14 15:52:08 +02:00
#!/bin/env python3
#
# simple log parser and status server for the jitsi meet video conference tool
#
2020-06-23 18:52:07 +02:00
# Reik Kaps <r31k@k4p5.de> 2020 for Leinelab Hannover
2020-06-14 15:52:08 +02:00
import re
import sys
import threading
from os.path import basename
from datetime import datetime
from argparse import ArgumentParser
from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib.parse import urlparse, parse_qs
2020-06-14 15:52:08 +02:00
try:
from sh import tail
from pybadges import badge
2020-06-14 15:52:08 +02:00
except ImportError as e:
print('[Error] Please install sh via pip! {}'.format(e))
sys.exit(1)
2020-06-18 18:45:11 +02:00
# active room dictionary
roomList = {}
# @see https://regex101.com/r/R1B1OL/1
2020-06-23 18:52:07 +02:00
# general regex for a jicofo log line
2020-06-18 18:45:11 +02:00
regex = r"(Jicofo)\s(\d+-\d+-\d+)\s(\d+\:\d+\:\d+\.\d+)\s(\w+):\s\[(\d+)\]\s(.*)\(\)\s(.*)"
prog = re.compile(regex)
##
# new jitsi room
2020-06-23 18:52:07 +02:00
#
2020-06-18 18:45:11 +02:00
room_regex = r"(^Joining\sthe\sroom):(.*)"
room = re.compile(room_regex)
# disposed room
disposed_regex = r"^Disposed\sconference\sfor\sroom\:\s(.*)@(.*)\sconference\scount:\s(\d+)"
disposed = re.compile(disposed_regex)
##
# get the room owner
owner_regex = r"Granted\sowner\sto\s([a-z0-9-]+)\@(.*)\/(.*)"
owner = re.compile(owner_regex)
# get member for room
2020-06-19 22:53:10 +02:00
member_regex = r"^Member\s(.*)@(.*)\/(.*)\s(joined)."
2020-06-18 18:45:11 +02:00
member = re.compile(member_regex)
2020-06-19 22:18:05 +02:00
# remove/leaving member
2020-06-19 22:53:10 +02:00
leaving_regex = r"^Member\s(.*)@(.*)\/(.*)\sis\s(leaving)"
2020-06-19 22:18:05 +02:00
leaving = re.compile(leaving_regex)
2020-06-18 18:45:11 +02:00
2020-06-14 15:52:08 +02:00
class MeetingRoom():
"""
represent a Jitsi Meetingroom
2020-06-23 18:52:07 +02:00
2020-06-14 15:52:08 +02:00
jid: eg room@conference.meet.leinelab.org
ctime: eg 2020-06-01 12:32:02 ..
"""
def __init__(self, jid, ctime):
self.jid = jid
self.ctime = ctime
2020-06-18 23:43:49 +02:00
self.members = []
2020-06-14 15:52:08 +02:00
def setOwner(self, name):
self.owner = name
def getOwner(self):
if hasattr(self, 'owner'):
return self.owner
else:
return None
def addMember(self, name):
self.members.append(name)
2020-06-19 22:42:07 +02:00
def delMember(self, name):
try:
self.members.remove(name)
except ValueError as e:
2020-06-23 18:52:07 +02:00
raise
2020-06-19 22:42:07 +02:00
2020-06-14 15:52:08 +02:00
def getMembers(self):
return self.members
def __str__(self):
2020-06-23 18:52:07 +02:00
return '{}: {}'.format(self.jid, len(self.members))
2020-06-14 15:52:08 +02:00
2020-06-19 22:18:05 +02:00
2020-06-14 15:52:08 +02:00
class logThread(threading.Thread):
def __init__(self, threadID, name, filename):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.filename = filename
def run(self):
while True:
getLogMsg(self.filename)
class MyServer(BaseHTTPRequestHandler):
2020-06-23 18:52:07 +02:00
def do_response(self, response_string, ctype="text/plain"):
self.send_response(200)
2020-06-23 18:52:07 +02:00
self.send_header("Content-type", ctype)
self.end_headers()
self.wfile.write(response_string)
2020-06-14 15:52:08 +02:00
def do_GET(self):
query_components = parse_qs(urlparse(self.path).query)
if "format" in query_components:
respones_format = query_components["format"][0]
else:
respones_format = 'text'
room_name = basename(self.path.split('?')[0])
print('{} - {}'.format(query_components, room_name))
if room_name in roomList:
if respones_format == 'json':
pass
elif respones_format == 'svg':
2020-06-23 18:52:07 +02:00
participants = len(roomList[room_name].members)
if participants > 1:
rtext = '{} participants'.format(str(participants))
else:
rtext = '{} participant'.format(str(participants))
s = badge(left_text='{}: {}'.format('Room', room_name),
2020-06-23 18:52:07 +02:00
right_text=rtext,
left_color='blue',
right_color='green')
response_string = bytes(s, "utf-8")
2020-06-23 18:52:07 +02:00
self.do_response(response_string, "image/svg+xml")
else:
# default text response
response_string = bytes(room_name
+ '|'
+ str(roomList[room_name].ctime)
+ '|'
+ str(len(roomList[room_name].members))
+ '|', "utf-8")
2020-06-23 18:52:07 +02:00
self.do_response(response_string)
2020-06-14 15:52:08 +02:00
else:
if respones_format == 'svg':
s = badge(left_text='{}: {}'.format('Room', room_name),
right_text='empty',
left_color='red',
right_color='darkgray')
response_string = bytes(s, "utf-8")
2020-06-23 18:52:07 +02:00
self.text_response(response_string, "image/svg+xml")
else:
2020-06-23 18:52:07 +02:00
# text or other formats
# return http code 404 (room) not found
self.send_response(404)
self.end_headers()
2020-06-14 15:52:08 +02:00
2020-06-23 18:52:07 +02:00
def process_line(line: str):
2020-06-14 15:52:08 +02:00
"""
process a logfile line, using global object and vars
globals: prog, disposed, owner, member, roomList
2020-06-23 18:52:07 +02:00
Attributes
----------
line: str
a line form the logfile
2020-06-14 15:52:08 +02:00
"""
erg = prog.match(line)
if erg:
# detect room creation
new_room = room.match(erg.group(7))
if new_room:
2020-06-23 18:52:07 +02:00
room_name = new_room.group(2).split("@")[0].strip()
room_date = datetime.fromisoformat('{} {}'.
format(erg.group(2),
erg.group(3)))
2020-06-19 22:18:05 +02:00
print('{} {} created'.format(room_date, room_name))
2020-06-14 15:52:08 +02:00
currentRoom = MeetingRoom(room_name, room_date)
if room_name in roomList:
# delete old room and recreate it
del roomList[room_name]
roomList[room_name] = currentRoom
else:
roomList[room_name] = currentRoom
disposed_room = disposed.match(erg.group(7))
if disposed_room:
room_name = disposed_room.group(1)
print('-> room disposed: {}'.format(room_name))
if room_name in roomList:
del roomList[room_name]
2020-06-19 22:18:05 +02:00
2020-06-14 15:52:08 +02:00
owner_match = owner.match(erg.group(7))
if owner_match:
room_name = owner_match.group(1).strip()
room_owner = owner_match.group(3).strip()
if room_name in roomList:
roomList[room_name].setOwner(room_owner)
member_match = member.match(erg.group(7))
if member_match:
room_name = member_match.group(1).strip()
room_member = member_match.group(3).strip()
2020-06-19 22:18:05 +02:00
print('{} enters {}'.format(room_member, room_name))
2020-06-14 15:52:08 +02:00
if room_name in roomList:
roomList[room_name].addMember(room_member)
2020-06-19 22:18:05 +02:00
# participant is leaving a the room
2020-06-23 18:52:07 +02:00
leaving_match = leaving.match(erg.group(7))
2020-06-19 22:18:05 +02:00
if leaving_match:
2020-06-23 18:52:07 +02:00
room_name = leaving_match.group(1).strip()
room_member = leaving_match.group(3).strip()
2020-06-19 22:18:05 +02:00
print('{} leaves {}'.format(room_member, room_name))
if room_name in roomList:
roomList[room_name].delMember(room_member)
2020-06-14 15:52:08 +02:00
2020-06-23 18:52:07 +02:00
def getLogMsg(logfile: str):
2020-06-14 15:52:08 +02:00
"""
get log messages and commit it to process_line
2020-06-23 18:52:07 +02:00
used tail from sh module
2020-06-14 15:52:08 +02:00
2020-06-23 18:52:07 +02:00
Attributes:
-----------
logfile: str
path as string to logfile
"""
2020-06-14 15:52:08 +02:00
for line in tail("-f", logfile, _iter=True):
process_line(line)
2020-06-18 18:45:11 +02:00
2020-06-14 15:52:08 +02:00
if __name__ == '__main__':
# start cmdline processing
parser = ArgumentParser(description='Jitsi Meet Conference Stats')
parser.add_argument('--log', '-l',
2020-06-18 18:45:11 +02:00
help='full path to jicofo logfile eg.' +
' /var/log/jitsi/jicofo.log')
2020-06-14 15:52:08 +02:00
parser.add_argument('--verbose', '-v',
2020-06-18 18:45:11 +02:00
help='be verbose eg. for debugging',
action='store_true')
2020-06-19 22:18:05 +02:00
parser.add_argument('--port', '-p',
help='set the http server port',
default=9999)
2020-06-14 15:52:08 +02:00
args = parser.parse_args()
if args.log:
try:
loggerThread = logThread(1, "Thread-Logger", args.log)
loggerThread.start()
2020-06-19 22:18:05 +02:00
except Exception as e:
print('Error: unable to start threads {}'.format(e))
2020-06-14 15:52:08 +02:00
hostName = 'localhost'
2020-06-19 22:18:05 +02:00
serverPort = args.port
2020-06-14 15:52:08 +02:00
webServer = HTTPServer((hostName, serverPort), MyServer)
try:
print("Server started http://%s:%s" % (hostName, serverPort))
webServer.serve_forever()
except:
pass