kopia lustrzana https://github.com/simonw/datasette
?_trace=1 now depends on trace_debug setting, closes #1359
rodzic
368aa5f1b1
commit
ff29dd55fa
|
@ -53,7 +53,7 @@ jobs:
|
|||
--plugins-dir=plugins \
|
||||
--branch=$GITHUB_SHA \
|
||||
--version-note=$GITHUB_SHA \
|
||||
--extra-options="--setting template_debug 1 --crossdb" \
|
||||
--extra-options="--setting template_debug 1 --setting trace_debug 1 --crossdb" \
|
||||
--install=pysqlite3-binary \
|
||||
--service=datasette-latest
|
||||
# Deploy docs.db to a different service
|
||||
|
|
|
@ -162,6 +162,11 @@ SETTINGS = (
|
|||
False,
|
||||
"Allow display of template debug information with ?_context=1",
|
||||
),
|
||||
Setting(
|
||||
"trace_debug",
|
||||
False,
|
||||
"Allow display of SQL trace debug information with ?_trace=1",
|
||||
),
|
||||
Setting("base_url", "/", "Datasette URLs should use this base path"),
|
||||
)
|
||||
|
||||
|
@ -1041,14 +1046,15 @@ class Datasette:
|
|||
if not database.is_mutable:
|
||||
await database.table_counts(limit=60 * 60 * 1000)
|
||||
|
||||
asgi = asgi_csrf.asgi_csrf(
|
||||
DatasetteRouter(self, routes),
|
||||
signing_secret=self._secret,
|
||||
cookie_name="ds_csrftoken",
|
||||
)
|
||||
if self.setting("trace_debug"):
|
||||
asgi = AsgiTracer(asgi)
|
||||
asgi = AsgiLifespan(
|
||||
AsgiTracer(
|
||||
asgi_csrf.asgi_csrf(
|
||||
DatasetteRouter(self, routes),
|
||||
signing_secret=self._secret,
|
||||
cookie_name="ds_csrftoken",
|
||||
)
|
||||
),
|
||||
asgi,
|
||||
on_startup=setup_db,
|
||||
)
|
||||
for wrapper in pm.hook.asgi_wrapper(datasette=self):
|
||||
|
|
|
@ -206,6 +206,16 @@ query string arguments:
|
|||
For how many seconds should this response be cached by HTTP proxies? Use
|
||||
``?_ttl=0`` to disable HTTP caching entirely for this request.
|
||||
|
||||
``?_trace=1``
|
||||
Turns on tracing for this page: SQL queries executed during the request will
|
||||
be gathered and included in the response, either in a new ``"_traces"`` key
|
||||
for JSON responses or at the bottom of the page if the response is in HTML.
|
||||
|
||||
The structure of the data returned here should be considered highly unstable
|
||||
and very likely to change.
|
||||
|
||||
Only available if the :ref:`setting_trace_debug` setting is enabled.
|
||||
|
||||
.. _table_arguments:
|
||||
|
||||
Table arguments
|
||||
|
@ -389,14 +399,6 @@ Special table arguments
|
|||
``?_nocount=1``
|
||||
Disable the ``select count(*)`` query used on this page - a count of ``None`` will be returned instead.
|
||||
|
||||
``?_trace=1``
|
||||
Turns on tracing for this page: SQL queries executed during the request will
|
||||
be gathered and included in the response, either in a new ``"_traces"`` key
|
||||
for JSON responses or at the bottom of the page if the response is in HTML.
|
||||
|
||||
The structure of the data returned here should be considered highly unstable
|
||||
and very likely to change.
|
||||
|
||||
.. _expand_foreign_keys:
|
||||
|
||||
Expanding foreign key references
|
||||
|
|
|
@ -286,6 +286,22 @@ Some examples:
|
|||
* https://latest.datasette.io/fixtures?_context=1
|
||||
* https://latest.datasette.io/fixtures/roadside_attractions?_context=1
|
||||
|
||||
.. _setting_trace_debug:
|
||||
|
||||
trace_debug
|
||||
~~~~~~~~~~~
|
||||
|
||||
This setting enables appending ``?_trace=1`` to any page in order to see the SQL queries and other trace information that was used to generate that page.
|
||||
|
||||
Enable it like this::
|
||||
|
||||
datasette mydatabase.db --setting trace_debug 1
|
||||
|
||||
Some examples:
|
||||
|
||||
* https://latest.datasette.io/?_trace=1
|
||||
* https://latest.datasette.io/fixtures/roadside_attractions?_trace=1
|
||||
|
||||
.. _setting_base_url:
|
||||
|
||||
base_url
|
||||
|
|
|
@ -214,6 +214,12 @@ def app_client_with_hash():
|
|||
yield client
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def app_client_with_trace():
|
||||
with make_app_client(config={"trace_debug": True}, is_immutable=True) as client:
|
||||
yield client
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def app_client_shorter_time_limit():
|
||||
with make_app_client(20) as client:
|
||||
|
|
|
@ -15,6 +15,7 @@ from .fixtures import ( # noqa
|
|||
app_client_conflicting_database_names,
|
||||
app_client_with_cors,
|
||||
app_client_with_dot,
|
||||
app_client_with_trace,
|
||||
app_client_immutable_and_inspect_file,
|
||||
generate_compound_rows,
|
||||
generate_sortable_rows,
|
||||
|
@ -1422,6 +1423,7 @@ def test_settings_json(app_client):
|
|||
"force_https_urls": False,
|
||||
"hash_urls": False,
|
||||
"template_debug": False,
|
||||
"trace_debug": False,
|
||||
"base_url": "/",
|
||||
} == response.json
|
||||
|
||||
|
@ -1692,8 +1694,10 @@ def test_nocount(app_client, nocount, expected_count):
|
|||
assert response.json["filtered_table_rows_count"] == expected_count
|
||||
|
||||
|
||||
def test_nocount_nofacet_if_shape_is_object(app_client):
|
||||
response = app_client.get("/fixtures/facetable.json?_trace=1&_shape=object")
|
||||
def test_nocount_nofacet_if_shape_is_object(app_client_with_trace):
|
||||
response = app_client_with_trace.get(
|
||||
"/fixtures/facetable.json?_trace=1&_shape=object"
|
||||
)
|
||||
assert "count(*)" not in response.text
|
||||
|
||||
|
||||
|
@ -1863,9 +1867,17 @@ def test_custom_query_with_unicode_characters(app_client):
|
|||
assert [{"id": 1, "name": "San Francisco"}] == response.json
|
||||
|
||||
|
||||
def test_trace(app_client):
|
||||
response = app_client.get("/fixtures/simple_primary_key.json?_trace=1")
|
||||
@pytest.mark.parametrize("trace_debug", (True, False))
|
||||
def test_trace(trace_debug):
|
||||
with make_app_client(config={"trace_debug": trace_debug}) as client:
|
||||
response = client.get("/fixtures/simple_primary_key.json?_trace=1")
|
||||
assert response.status == 200
|
||||
|
||||
data = response.json
|
||||
if not trace_debug:
|
||||
assert "_trace" not in data
|
||||
return
|
||||
|
||||
assert "_trace" in data
|
||||
trace_info = data["_trace"]
|
||||
assert isinstance(trace_info["request_duration_ms"], float)
|
||||
|
|
|
@ -3,6 +3,7 @@ from .fixtures import ( # noqa
|
|||
app_client,
|
||||
app_client_csv_max_mb_one,
|
||||
app_client_with_cors,
|
||||
app_client_with_trace,
|
||||
)
|
||||
|
||||
EXPECTED_TABLE_CSV = """id,content
|
||||
|
@ -160,8 +161,8 @@ def test_table_csv_stream(app_client):
|
|||
assert 1002 == len([b for b in response.body.split(b"\r\n") if b])
|
||||
|
||||
|
||||
def test_csv_trace(app_client):
|
||||
response = app_client.get("/fixtures/simple_primary_key.csv?_trace=1")
|
||||
def test_csv_trace(app_client_with_trace):
|
||||
response = app_client_with_trace.get("/fixtures/simple_primary_key.csv?_trace=1")
|
||||
assert response.headers["content-type"] == "text/html; charset=utf-8"
|
||||
soup = Soup(response.text, "html.parser")
|
||||
assert (
|
||||
|
@ -171,13 +172,13 @@ def test_csv_trace(app_client):
|
|||
assert "select id, content from simple_primary_key" in soup.find("pre").text
|
||||
|
||||
|
||||
def test_table_csv_stream_does_not_calculate_facets(app_client):
|
||||
response = app_client.get("/fixtures/simple_primary_key.csv?_trace=1")
|
||||
def test_table_csv_stream_does_not_calculate_facets(app_client_with_trace):
|
||||
response = app_client_with_trace.get("/fixtures/simple_primary_key.csv?_trace=1")
|
||||
soup = Soup(response.text, "html.parser")
|
||||
assert "select content, count(*) as n" not in soup.find("pre").text
|
||||
|
||||
|
||||
def test_table_csv_stream_does_not_calculate_counts(app_client):
|
||||
response = app_client.get("/fixtures/simple_primary_key.csv?_trace=1")
|
||||
def test_table_csv_stream_does_not_calculate_counts(app_client_with_trace):
|
||||
response = app_client_with_trace.get("/fixtures/simple_primary_key.csv?_trace=1")
|
||||
soup = Soup(response.text, "html.parser")
|
||||
assert "select count(*)" not in soup.find("pre").text
|
||||
|
|
Ładowanie…
Reference in New Issue