Browse Source

Proper log rendering

master
JustAnotherArchivist 3 years ago
parent
commit
73941d6357
1 changed files with 31 additions and 6 deletions
  1. +31
    -6
      irclog.py

+ 31
- 6
irclog.py View File

@@ -5,6 +5,8 @@ import base64
import collections
import datetime
import functools
import hashlib
import html
import importlib.util
import inspect
import ircstates
@@ -741,19 +743,42 @@ class WebServer:
lines.append(f'{"(PW) " if auth else ""}<a href="/{path}/today">{channel}</a>')
return aiohttp.web.Response(text = f'<html><body>{"<br />".join(lines)}</body></html>', content_type = 'text/html')

def _raw_to_lines(self, f, filter = lambda dt, command, content: True):
# f: iterable producing str lines (e.g. file-like) on iteration or bytes
# filter: function taking the line fields (ts: float, command: str, content: str) and returning whether to include the line
if isinstance(f, bytes):
f = f.decode('utf-8').splitlines()
for line in f:
ts, command, content = line.strip().split(' ', 2)
ts = float(ts)
if not filter(ts, command, content):
continue
yield ts, command, content

def _render_log(self, lines, path, withDate = False):
# lines: iterable of (timestamp: float, command: str, content: str)
# withDate: whether to include the date with the time of the log line
ret = []
for ts, command, content in lines:
d = datetime.datetime.utcfromtimestamp(ts).replace(tzinfo = datetime.timezone.utc)
date = f'{d:%Y-%m-%d }' if withDate else ''
lineId = hashlib.md5(f'{ts} {command} {content}'.encode('utf-8')).hexdigest()[:8]
ret.append(f'<tr id="l{lineId}" class="command_{html.escape(command)}"><td><a href="/{html.escape(path)}/{d:%Y-%m-%d}#l{lineId}">{date}{d:%H:%M:%S}</a></td><td>{html.escape(content)}</td></tr>')
return '<table>\n' + '\n'.join(ret) + '\n</table>'

async def get_log(self, request):
self.logger.info(f'Received request {id(request)} from {request.remote!r} for {request.path!r}')
if request.match_info['date'] == 'today':
date = datetime.datetime.now(tz = datetime.timezone.utc).replace(hour = 0, minute = 0, second = 0, microsecond = 0)
else:
date = datetime.datetime.strptime(request.match_info['date'], '%Y-%m-%d').replace(tzinfo = datetime.timezone.utc)
dateStart = date.timestamp()
dateEnd = (date + datetime.timedelta(days = 1)).timestamp()
#TODO Implement this in a better way...
fn = date.strftime('%Y-%m.log')
with open(os.path.join(self.config['storage']['path'], request.match_info["path"], fn), 'r') as fp:
lines = [l.strip().split(' ', 2) for l in fp]
dateStart = date.timestamp()
dateEnd = (date + datetime.timedelta(days = 1)).timestamp()
return aiohttp.web.Response(text = f'<html><body><a href="/{request.match_info["path"]}/{(date - datetime.timedelta(days = 1)).strftime("%Y-%m-%d")}">Previous day</a> <a href="/{request.match_info["path"]}/{(date + datetime.timedelta(days = 1)).strftime("%Y-%m-%d")}">Next day</a><br /><br />' + '<br />'.join(' '.join(l) for l in lines if dateStart <= float(l[0]) < dateEnd) + '</body></html>', content_type = 'text/html')
lines = list(self._raw_to_lines(fp, filter = lambda ts, command, content: dateStart <= ts <= dateEnd))
return aiohttp.web.Response(text = f'<html><body><a href="/{request.match_info["path"]}/{(date - datetime.timedelta(days = 1)).strftime("%Y-%m-%d")}">Previous day</a> <a href="/{request.match_info["path"]}/{(date + datetime.timedelta(days = 1)).strftime("%Y-%m-%d")}">Next day</a><br /><br />' + self._render_log(lines, request.match_info['path']) + '</body></html>', content_type = 'text/html')

async def search(self, request):
self.logger.info(f'Received request {id(request)} from {request.remote!r} for {request.path!r}')
@@ -761,10 +786,10 @@ class WebServer:
if 'q' not in request.query:
return aiohttp.web.Response(text = '<html><body><form><input name="q" /><input type="submit" value="Search!" /></form></body></html>', content_type = 'text/html')

proc = await asyncio.create_subprocess_exec('grep', '--fixed-strings', '--recursive', request.query['q'], os.path.join(self.config['storage']['path'], request.match_info["path"], ''), stdout = asyncio.subprocess.PIPE)
proc = await asyncio.create_subprocess_exec('grep', '--fixed-strings', '--recursive', '--no-filename', request.query['q'], os.path.join(self.config['storage']['path'], request.match_info['path'], ''), stdout = asyncio.subprocess.PIPE)
#TODO Limit size and runtime
stdout, _ = await proc.communicate()
return aiohttp.web.Response(text = '<html><body>' + '<br />'.join(stdout.decode('utf-8').splitlines()) + '</body></html>', content_type = 'text/html')
return aiohttp.web.Response(text = '<html><body>' + self._render_log(self._raw_to_lines(stdout), request.match_info['path'], withDate = True) + '</body></html>', content_type = 'text/html')


def configure_logging(config):


Loading…
Cancel
Save