-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Request argument as a global variable #69
Comments
Because the request argument changes with every request and they are executed asynchronously. So there would be no way to know which function is accessing it. |
@gaojiuli @aborsu Flask is setting it as a variable for the current Thread, a similar approach can be done using the The problem is that the Task is only propagated when |
I actually firmly believe that the global request object in flask is a bad design. |
Well global variables is a bad design. Setting variables in a context because its something you need in most of the calls of your functions its not. Imagine you want to log the I totally agree with the explicit is better than implicit but principles are there to solve problems not to create them. If we wanted to follow it 100% why would you set a class variable when you can pass the value every time you call a function of class which is more explicit??? |
I agree with @aborsu. Pseudo-global request of flask is not only looks surprisingly for new users. It has significant performance issues (not sure it's the same for |
Mutable global variables are unsafe and unacceptable in asynchronous request handling. I don't think we have an option here. Sanic |
Global variables is hard for testing, I prefer explict request argument, then I may create a request object explict and pass it to view function. See http://wiki.c2.com/?GlobalVariablesAreBad for more. |
I agree with @aborsu I think going for |
Global variables are a real pain to unit testing. |
Going to close this issue citing the thumbs up given to @aborsu |
@aborsu @argaen You both have point. And Sanic is not alone here. For instance, Tornado is also lacking this feature tornadoweb/tornado#2144 As of design, for instance, in Pyramid request is explicitly passed everywhere, but for such cases there's still a documented way of getting current request https://docs.pylonsproject.org/projects/pyramid/en/1.9-branch/narr/threadlocals.html Also here's PEP-0550 mentioned by @bdarnell https://www.python.org/dev/peps/pep-0550/ |
Please tell, what are these “other ways”? Because I can't find any. And @argaen is right on point for our use case. I want to log the equivalent of Request-Id in every log, because we build microservices and need to be able to find correlated logs easily.
In the general sense, yes, it feels right. But it doesn't help our use case. And a design choice can only be properly judged when we know which problem it tries to solve. A really good design should enable developers to respond to complex business needs, and not limit them to trivialities. |
For folks trying to find workarounds, PEP 567 might also be a good read. |
A few things:
import asyncio
import logging
import uuid
from contextvars import ContextVar
from sanic import Sanic
from sanic.log import LOGGING_CONFIG_DEFAULTS
from sanic.response import text
class RequestLogFilter(logging.Filter):
def filter(self, record):
request = app.ctxvar.get()
setattr(record, "request_id", request.ctx.request_id)
return True
LOGGING_CONFIG = {**LOGGING_CONFIG_DEFAULTS}
LOGGING_CONFIG["filters"] = {"request_log": {"()": RequestLogFilter}}
LOGGING_CONFIG["handlers"]["access_console"]["filters"] = ["request_log"]
LOGGING_CONFIG["formatters"]["access"]["format"] = (
"%(asctime)s - (%(name)s)[%(levelname)s][req=%(request_id)s][%(host)s]: "
"%(request)s %(message)s %(status)d %(byte)d"
)
app = Sanic("__BASE__", log_config=LOGGING_CONFIG)
# Specifically not using app.ctx here because that is likely to be used by Sanic in the near future
app.ctxvar = ContextVar("request")
@app.get("/")
async def get1(request):
await asyncio.sleep(3)
return text("Done.")
@app.middleware("request")
async def add_req_id(request):
request.app.ctxvar.set(request)
request.ctx.request_id = uuid.uuid4()
if __name__ == "__main__":
app.run(access_log=True, debug=True, port=9999) |
This issue has been mentioned on Sanic Community Discussion. There might be relevant details there: https://community.sanicframework.org/t/externally-accessing-the-request-object/894/1 |
I think it's a little long-winded.
in sanic:
in flask:
Since each function takes a request argument, why not set it as a global variable.
Could you tell me the reason why you don't do this,thank you
The text was updated successfully, but these errors were encountered: