Page MenuHomeWickedGov Phorge

eventlogging-devserver
No OneTemporary

Size
5 KB
Referenced Files
None
Subscribers
None

eventlogging-devserver

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
eventlogging-devserver
----------------------
Invoking this command-line tool will spawn a web server that can serve
as EventLogging endpoint during MediaWiki development. Events logged to this
server will be validated verbosely and pretty-printed to the terminal.
Example:
event : {"wiki": "devwiki", "schema": "TrackedPageContentSaveComplete", "revision": 7872558, "event": {"revId": 10, "token": "foobar"}}
url : http://localhost:8080/event?%7B%22wiki%22%3A+%22devwiki%22%2C+%22schema%22%3A+%22TrackedPageContentSaveComplete%22%2C+%22revision%22%3A+7872558%2C+%22event%22%3A+%7B%22revId%22%3A+10%2C+%22token%22%3A+%22foobar%22%7D%7D
usage: eventlogging-devserver [-h] [--host HOST] [--port PORT]
optional arguments:
-h, --help show this help message and exit
--host HOST server host (default: 'localhost')
--port PORT server port (default: 8080)
-v, --verbose print pretty colors to stderr
:copyright: (c) 2012 by Ori Livneh <ori@wikimedia.org>
:license: GNU General Public Licence 2.0 or later
"""
import sys
import argparse
import itertools
import json
from wsgiref.simple_server import make_server, WSGIRequestHandler
import eventlogging.parse
import eventlogging.schema
import jsonschema
from pygments.console import ansiformat
argparser = argparse.ArgumentParser(fromfile_prefix_chars='@')
argparser.add_argument('--host', default='localhost',
help='server host (default: localhost)')
argparser.add_argument('--port', default=8080, type=int,
help='server port (default: 8080)')
argparser.add_argument('--verbose', '-v', action='store_true',
help='print out events to stderr as they come in')
args = argparser.parse_args()
colorize = ansiformat
seq_ids = itertools.count()
parser = eventlogging.parse.LogParser('%q %{recvFrom}s %{seqId}d %t %h')
log_fmt = ('?%(QUERY_STRING)s %(SERVER_NAME)s '
'%(SEQ_ID)d %(TIME)s %(REMOTE_ADDR)s')
max_qs_size = 900
def heading(caption=None):
if caption is None:
return 77 * '-'
return '-- {:-<95}'.format(colorize('*yellow*', caption) + ' ')
class EventLoggingHandler(WSGIRequestHandler):
"""WSGI request handler; annotates environ dict with seq ID and
timestamp in NCSA Common Log Format."""
def get_environ(self):
environ = WSGIRequestHandler.get_environ(self)
environ.update(SEQ_ID=next(seq_ids), TIME=eventlogging.parse.utcnow())
return environ
def log_message(self, format, *args):
pass # We'll handle logging in the WSGI app.
def validate(log_line):
"""Parse and validate a log line containing an encapsulated event.
Returns a tuple of (event, errors). If no object was decoded,
'event' will be None."""
try:
event = parser.parse(log_line)
except ValueError as err:
return None, [err]
try:
scid = event['schema'], event['revision']
except KeyError as err:
return event, [err]
try:
schema = eventlogging.schema.get_schema(scid, encapsulate=True)
except jsonschema.SchemaError as err:
return event, [err]
validator = jsonschema.Draft3Validator(schema)
return event, list(validator.iter_errors(event))
def validate_size(environ):
"""Check whether the query string respects the maximum size.
Returns a list with a ValueError if the size exceeds the maximum.
Returns an empty list otherwise."""
if 'QUERY_STRING' in environ:
qs_size = len(environ['QUERY_STRING'])
if qs_size > max_qs_size:
return [ValueError(
'Query string size (%d) is greater than max size (%d)' %
(qs_size, max_qs_size))]
return []
def handle_event(environ, start_response):
"""WSGI app; parses, validates and pretty-prints incoming event
requests."""
log_line = log_fmt % environ
event, errors = validate(log_line)
errors.extend(validate_size(environ))
headers = {
'Server': 'eventlogging-devserver',
'Requested-Event-Valid': str(int(not errors))
}
for i, error in enumerate(errors):
headers['Validation-Error-%d' % (i + 1)] = str(error)
status = '204 No Content'
headers = list(headers.items())
start_response(status, headers)
sys.stdout.write(json.dumps(event) + "\n")
if args.verbose:
print(heading('request'))
print(log_line)
print(heading('event'))
pretty_json = json.dumps(event, indent=2, sort_keys=True)
print(pretty_json)
print(heading('validation'))
for error in errors:
print(colorize('red', 'Error:'), error)
if not errors:
print(colorize('green', 'Valid.'))
print(heading())
return []
httpd = make_server(args.host, args.port, handle_event,
handler_class=EventLoggingHandler)
sys.stderr.write('''
___ _
/ (_) \_|_) o
\__ _ _ _ _|_ | __ __, __, _ _ __,
/ | |_|/ / |/ | | _| / \_/ | / | | / |/ | / |
\___/ \/ |__/ | |_/|_/(/\___/\__/ \_/|/\_/|/|_/ | |_/\_/|/
-----------------------------------------/|---/|--------------/|----------
(C) Wikimedia Foundation, 2020 \| \| \|
# Ensure the following values are set in LocalSettings.php:''' +
colorize('green', '''
wfLoadExtension('EventLogging');
$wgEventLoggingBaseUri = 'http://localhost:8080/event';
'''))
sys.stderr.write(heading('Listening to events...') + "\n")
try:
httpd.serve_forever()
except KeyboardInterrupt:
pass

File Metadata

Mime Type
text/x-script.python
Expires
Wed, Aug 19, 15:46 (3 w, 6 d ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
2e/2c/0af828189a0ec6403250edb5c925
Default Alt Text
eventlogging-devserver (5 KB)

Event Timeline