Measure your views¶
The question that brings most people here is which view is slow. The two middlewares answer it without you touching a view.
flowchart TB
R([Request]) --> SM
subgraph stack [MIDDLEWARE]
direction TB
SM["StatsdMiddleware<br/><i>opens the scope</i>"]
YOURS["your middlewares"]
SMT["StatsdMiddlewareTimer<br/><i>times the view</i>"]
SM --> YOURS --> SMT
end
SMT --> V["view<br/><i>request.statsd</i>"]
V --> DB[("execute_wrapper<br/>sql.<alias>")]
V --> T["render_to_string<br/>render_django"]
SM -. "scope on asgiref Local" .-> SCOPE(["timings, counters,<br/>view name"])
SCOPE -. "flushed on response" .-> OUT[/"statsd, UDP 8125"/]
classDef ours fill:#3b6ea5,stroke:#2a4f78,color:#fff
classDef theirs fill:#eef2f6,stroke:#b6c2ce,color:#1b1f23
class SM,SMT,SCOPE ours
class YOURS,V,DB,T,OUT theirs
StatsdMiddleware sits at the top of the stack and opens a scope on
the request. StatsdMiddlewareTimer sits at the bottom and times the
view itself. Everything you install between them is timed by the pair.
Put them the wrong way round and the timer stops a clock nobody
started. The first request tells you so:
AssertionError: Unable to stop tracking process_response, never
started tracking it
You find out on the first request in development, so this is a mistake you make once.
What one request submits¶
# GET /dashboard/
myproject.view.get.myproject.views.dashboard.hit
myproject.view.get.myproject.views.dashboard.process_request
myproject.view.get.myproject.views.dashboard.process_response
myproject.view.get.myproject.views.dashboard.process_view
myproject.view.get.myproject.views.dashboard.total
myproject.view.http_codes.2xx
myproject.view.http_codes.hit
myproject.view.site.hit
process_request covers the middlewares above the view,
process_view the resolution and the view call, and
process_response the way back out. They don’t add up to total,
and the gap is real: it’s the part of the request outside the section
the middleware pair wraps.
Errors¶
A view that raises reports under the same name, with two differences:
# GET /broken/, which raises
myproject.view.get.myproject.views.broken.hit
myproject.view.get.myproject.views.broken.process_exception
myproject.view.get.myproject.views.broken.process_request
myproject.view.get.myproject.views.broken.process_response
myproject.view.get.myproject.views.broken.process_view
myproject.view.get.myproject.views.broken.total
myproject.view.http_codes.5xx
myproject.view.http_codes.hit
myproject.view.site.hit
process_exception appears, and the status counter lands on
http_codes.5xx. The view is still timed, so a
view that fails slowly shows up as a slow view and not as a gap.
Skipping views¶
Health checks and the admin generate traffic nobody charts.
STATSD_VIEWS_TO_SKIP holds regular expressions matched against the
view name, and the admin is skipped out of the box.
STATSD_VIEWS_TO_SKIP = [
r'django.contrib.admin',
r'myproject.views.health_check',
]
Ajax requests¶
STATSD_TAGS_LIKE files an ajax view under a tagged name as well as
its plain one, so you can chart the two apart without losing the
combined figure. The separators it understands are _is_ and =.