Merge pull request #2965 from matrix-org/rav/request_logging
Add a metric which increments when a request is receivedpull/2962/head
commit
ba1d08bc4b
|
@ -1,5 +1,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright 2014-2016 OpenMarket Ltd
|
# Copyright 2014-2016 OpenMarket Ltd
|
||||||
|
# Copyright 2018 New Vector Ltd
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -59,6 +60,11 @@ response_count = metrics.register_counter(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
requests_counter = metrics.register_counter(
|
||||||
|
"requests_received",
|
||||||
|
labels=["method", "servlet", ],
|
||||||
|
)
|
||||||
|
|
||||||
outgoing_responses_counter = metrics.register_counter(
|
outgoing_responses_counter = metrics.register_counter(
|
||||||
"responses",
|
"responses",
|
||||||
labels=["method", "code"],
|
labels=["method", "code"],
|
||||||
|
@ -145,7 +151,8 @@ def wrap_request_handler(request_handler, include_metrics=False):
|
||||||
# at the servlet name. For most requests that name will be
|
# at the servlet name. For most requests that name will be
|
||||||
# JsonResource (or a subclass), and JsonResource._async_render
|
# JsonResource (or a subclass), and JsonResource._async_render
|
||||||
# will update it once it picks a servlet.
|
# will update it once it picks a servlet.
|
||||||
request_metrics.start(self.clock, name=self.__class__.__name__)
|
servlet_name = self.__class__.__name__
|
||||||
|
request_metrics.start(self.clock, name=servlet_name)
|
||||||
|
|
||||||
request_context.request = request_id
|
request_context.request = request_id
|
||||||
with request.processing():
|
with request.processing():
|
||||||
|
@ -154,6 +161,7 @@ def wrap_request_handler(request_handler, include_metrics=False):
|
||||||
if include_metrics:
|
if include_metrics:
|
||||||
yield request_handler(self, request, request_metrics)
|
yield request_handler(self, request, request_metrics)
|
||||||
else:
|
else:
|
||||||
|
requests_counter.inc(request.method, servlet_name)
|
||||||
yield request_handler(self, request)
|
yield request_handler(self, request)
|
||||||
except CodeMessageException as e:
|
except CodeMessageException as e:
|
||||||
code = e.code
|
code = e.code
|
||||||
|
@ -229,7 +237,7 @@ class JsonResource(HttpServer, resource.Resource):
|
||||||
""" This implements the HttpServer interface and provides JSON support for
|
""" This implements the HttpServer interface and provides JSON support for
|
||||||
Resources.
|
Resources.
|
||||||
|
|
||||||
Register callbacks via register_path()
|
Register callbacks via register_paths()
|
||||||
|
|
||||||
Callbacks can return a tuple of status code and a dict in which case the
|
Callbacks can return a tuple of status code and a dict in which case the
|
||||||
the dict will automatically be sent to the client as a JSON object.
|
the dict will automatically be sent to the client as a JSON object.
|
||||||
|
@ -276,49 +284,59 @@ class JsonResource(HttpServer, resource.Resource):
|
||||||
This checks if anyone has registered a callback for that method and
|
This checks if anyone has registered a callback for that method and
|
||||||
path.
|
path.
|
||||||
"""
|
"""
|
||||||
|
callback, group_dict = self._get_handler_for_request(request)
|
||||||
|
|
||||||
|
servlet_instance = getattr(callback, "__self__", None)
|
||||||
|
if servlet_instance is not None:
|
||||||
|
servlet_classname = servlet_instance.__class__.__name__
|
||||||
|
else:
|
||||||
|
servlet_classname = "%r" % callback
|
||||||
|
|
||||||
|
request_metrics.name = servlet_classname
|
||||||
|
requests_counter.inc(request.method, servlet_classname)
|
||||||
|
|
||||||
|
# Now trigger the callback. If it returns a response, we send it
|
||||||
|
# here. If it throws an exception, that is handled by the wrapper
|
||||||
|
# installed by @request_handler.
|
||||||
|
|
||||||
|
kwargs = intern_dict({
|
||||||
|
name: urllib.unquote(value).decode("UTF-8") if value else value
|
||||||
|
for name, value in group_dict.items()
|
||||||
|
})
|
||||||
|
|
||||||
|
callback_return = yield callback(request, **kwargs)
|
||||||
|
if callback_return is not None:
|
||||||
|
code, response = callback_return
|
||||||
|
self._send_response(request, code, response)
|
||||||
|
|
||||||
|
def _get_handler_for_request(self, request):
|
||||||
|
"""Finds a callback method to handle the given request
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (twisted.web.http.Request):
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple[Callable, dict[str, str]]: callback method, and the dict
|
||||||
|
mapping keys to path components as specified in the handler's
|
||||||
|
path match regexp.
|
||||||
|
|
||||||
|
The callback will normally be a method registered via
|
||||||
|
register_paths, so will return (possibly via Deferred) either
|
||||||
|
None, or a tuple of (http code, response body).
|
||||||
|
"""
|
||||||
if request.method == "OPTIONS":
|
if request.method == "OPTIONS":
|
||||||
self._send_response(request, 200, {})
|
return _options_handler, {}
|
||||||
return
|
|
||||||
|
|
||||||
# Loop through all the registered callbacks to check if the method
|
# Loop through all the registered callbacks to check if the method
|
||||||
# and path regex match
|
# and path regex match
|
||||||
for path_entry in self.path_regexs.get(request.method, []):
|
for path_entry in self.path_regexs.get(request.method, []):
|
||||||
m = path_entry.pattern.match(request.path)
|
m = path_entry.pattern.match(request.path)
|
||||||
if not m:
|
if m:
|
||||||
continue
|
# We found a match!
|
||||||
|
return path_entry.callback, m.groupdict()
|
||||||
# We found a match! First update the metrics object to indicate
|
|
||||||
# which servlet is handling the request.
|
|
||||||
|
|
||||||
callback = path_entry.callback
|
|
||||||
|
|
||||||
servlet_instance = getattr(callback, "__self__", None)
|
|
||||||
if servlet_instance is not None:
|
|
||||||
servlet_classname = servlet_instance.__class__.__name__
|
|
||||||
else:
|
|
||||||
servlet_classname = "%r" % callback
|
|
||||||
|
|
||||||
request_metrics.name = servlet_classname
|
|
||||||
|
|
||||||
# Now trigger the callback. If it returns a response, we send it
|
|
||||||
# here. If it throws an exception, that is handled by the wrapper
|
|
||||||
# installed by @request_handler.
|
|
||||||
|
|
||||||
kwargs = intern_dict({
|
|
||||||
name: urllib.unquote(value).decode("UTF-8") if value else value
|
|
||||||
for name, value in m.groupdict().items()
|
|
||||||
})
|
|
||||||
|
|
||||||
callback_return = yield callback(request, **kwargs)
|
|
||||||
if callback_return is not None:
|
|
||||||
code, response = callback_return
|
|
||||||
self._send_response(request, code, response)
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
# Huh. No one wanted to handle that? Fiiiiiine. Send 400.
|
# Huh. No one wanted to handle that? Fiiiiiine. Send 400.
|
||||||
request_metrics.name = self.__class__.__name__ + ".UnrecognizedRequest"
|
return _unrecognised_request_handler, {}
|
||||||
raise UnrecognizedRequestError()
|
|
||||||
|
|
||||||
def _send_response(self, request, code, response_json_object,
|
def _send_response(self, request, code, response_json_object,
|
||||||
response_code_message=None):
|
response_code_message=None):
|
||||||
|
@ -335,6 +353,34 @@ class JsonResource(HttpServer, resource.Resource):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _options_handler(request):
|
||||||
|
"""Request handler for OPTIONS requests
|
||||||
|
|
||||||
|
This is a request handler suitable for return from
|
||||||
|
_get_handler_for_request. It returns a 200 and an empty body.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (twisted.web.http.Request):
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple[int, dict]: http code, response body.
|
||||||
|
"""
|
||||||
|
return 200, {}
|
||||||
|
|
||||||
|
|
||||||
|
def _unrecognised_request_handler(request):
|
||||||
|
"""Request handler for unrecognised requests
|
||||||
|
|
||||||
|
This is a request handler suitable for return from
|
||||||
|
_get_handler_for_request. It actually just raises an
|
||||||
|
UnrecognizedRequestError.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (twisted.web.http.Request):
|
||||||
|
"""
|
||||||
|
raise UnrecognizedRequestError()
|
||||||
|
|
||||||
|
|
||||||
class RequestMetrics(object):
|
class RequestMetrics(object):
|
||||||
def start(self, clock, name):
|
def start(self, clock, name):
|
||||||
self.start = clock.time_msec()
|
self.start = clock.time_msec()
|
||||||
|
|
|
@ -57,15 +57,31 @@ class Metrics(object):
|
||||||
return metric
|
return metric
|
||||||
|
|
||||||
def register_counter(self, *args, **kwargs):
|
def register_counter(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Returns:
|
||||||
|
CounterMetric
|
||||||
|
"""
|
||||||
return self._register(CounterMetric, *args, **kwargs)
|
return self._register(CounterMetric, *args, **kwargs)
|
||||||
|
|
||||||
def register_callback(self, *args, **kwargs):
|
def register_callback(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Returns:
|
||||||
|
CallbackMetric
|
||||||
|
"""
|
||||||
return self._register(CallbackMetric, *args, **kwargs)
|
return self._register(CallbackMetric, *args, **kwargs)
|
||||||
|
|
||||||
def register_distribution(self, *args, **kwargs):
|
def register_distribution(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Returns:
|
||||||
|
DistributionMetric
|
||||||
|
"""
|
||||||
return self._register(DistributionMetric, *args, **kwargs)
|
return self._register(DistributionMetric, *args, **kwargs)
|
||||||
|
|
||||||
def register_cache(self, *args, **kwargs):
|
def register_cache(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Returns:
|
||||||
|
CacheMetric
|
||||||
|
"""
|
||||||
return self._register(CacheMetric, *args, **kwargs)
|
return self._register(CacheMetric, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue