Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F4141024
eventlogging-devserver
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
5 KB
Referenced Files
None
Subscribers
None
eventlogging-devserver
View Options
#!/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
Details
Attached
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)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment