Skip to content

Commit e50e9f3

Browse files
committed
💩 🔊 Add additional logging to the request handling
We have been a mysterious 403 error during `/usercache/put` from some users only. However, we don't see the `usercache/put` in the application logs. This adds additional logging to the incoming call path on the webapp, allowing us to intercept messages earlier and figure out where the error is thrown from. This helps with e-mission/e-mission-docs#1127 We log the messages using `logging` instead of `print` so that they show up in the correct order with the rest of the log messages. We also log the raw token in the input hook so we can see what we receive before trying to map it to a local UUID. We also cannot get the length of an error so we need to check the output type first ``` Traceback (most recent call last): File "/usr/src/devapp/emission/net/api/bottle.py", line 1022, in _handle if len(out) > 40: TypeError: object of type 'HTTPError' has no len() ``` We also have to handle a `None` output
1 parent a6e4a8f commit e50e9f3

3 files changed

Lines changed: 30 additions & 1 deletion

File tree

emission/net/api/bottle.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"""
1515

1616
import sys
17+
import logging
1718

1819
__author__ = 'Marcel Hellkamp'
1920
__version__ = '0.13-dev'
@@ -470,6 +471,7 @@ def build(self, _name, *anons, **query):
470471

471472
def match(self, environ):
472473
""" Return a (target, url_args) tuple or raise HTTPError(400/404/405). """
474+
logging.debug(f"403_CHECK: bottle just tried to match {environ['REQUEST_METHOD']} {environ['PATH_INFO']}")
473475
verb = environ['REQUEST_METHOD'].upper()
474476
path = environ['PATH_INFO'] or '/'
475477

@@ -977,6 +979,7 @@ def default_error_handler(self, res):
977979
return tob(template(ERROR_PAGE_TEMPLATE, e=res, template_settings=dict(name='__ERROR_PAGE_TEMPLATE')))
978980

979981
def _handle(self, environ):
982+
logging.debug(f"403_CHECK: bottle just received {environ['REQUEST_METHOD']} {environ['PATH_INFO']}")
980983
path = environ['bottle.raw_path'] = environ['PATH_INFO']
981984
if py3k:
982985
environ['PATH_INFO'] = path.encode('latin1').decode('utf8', 'ignore')
@@ -985,18 +988,27 @@ def _handle(self, environ):
985988
request.bind(environ)
986989
response.bind()
987990

991+
try:
992+
logging.debug(f"403_CHECK: parsed body {request.json}")
993+
except Exception as e:
994+
print_exc()
995+
988996
try:
989997
while True: # Remove in 0.14 together with RouteReset
990998
out = None
991999
try:
1000+
logging.debug(f"403_CHECK: bottle just called hook with {environ['REQUEST_METHOD']} {environ['PATH_INFO']}")
9921001
self.trigger_hook('before_request')
9931002
route, args = self.router.match(environ)
9941003
environ['route.handle'] = route
9951004
environ['bottle.route'] = route
9961005
environ['route.url_args'] = args
1006+
logging.debug(f"403_CHECK: bottle just called route with {args}")
9971007
out = route.call(**args)
9981008
break
9991009
except HTTPResponse as E:
1010+
logging.error(f"403_CHECK: exception at first level {E}")
1011+
print_exc()
10001012
out = E
10011013
break
10021014
except RouteReset:
@@ -1006,16 +1018,24 @@ def _handle(self, environ):
10061018
route.reset()
10071019
continue
10081020
finally:
1021+
if out and not isinstance(out, HTTPError) and len(out) > 40:
1022+
logging.debug(f"403_CHECK: after call, truncated output {out[:20]}...{out[-20:]}")
1023+
else:
1024+
logging.debug(f"403_CHECK: after call, full output is {out}")
10091025
if isinstance(out, HTTPResponse):
10101026
out.apply(response)
10111027
try:
10121028
self.trigger_hook('after_request')
10131029
except HTTPResponse as E:
1030+
logging.error(f"403_CHECK: exception while handling post-hook {E}")
1031+
print_exc()
10141032
out = E
10151033
out.apply(response)
10161034
except (KeyboardInterrupt, SystemExit, MemoryError):
1035+
print_exc()
10171036
raise
10181037
except Exception as E:
1038+
print_exc()
10191039
if not self.catchall: raise
10201040
stacktrace = format_exc()
10211041
environ['wsgi.errors'].write(stacktrace)

emission/net/api/cfc_webapp.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,8 @@ def before_request():
432432
request.params.start_ts = time.time()
433433
request.params.timer = ect.Timer()
434434
request.params.timer.__enter__()
435-
logging.debug("START %s %s" % (request.method, request.path))
435+
logging.debug("START %s %s %s" % (request.method, request.path,
436+
request.json.get('user', None) if request.json else None))
436437

437438
@app.hook('after_request')
438439
def after_request():

emission/net/api/wsgiserver2.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,7 @@ def __init__(self, server, conn):
590590

591591
def parse_request(self):
592592
"""Parse the next HTTP request start-line and message-headers."""
593+
print(f"403_CHECK: wsgiserver started parsing request")
593594
self.rfile = SizeCheckWrapper(self.conn.rfile,
594595
self.server.max_request_header_size)
595596
try:
@@ -600,6 +601,7 @@ def parse_request(self):
600601
"allowed bytes.")
601602
return
602603
else:
604+
print(f"403_CHECK: wsgiserver in else block with {success=}")
603605
if not success:
604606
return
605607

@@ -614,6 +616,7 @@ def parse_request(self):
614616
if not success:
615617
return
616618

619+
print(f"403_CHECK: wsgiserver ready to read request")
617620
self.ready = True
618621

619622
def read_request_line(self):
@@ -625,6 +628,7 @@ def read_request_line(self):
625628
# (although your TCP stack might suffer for it: cf Apache's history
626629
# with FIN_WAIT_2).
627630
request_line = self.rfile.readline()
631+
print(f"403_CHECK: wsgiserver read request line {request_line}")
628632

629633
# Set started_request to True so communicate() knows to send 408
630634
# from here on out.
@@ -716,6 +720,7 @@ def read_request_headers(self):
716720
"""Read self.rfile into self.inheaders. Return success."""
717721

718722
# then all the http headers
723+
print(f"403_CHECK: wsgiserver reading headers {request_line}")
719724
try:
720725
read_headers(self.rfile, self.inheaders)
721726
except ValueError:
@@ -809,6 +814,7 @@ def parse_request_uri(self, uri):
809814
segment = *pchar *( ";" param )
810815
param = *pchar
811816
"""
817+
print(f"403_CHECK: wsgiserver parsing {uri=}")
812818
if uri == ASTERISK:
813819
return None, None, uri
814820

@@ -1921,6 +1927,7 @@ def tick(self):
19211927
"""Accept a new connection and put it on the Queue."""
19221928
try:
19231929
s, addr = self.socket.accept()
1930+
print(f"403_CHECK: wsgiserver accepted connection from socket")
19241931
if self.stats['Enabled']:
19251932
self.stats['Accepts'] += 1
19261933
if not self.ready:
@@ -1977,6 +1984,7 @@ def tick(self):
19771984

19781985
conn.ssl_env = ssl_env
19791986

1987+
print(f"403_CHECK: wsgiserver queued new connection from {conn.remote_port=}")
19801988
self.requests.put(conn)
19811989
except socket.timeout:
19821990
# The only reason for the timeout in start() is so we can

0 commit comments

Comments
 (0)