mirror of
https://github.com/kennethreitz/responder.git
synced 2026-06-05 23:00:17 +00:00
deploy: 90a082a0ac
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -103,6 +103,7 @@
|
||||
<span class="n">openapi_theme</span><span class="o">=</span><span class="n">DEFAULT_OPENAPI_THEME</span><span class="p">,</span>
|
||||
<span class="n">lifespan</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">request_id</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
|
||||
<span class="n">enable_logging</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
|
||||
<span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""Create a new Responder API instance.</span>
|
||||
|
||||
@@ -128,6 +129,7 @@
|
||||
<span class="sd"> :param openapi_theme: Documentation UI theme: ``"swagger_ui"``, ``"redoc"``, ``"rapidoc"``, or ``"elements"``.</span>
|
||||
<span class="sd"> :param lifespan: An async context manager for startup/shutdown logic.</span>
|
||||
<span class="sd"> :param request_id: If ``True``, add ``X-Request-ID`` headers to all responses.</span>
|
||||
<span class="sd"> :param enable_logging: If ``True``, enable structured logging with per-request context (request ID, method, path, client IP).</span>
|
||||
<span class="sd"> """</span> <span class="c1"># noqa: E501</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">background</span> <span class="o">=</span> <span class="n">BackgroundQueue</span><span class="p">()</span>
|
||||
|
||||
@@ -200,7 +202,7 @@
|
||||
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">templates</span> <span class="o">=</span> <span class="n">Templates</span><span class="p">(</span><span class="n">directory</span><span class="o">=</span><span class="n">templates_dir</span><span class="p">)</span>
|
||||
|
||||
<span class="k">if</span> <span class="n">request_id</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="n">request_id</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">enable_logging</span><span class="p">:</span>
|
||||
<span class="kn">import</span><span class="w"> </span><span class="nn">uuid</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="nn">_uuid</span>
|
||||
|
||||
<span class="k">def</span><span class="w"> </span><span class="nf">_add_request_id</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
|
||||
@@ -209,6 +211,20 @@
|
||||
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">router</span><span class="o">.</span><span class="n">after_request</span><span class="p">(</span><span class="n">_add_request_id</span><span class="p">)</span>
|
||||
|
||||
<span class="k">if</span> <span class="n">enable_logging</span><span class="p">:</span>
|
||||
<span class="kn">import</span><span class="w"> </span><span class="nn">logging</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="nn">_logging</span>
|
||||
|
||||
<span class="kn">from</span><span class="w"> </span><span class="nn">.ext.logging</span><span class="w"> </span><span class="kn">import</span> <span class="n">LoggingMiddleware</span><span class="p">,</span> <span class="n">get_logger</span><span class="p">,</span> <span class="n">setup_logging</span>
|
||||
|
||||
<span class="n">log_level</span> <span class="o">=</span> <span class="n">_logging</span><span class="o">.</span><span class="n">DEBUG</span> <span class="k">if</span> <span class="n">debug</span> <span class="k">else</span> <span class="n">_logging</span><span class="o">.</span><span class="n">INFO</span>
|
||||
<span class="n">setup_logging</span><span class="p">(</span><span class="n">level</span><span class="o">=</span><span class="n">log_level</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">add_middleware</span><span class="p">(</span><span class="n">LoggingMiddleware</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">log</span> <span class="o">=</span> <span class="n">get_logger</span><span class="p">(</span><span class="s2">"responder.app"</span><span class="p">)</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="kn">import</span><span class="w"> </span><span class="nn">logging</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="nn">_logging</span>
|
||||
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">log</span> <span class="o">=</span> <span class="n">_logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="s2">"responder.app"</span><span class="p">)</span>
|
||||
|
||||
<span class="nd">@property</span>
|
||||
<span class="k">def</span><span class="w"> </span><span class="nf">requests</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""A test client connected to the ASGI app. Lazily initialized."""</span>
|
||||
|
||||
@@ -73,6 +73,7 @@ One ``pip install``, batteries included:
|
||||
- Sync and async views — ``async`` is always optional.
|
||||
- Class-based views with ``on_get``, ``on_post``, ``on_request``.
|
||||
- Built-in rate limiting with ``X-RateLimit`` headers.
|
||||
- Structured logging with per-request context.
|
||||
- Content negotiation: JSON, YAML, and MessagePack.
|
||||
- A pleasant API with a single import statement.
|
||||
- OpenAPI schema generation with Swagger UI.
|
||||
|
||||
@@ -596,6 +596,60 @@ can pace themselves.
|
||||
The rate limiter is per-client, keyed by IP address.
|
||||
|
||||
|
||||
Structured Logging
|
||||
------------------
|
||||
|
||||
Production applications need structured, searchable logs. Responder
|
||||
includes built-in logging that automatically attaches request context
|
||||
— request ID, HTTP method, path, and client IP — to every log message
|
||||
emitted during request handling::
|
||||
|
||||
api = responder.API(enable_logging=True)
|
||||
|
||||
This gives you:
|
||||
|
||||
- **Access logging** with timing for every request::
|
||||
|
||||
2026-03-24 12:00:00 [INFO] responder.access — GET /users → 200 (1.2ms)
|
||||
|
||||
- **A logger on the API instance** — use ``api.log`` anywhere in
|
||||
your routes. Request context (ID, method, path, client IP) is
|
||||
attached automatically::
|
||||
|
||||
@api.route("/users/{user_id:int}")
|
||||
def get_user(req, resp, *, user_id):
|
||||
api.log.info("fetching user %d", user_id)
|
||||
# => [INFO] responder.app -- fetching user 42 [GET /users/42] [req:a1b2c3] [client:10.0.0.1]
|
||||
resp.media = {"id": user_id}
|
||||
|
||||
- **Request IDs** generated automatically (or forwarded from the
|
||||
``X-Request-ID`` header) and included in responses.
|
||||
|
||||
The logging uses Python's standard ``logging`` module, so it works with
|
||||
any handler — files, syslog, JSON formatters, Datadog, Sentry, whatever
|
||||
you already use.
|
||||
|
||||
For additional loggers (e.g. in helper modules), use ``get_logger``::
|
||||
|
||||
from responder.ext.logging import get_logger
|
||||
logger = get_logger("myapp.db")
|
||||
|
||||
You can also access the current request context directly::
|
||||
|
||||
from responder.ext.logging import RequestContext
|
||||
|
||||
@api.route("/debug")
|
||||
def debug(req, resp):
|
||||
resp.media = {
|
||||
"request_id": RequestContext.get_request_id(),
|
||||
"client_ip": RequestContext.get_client_ip(),
|
||||
}
|
||||
|
||||
When ``enable_logging=True`` is set, it supersedes ``request_id=True``
|
||||
— the logging middleware handles request IDs itself, so you don't get
|
||||
duplicate headers.
|
||||
|
||||
|
||||
Pydantic Validation
|
||||
-------------------
|
||||
|
||||
|
||||
@@ -103,6 +103,7 @@ work with — welcome.</p>
|
||||
<li><p>Sync and async views — <code class="docutils literal notranslate"><span class="pre">async</span></code> is always optional.</p></li>
|
||||
<li><p>Class-based views with <code class="docutils literal notranslate"><span class="pre">on_get</span></code>, <code class="docutils literal notranslate"><span class="pre">on_post</span></code>, <code class="docutils literal notranslate"><span class="pre">on_request</span></code>.</p></li>
|
||||
<li><p>Built-in rate limiting with <code class="docutils literal notranslate"><span class="pre">X-RateLimit</span></code> headers.</p></li>
|
||||
<li><p>Structured logging with per-request context.</p></li>
|
||||
<li><p>Content negotiation: JSON, YAML, and MessagePack.</p></li>
|
||||
<li><p>A pleasant API with a single import statement.</p></li>
|
||||
<li><p>OpenAPI schema generation with Swagger UI.</p></li>
|
||||
@@ -156,6 +157,7 @@ work with — welcome.</p>
|
||||
<li class="toctree-l2"><a class="reference internal" href="tour.html#trusted-hosts">Trusted Hosts</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="tour.html#request-id">Request ID</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="tour.html#rate-limiting">Rate Limiting</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="tour.html#structured-logging">Structured Logging</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="tour.html#pydantic-validation">Pydantic Validation</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="tour.html#content-negotiation">Content Negotiation</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="tour.html#messagepack">MessagePack</a></li>
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
@@ -581,6 +581,59 @@ response with a <code class="docutils literal notranslate"><span class="pre">Ret
|
||||
can pace themselves.</p>
|
||||
<p>The rate limiter is per-client, keyed by IP address.</p>
|
||||
</section>
|
||||
<section id="structured-logging">
|
||||
<h2>Structured Logging<a class="headerlink" href="#structured-logging" title="Link to this heading">¶</a></h2>
|
||||
<p>Production applications need structured, searchable logs. Responder
|
||||
includes built-in logging that automatically attaches request context
|
||||
— request ID, HTTP method, path, and client IP — to every log message
|
||||
emitted during request handling:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">api</span> <span class="o">=</span> <span class="n">responder</span><span class="o">.</span><span class="n">API</span><span class="p">(</span><span class="n">enable_logging</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This gives you:</p>
|
||||
<ul>
|
||||
<li><p><strong>Access logging</strong> with timing for every request:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>2026-03-24 12:00:00 [INFO] responder.access — GET /users → 200 (1.2ms)
|
||||
</pre></div>
|
||||
</div>
|
||||
</li>
|
||||
<li><p><strong>A logger on the API instance</strong> — use <code class="docutils literal notranslate"><span class="pre">api.log</span></code> anywhere in
|
||||
your routes. Request context (ID, method, path, client IP) is
|
||||
attached automatically:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">"/users/{user_id:int}"</span><span class="p">)</span>
|
||||
<span class="k">def</span><span class="w"> </span><span class="nf">get_user</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">user_id</span><span class="p">):</span>
|
||||
<span class="n">api</span><span class="o">.</span><span class="n">log</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s2">"fetching user </span><span class="si">%d</span><span class="s2">"</span><span class="p">,</span> <span class="n">user_id</span><span class="p">)</span>
|
||||
<span class="c1"># => [INFO] responder.app -- fetching user 42 [GET /users/42] [req:a1b2c3] [client:10.0.0.1]</span>
|
||||
<span class="n">resp</span><span class="o">.</span><span class="n">media</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"id"</span><span class="p">:</span> <span class="n">user_id</span><span class="p">}</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</li>
|
||||
<li><p><strong>Request IDs</strong> generated automatically (or forwarded from the
|
||||
<code class="docutils literal notranslate"><span class="pre">X-Request-ID</span></code> header) and included in responses.</p></li>
|
||||
</ul>
|
||||
<p>The logging uses Python’s standard <code class="docutils literal notranslate"><span class="pre">logging</span></code> module, so it works with
|
||||
any handler — files, syslog, JSON formatters, Datadog, Sentry, whatever
|
||||
you already use.</p>
|
||||
<p>For additional loggers (e.g. in helper modules), use <code class="docutils literal notranslate"><span class="pre">get_logger</span></code>:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">responder.ext.logging</span><span class="w"> </span><span class="kn">import</span> <span class="n">get_logger</span>
|
||||
<span class="n">logger</span> <span class="o">=</span> <span class="n">get_logger</span><span class="p">(</span><span class="s2">"myapp.db"</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>You can also access the current request context directly:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">responder.ext.logging</span><span class="w"> </span><span class="kn">import</span> <span class="n">RequestContext</span>
|
||||
|
||||
<span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">"/debug"</span><span class="p">)</span>
|
||||
<span class="k">def</span><span class="w"> </span><span class="nf">debug</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
|
||||
<span class="n">resp</span><span class="o">.</span><span class="n">media</span> <span class="o">=</span> <span class="p">{</span>
|
||||
<span class="s2">"request_id"</span><span class="p">:</span> <span class="n">RequestContext</span><span class="o">.</span><span class="n">get_request_id</span><span class="p">(),</span>
|
||||
<span class="s2">"client_ip"</span><span class="p">:</span> <span class="n">RequestContext</span><span class="o">.</span><span class="n">get_client_ip</span><span class="p">(),</span>
|
||||
<span class="p">}</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>When <code class="docutils literal notranslate"><span class="pre">enable_logging=True</span></code> is set, it supersedes <code class="docutils literal notranslate"><span class="pre">request_id=True</span></code>
|
||||
— the logging middleware handles request IDs itself, so you don’t get
|
||||
duplicate headers.</p>
|
||||
</section>
|
||||
<section id="pydantic-validation">
|
||||
<h2>Pydantic Validation<a class="headerlink" href="#pydantic-validation" title="Link to this heading">¶</a></h2>
|
||||
<p><a class="reference external" href="https://docs.pydantic.dev/">Pydantic</a> models integrate directly with
|
||||
@@ -712,6 +765,7 @@ bodies by passing <code class="docutils literal notranslate"><span class="pre">&
|
||||
<li><a class="reference internal" href="#trusted-hosts">Trusted Hosts</a></li>
|
||||
<li><a class="reference internal" href="#request-id">Request ID</a></li>
|
||||
<li><a class="reference internal" href="#rate-limiting">Rate Limiting</a></li>
|
||||
<li><a class="reference internal" href="#structured-logging">Structured Logging</a></li>
|
||||
<li><a class="reference internal" href="#pydantic-validation">Pydantic Validation</a></li>
|
||||
<li><a class="reference internal" href="#content-negotiation">Content Negotiation</a></li>
|
||||
<li><a class="reference internal" href="#messagepack">MessagePack</a></li>
|
||||
|
||||
Reference in New Issue
Block a user