]>
jfr.im git - dlqueue.git/blob - venv/lib/python3.11/site-packages/werkzeug/middleware/profiler.py
5 This module provides a middleware that profiles each request with the
6 :mod:`cProfile` module. This can help identify bottlenecks in your code
7 that may be slowing down your application.
9 .. autoclass:: ProfilerMiddleware
11 :copyright: 2007 Pallets
12 :license: BSD-3-Clause
14 from __future__
import annotations
20 from pstats
import Stats
23 from cProfile
import Profile
25 from profile
import Profile
# type: ignore
28 from _typeshed
.wsgi
import StartResponse
29 from _typeshed
.wsgi
import WSGIApplication
30 from _typeshed
.wsgi
import WSGIEnvironment
33 class ProfilerMiddleware
:
34 """Wrap a WSGI application and profile the execution of each
35 request. Responses are buffered so that timings are more exact.
37 If ``stream`` is given, :class:`pstats.Stats` are written to it
38 after each request. If ``profile_dir`` is given, :mod:`cProfile`
39 data files are saved to that directory, one file per request.
41 The filename can be customized by passing ``filename_format``. If
42 it is a string, it will be formatted using :meth:`str.format` with
43 the following fields available:
45 - ``{method}`` - The request method; GET, POST, etc.
46 - ``{path}`` - The request path or 'root' should one not exist.
47 - ``{elapsed}`` - The elapsed time of the request in milliseconds.
48 - ``{time}`` - The time of the request.
50 If it is a callable, it will be called with the WSGI ``environ`` and
51 be expected to return a filename string. The ``environ`` dictionary
52 will also have the ``"werkzeug.profiler"`` key populated with a
53 dictionary containing the following fields (more may be added in the
55 - ``{elapsed}`` - The elapsed time of the request in milliseconds.
56 - ``{time}`` - The time of the request.
58 :param app: The WSGI application to wrap.
59 :param stream: Write stats to this stream. Disable with ``None``.
60 :param sort_by: A tuple of columns to sort stats by. See
61 :meth:`pstats.Stats.sort_stats`.
62 :param restrictions: A tuple of restrictions to filter stats by. See
63 :meth:`pstats.Stats.print_stats`.
64 :param profile_dir: Save profile data files to this directory.
65 :param filename_format: Format string for profile data file names,
66 or a callable returning a name. See explanation above.
68 .. code-block:: python
70 from werkzeug.middleware.profiler import ProfilerMiddleware
71 app = ProfilerMiddleware(app)
73 .. versionchanged:: 3.0
74 Added the ``"werkzeug.profiler"`` key to the ``filename_format(environ)``
75 parameter with the ``elapsed`` and ``time`` fields.
77 .. versionchanged:: 0.15
78 Stats are written even if ``profile_dir`` is given, and can be
79 disable by passing ``stream=None``.
81 .. versionadded:: 0.15
82 Added ``filename_format``.
85 Added ``restrictions`` and ``profile_dir``.
91 stream
: t
.IO
[str] |
None = sys
.stdout
,
92 sort_by
: t
.Iterable
[str] = ("time", "calls"),
93 restrictions
: t
.Iterable
[str |
int |
float] = (),
94 profile_dir
: str |
None = None,
95 filename_format
: str = "{method}.{path}.{elapsed:.0f}ms.{time:.0f}.prof",
99 self
._sort
_by
= sort_by
100 self
._restrictions
= restrictions
101 self
._profile
_dir
= profile_dir
102 self
._filename
_format
= filename_format
105 self
, environ
: WSGIEnvironment
, start_response
: StartResponse
106 ) -> t
.Iterable
[bytes]:
107 response_body
: list[bytes] = []
109 def catching_start_response(status
, headers
, exc_info
=None): # type: ignore
110 start_response(status
, headers
, exc_info
)
111 return response_body
.append
113 def runapp() -> None:
114 app_iter
= self
._app
(
115 environ
, t
.cast("StartResponse", catching_start_response
)
117 response_body
.extend(app_iter
)
119 if hasattr(app_iter
, "close"):
124 profile
.runcall(runapp
)
125 body
= b
"".join(response_body
)
126 elapsed
= time
.time() - start
128 if self
._profile
_dir
is not None:
129 if callable(self
._filename
_format
):
130 environ
["werkzeug.profiler"] = {
131 "elapsed": elapsed
* 1000.0,
134 filename
= self
._filename
_format
(environ
)
136 filename
= self
._filename
_format
.format(
137 method
=environ
["REQUEST_METHOD"],
138 path
=environ
["PATH_INFO"].strip("/").replace("/", ".") or "root",
139 elapsed
=elapsed
* 1000.0,
142 filename
= os
.path
.join(self
._profile
_dir
, filename
)
143 profile
.dump_stats(filename
)
145 if self
._stream
is not None:
146 stats
= Stats(profile
, stream
=self
._stream
)
147 stats
.sort_stats(*self
._sort
_by
)
148 print("-" * 80, file=self
._stream
)
149 path_info
= environ
.get("PATH_INFO", "")
150 print(f
"PATH: {path_info!r}", file=self
._stream
)
151 stats
.print_stats(*self
._restrictions
)
152 print(f
"{'-' * 80}\n", file=self
._stream
)