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 \
|
--plugins-dir=plugins \
|
||||||
--branch=$GITHUB_SHA \
|
--branch=$GITHUB_SHA \
|
||||||
--version-note=$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 \
|
--install=pysqlite3-binary \
|
||||||
--service=datasette-latest
|
--service=datasette-latest
|
||||||
# Deploy docs.db to a different service
|
# Deploy docs.db to a different service
|
||||||
|
|
|
@ -162,6 +162,11 @@ SETTINGS = (
|
||||||
False,
|
False,
|
||||||
"Allow display of template debug information with ?_context=1",
|
"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"),
|
Setting("base_url", "/", "Datasette URLs should use this base path"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1041,14 +1046,15 @@ class Datasette:
|
||||||
if not database.is_mutable:
|
if not database.is_mutable:
|
||||||
await database.table_counts(limit=60 * 60 * 1000)
|
await database.table_counts(limit=60 * 60 * 1000)
|
||||||
|
|
||||||
asgi = AsgiLifespan(
|
asgi = asgi_csrf.asgi_csrf(
|
||||||
AsgiTracer(
|
|
||||||
asgi_csrf.asgi_csrf(
|
|
||||||
DatasetteRouter(self, routes),
|
DatasetteRouter(self, routes),
|
||||||
signing_secret=self._secret,
|
signing_secret=self._secret,
|
||||||
cookie_name="ds_csrftoken",
|
cookie_name="ds_csrftoken",
|
||||||
)
|
)
|
||||||
),
|
if self.setting("trace_debug"):
|
||||||
|
asgi = AsgiTracer(asgi)
|
||||||
|
asgi = AsgiLifespan(
|
||||||
|
asgi,
|
||||||
on_startup=setup_db,
|
on_startup=setup_db,
|
||||||
)
|
)
|
||||||
for wrapper in pm.hook.asgi_wrapper(datasette=self):
|
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
|
For how many seconds should this response be cached by HTTP proxies? Use
|
||||||
``?_ttl=0`` to disable HTTP caching entirely for this request.
|
``?_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:
|
||||||
|
|
||||||
Table arguments
|
Table arguments
|
||||||
|
@ -389,14 +399,6 @@ Special table arguments
|
||||||
``?_nocount=1``
|
``?_nocount=1``
|
||||||
Disable the ``select count(*)`` query used on this page - a count of ``None`` will be returned instead.
|
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:
|
.. _expand_foreign_keys:
|
||||||
|
|
||||||
Expanding foreign key references
|
Expanding foreign key references
|
||||||
|
|
|
@ -286,6 +286,22 @@ Some examples:
|
||||||
* https://latest.datasette.io/fixtures?_context=1
|
* https://latest.datasette.io/fixtures?_context=1
|
||||||
* https://latest.datasette.io/fixtures/roadside_attractions?_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:
|
.. _setting_base_url:
|
||||||
|
|
||||||
base_url
|
base_url
|
||||||
|
|
|
@ -214,6 +214,12 @@ def app_client_with_hash():
|
||||||
yield client
|
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")
|
@pytest.fixture(scope="session")
|
||||||
def app_client_shorter_time_limit():
|
def app_client_shorter_time_limit():
|
||||||
with make_app_client(20) as client:
|
with make_app_client(20) as client:
|
||||||
|
|
|
@ -15,6 +15,7 @@ from .fixtures import ( # noqa
|
||||||
app_client_conflicting_database_names,
|
app_client_conflicting_database_names,
|
||||||
app_client_with_cors,
|
app_client_with_cors,
|
||||||
app_client_with_dot,
|
app_client_with_dot,
|
||||||
|
app_client_with_trace,
|
||||||
app_client_immutable_and_inspect_file,
|
app_client_immutable_and_inspect_file,
|
||||||
generate_compound_rows,
|
generate_compound_rows,
|
||||||
generate_sortable_rows,
|
generate_sortable_rows,
|
||||||
|
@ -1422,6 +1423,7 @@ def test_settings_json(app_client):
|
||||||
"force_https_urls": False,
|
"force_https_urls": False,
|
||||||
"hash_urls": False,
|
"hash_urls": False,
|
||||||
"template_debug": False,
|
"template_debug": False,
|
||||||
|
"trace_debug": False,
|
||||||
"base_url": "/",
|
"base_url": "/",
|
||||||
} == response.json
|
} == response.json
|
||||||
|
|
||||||
|
@ -1692,8 +1694,10 @@ def test_nocount(app_client, nocount, expected_count):
|
||||||
assert response.json["filtered_table_rows_count"] == expected_count
|
assert response.json["filtered_table_rows_count"] == expected_count
|
||||||
|
|
||||||
|
|
||||||
def test_nocount_nofacet_if_shape_is_object(app_client):
|
def test_nocount_nofacet_if_shape_is_object(app_client_with_trace):
|
||||||
response = app_client.get("/fixtures/facetable.json?_trace=1&_shape=object")
|
response = app_client_with_trace.get(
|
||||||
|
"/fixtures/facetable.json?_trace=1&_shape=object"
|
||||||
|
)
|
||||||
assert "count(*)" not in response.text
|
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
|
assert [{"id": 1, "name": "San Francisco"}] == response.json
|
||||||
|
|
||||||
|
|
||||||
def test_trace(app_client):
|
@pytest.mark.parametrize("trace_debug", (True, False))
|
||||||
response = app_client.get("/fixtures/simple_primary_key.json?_trace=1")
|
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
|
data = response.json
|
||||||
|
if not trace_debug:
|
||||||
|
assert "_trace" not in data
|
||||||
|
return
|
||||||
|
|
||||||
assert "_trace" in data
|
assert "_trace" in data
|
||||||
trace_info = data["_trace"]
|
trace_info = data["_trace"]
|
||||||
assert isinstance(trace_info["request_duration_ms"], float)
|
assert isinstance(trace_info["request_duration_ms"], float)
|
||||||
|
|
|
@ -3,6 +3,7 @@ from .fixtures import ( # noqa
|
||||||
app_client,
|
app_client,
|
||||||
app_client_csv_max_mb_one,
|
app_client_csv_max_mb_one,
|
||||||
app_client_with_cors,
|
app_client_with_cors,
|
||||||
|
app_client_with_trace,
|
||||||
)
|
)
|
||||||
|
|
||||||
EXPECTED_TABLE_CSV = """id,content
|
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])
|
assert 1002 == len([b for b in response.body.split(b"\r\n") if b])
|
||||||
|
|
||||||
|
|
||||||
def test_csv_trace(app_client):
|
def test_csv_trace(app_client_with_trace):
|
||||||
response = app_client.get("/fixtures/simple_primary_key.csv?_trace=1")
|
response = app_client_with_trace.get("/fixtures/simple_primary_key.csv?_trace=1")
|
||||||
assert response.headers["content-type"] == "text/html; charset=utf-8"
|
assert response.headers["content-type"] == "text/html; charset=utf-8"
|
||||||
soup = Soup(response.text, "html.parser")
|
soup = Soup(response.text, "html.parser")
|
||||||
assert (
|
assert (
|
||||||
|
@ -171,13 +172,13 @@ def test_csv_trace(app_client):
|
||||||
assert "select id, content from simple_primary_key" in soup.find("pre").text
|
assert "select id, content from simple_primary_key" in soup.find("pre").text
|
||||||
|
|
||||||
|
|
||||||
def test_table_csv_stream_does_not_calculate_facets(app_client):
|
def test_table_csv_stream_does_not_calculate_facets(app_client_with_trace):
|
||||||
response = app_client.get("/fixtures/simple_primary_key.csv?_trace=1")
|
response = app_client_with_trace.get("/fixtures/simple_primary_key.csv?_trace=1")
|
||||||
soup = Soup(response.text, "html.parser")
|
soup = Soup(response.text, "html.parser")
|
||||||
assert "select content, count(*) as n" not in soup.find("pre").text
|
assert "select content, count(*) as n" not in soup.find("pre").text
|
||||||
|
|
||||||
|
|
||||||
def test_table_csv_stream_does_not_calculate_counts(app_client):
|
def test_table_csv_stream_does_not_calculate_counts(app_client_with_trace):
|
||||||
response = app_client.get("/fixtures/simple_primary_key.csv?_trace=1")
|
response = app_client_with_trace.get("/fixtures/simple_primary_key.csv?_trace=1")
|
||||||
soup = Soup(response.text, "html.parser")
|
soup = Soup(response.text, "html.parser")
|
||||||
assert "select count(*)" not in soup.find("pre").text
|
assert "select count(*)" not in soup.find("pre").text
|
||||||
|
|
Ładowanie…
Reference in New Issue