THE QUESTION
All of my app's routes are defined via flask-restful Resources. How can I find the resource object/class that is processing current request?
WHY I WANT THIS
I wanted to log all exceptions raised while processing requests. I connect to flask.got_request_exception
, as described in http://flask.pocoo.org/docs/1.0/api/#signals and something like this works well:
from flask import got_request_exception, request
def log_exception(sender, exception, **extra):
logger.info("URL: {}, Exception: {}".format(request.url, type(exception).__name__))
got_request_exception.connect(log_exception, app)
The only problem is that I want to log some of the request data, but not all the data - e.g. I'd like to hide passwords. I think it would be a good idea to have logging-data-logic together with request processing logic, like this:
from flask import request
import flask_restful
class SomeResource(flask_restful.Resource):
def get(self):
# ... GET processing
def log_data(self):
# log all body params
return request.get_json()
class Login(flask_restful.Resource):
def post(self):
# ... authentication
def log_data(self):
# log selected body params
return {'login': request.get_json()['login'], 'password': 'HIDDEN!'}
and than use it in my log_exception
:
from flask import got_request_exception, request
def log_exception(sender, exception, **extra):
resource_class = # THIS IS THE THING I'M MISSING
logger.info("URL: {}, Exception: {}, Data: {}".format(request.url, type(exception).__name__),
resource_class.log_data())
got_request_exception.connect(log_exception, app)
But maybe this should be done other way?
Instead of inheriting from flask_restful.Resource
your want to inherit all from a custom resource
class MyResource(flask_restful.Resource):
def dispatch_request(self, *args, **kwargs):
try:
return super(MyResource,self).dispatch_request(*args, **kwargs)
except Exception as ex:
setattr(ex, "_raised_by", self)
raise ex
and then you can use the exception handler
def log_exception(sender, exception, **extra):
_raised_by = getattr(exception, "_raised_by", None)
if _raised_by:
print(_raised_by)
property("URL: {}, Exception: {}".format(request.url, type(exception).__name__))
Here is complete code I tried
from flask import request, Flask
import flask_restful
app = Flask(__name__)
api = flask_restful.Api(app)
class MyResource(flask_restful.Resource):
def dispatch_request(self, *args, **kwargs):
try:
return super(MyResource,self).dispatch_request(*args, **kwargs)
except Exception as ex:
setattr(ex, "_raised_by", self)
raise ex
# MyResource = flask_restful.Resource
class SomeResource(MyResource):
def get(self):
raise Exception("Not implemented")
def log_data(self):
# log all body params
return request.get_json()
class Login(MyResource):
def post(self):
raise Exception("Not implemented")
def log_data(self):
# log selected body params
return {'login': request.get_json()['login'], 'password': 'HIDDEN!'}
from flask import got_request_exception, request
api.add_resource(Login, '/login')
api.add_resource(SomeResource, '/some')
def log_exception(sender, exception, **extra):
_raised_by = getattr(exception, "_raised_by", None)
if _raised_by:
print(_raised_by)
property("URL: {}, Exception: {}".format(request.url, type(exception).__name__))
got_request_exception.connect(log_exception, app)
if __name__ == '__main__':
app.run(debug=True)
Edit-1: 5th Aug
As commented by @jbet, in case one wants to get the processing class always a cleaner option would be to use the MyResource
as below
from flask import g
class MyResource(flask_restful.Resource):
def dispatch_request(self, *args, **kwargs):
g.processed_by = self
return super(MyResource,self).dispatch_request(*args, **kwargs)
I could not find a nice way to get access to your object once the logging method had been called by signals.
If you were willing to handle all possible cases, you could create your own custom exceptions that call the log_data method on your class.
Instead, I opted to handle logging myself in a base class. Simple example for you, I just used print functions, but you could call app.logging.info instead.
from flask import Flask, request
import flask_restful
app = Flask(__name__)
api = flask_restful.Api(app)
class MyCustomResource(flask_restful.Resource):
def get(self):
try:
self.my_get()
except Exception as exception:
# Will catch all errors in your subclass my_get method
print("exception caught")
print(request.url)
print(type(exception).__name__)
print(self.log_data())
# Re-raise if you want (or not)
raise exception
def my_get(self):
# Python equivalent of virtual method
raise NotImplementedError()
def log_data(self):
# Python equivalent of virtual method
raise NotImplementedError()
class SomeResource(MyCustomResource):
def my_get(self):
# Example unknown error occurs here
raise Exception("error!")
return "some data"
def log_data(self):
# Called by parent
return "some logging data for the object"
api.add_resource(SomeResource, "/")
If you wanted to dig deeper into the flask restful source you could also monkey patch (or build your own package) whatever method is calling get/post.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With