Experimental suppor tfor request IDs

This commit is contained in:
Simon Willison 2025-02-06 10:39:26 -08:00
commit b8ad73b70b
2 changed files with 14 additions and 1 deletions

View file

@ -3,6 +3,7 @@ import asyncio
from typing import Any, Dict, Iterable, List, Optional, Sequence, Tuple, Union from typing import Any, Dict, Iterable, List, Optional, Sequence, Tuple, Union
import asgi_csrf import asgi_csrf
import collections import collections
import contextvars
import dataclasses import dataclasses
import datetime import datetime
import functools import functools
@ -21,6 +22,7 @@ import threading
import time import time
import types import types
import urllib.parse import urllib.parse
import uuid
from concurrent import futures from concurrent import futures
from pathlib import Path from pathlib import Path
@ -272,6 +274,7 @@ class Datasette:
internal=None, internal=None,
): ):
self._startup_invoked = False self._startup_invoked = False
self._request_id = contextvars.ContextVar("request_id", default=None)
assert config_dir is None or isinstance( assert config_dir is None or isinstance(
config_dir, Path config_dir, Path
), "config_dir= should be a pathlib.Path" ), "config_dir= should be a pathlib.Path"
@ -452,6 +455,10 @@ class Datasette:
self._root_token = secrets.token_hex(32) self._root_token = secrets.token_hex(32)
self.client = DatasetteClient(self) self.client = DatasetteClient(self)
@property
def request_id(self):
return self._request_id.get()
async def apply_metadata_json(self): async def apply_metadata_json(self):
# Apply any metadata entries from metadata.json to the internal tables # Apply any metadata entries from metadata.json to the internal tables
# step 1: top-level metadata # step 1: top-level metadata
@ -1016,6 +1023,7 @@ class Datasette:
used_default = True used_default = True
self._permission_checks.append( self._permission_checks.append(
{ {
"request_id": self.request_id,
"when": datetime.datetime.now(datetime.timezone.utc).isoformat(), "when": datetime.datetime.now(datetime.timezone.utc).isoformat(),
"actor": actor, "actor": actor,
"action": action, "action": action,
@ -1721,7 +1729,11 @@ class DatasetteRouter:
if raw_path: if raw_path:
path = raw_path.decode("ascii") path = raw_path.decode("ascii")
path = path.partition("?")[0] path = path.partition("?")[0]
return await self.route_path(scope, receive, send, path) token = self.ds._request_id.set(str(uuid.uuid4()))
try:
return await self.route_path(scope, receive, send, path)
finally:
self.ds._request_id.reset(token)
async def route_path(self, scope, receive, send, path): async def route_path(self, scope, receive, send, path):
# Strip off base_url if present before routing # Strip off base_url if present before routing

View file

@ -135,6 +135,7 @@ debugPost.addEventListener('submit', function(ev) {
<span class="check-used-default">(used default)</span> <span class="check-used-default">(used default)</span>
{% endif %} {% endif %}
</h2> </h2>
<p>{{ check.request_id }}</p>
<p><strong>Actor:</strong> {{ check.actor|tojson }}</p> <p><strong>Actor:</strong> {{ check.actor|tojson }}</p>
{% if check.resource %} {% if check.resource %}
<p><strong>Resource:</strong> {{ check.resource }}</p> <p><strong>Resource:</strong> {{ check.resource }}</p>