Add API catch-all endpoint for logging

enabled using a setting.
Fix #153
This commit is contained in:
Klaas van Schelven
2025-07-16 17:22:38 +02:00
parent d35327fe80
commit 995c627fe6
4 changed files with 46 additions and 1 deletions

View File

@@ -44,6 +44,7 @@ DEFAULTS = {
"DIGEST_IMMEDIATELY": True,
"VALIDATE_ON_DIGEST": "none", # other legal values are "warn" and "strict"
"KEEP_ENVELOPES": 0, # set to a number to store that many; 0 means "store none". This is for debugging.
"API_LOG_UNIMPLEMENTED_CALLS": False, # if True, log unimplemented API calls; see #153
# MAX* below mirror the (current) values for the Sentry Relay
"MAX_EVENT_SIZE": _MEBIBYTE,

View File

@@ -120,6 +120,7 @@ BUGSINK = {
"VALIDATE_ON_DIGEST": "warn",
# "KEEP_ENVELOPES": 10,
"API_LOG_UNIMPLEMENTED_CALLS": True,
# set MAX_EVENTS* very high to be able to do serious performance testing (which I do often in my dev environment)
"MAX_EVENTS_PER_PROJECT_PER_5_MINUTES": 1_000_000,

View File

@@ -11,7 +11,7 @@ from teams.views import debug_email as debug_teams_email
from bugsink.app_settings import get_settings
from users.views import signup, confirm_email, resend_confirmation, request_reset_password, reset_password, preferences
from ingest.views import download_envelope
from files.views import chunk_upload, artifact_bundle_assemble
from files.views import chunk_upload, artifact_bundle_assemble, api_catch_all
from bugsink.decorators import login_exempt
from .views import home, trigger_error, favicon, settings_view, silence_email_system_warning, counts, health_check_ready
@@ -52,6 +52,8 @@ urlpatterns = [
path('api/', include('ingest.urls')),
path('api/<path:subpath>', api_catch_all, name='api_catch_all'),
# not in /api/ because it's not part of the ingest API, but still part of the ingest app
path('ingest/envelope/<str:envelope_id>/', download_envelope, name='download_envelope'),

View File

@@ -2,10 +2,12 @@ import json
from hashlib import sha1
from gzip import GzipFile
from io import BytesIO
import logging
from django.http import JsonResponse, HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth.decorators import user_passes_test
from django.http import Http404
from sentry.assemble import ChunkFileState
@@ -16,6 +18,8 @@ from bsmain.models import AuthToken
from .models import Chunk, File
from .tasks import assemble_artifact_bundle
logger = logging.getLogger("bugsink.api")
_KIBIBYTE = 1024
_MEBIBYTE = 1024 * _KIBIBYTE
@@ -200,3 +204,40 @@ def download_file(request, checksum):
response = HttpResponse(file.data, content_type="application/octet-stream")
response["Content-Disposition"] = f"attachment; filename={file.filename}"
return response
@csrf_exempt
def api_catch_all(request, subpath):
if not get_settings().API_LOG_UNIMPLEMENTED_CALLS:
raise Http404("Unimplemented API endpoint: /api/" + subpath)
lines = [
"Unimplemented API usage:",
f" Path: /api/{subpath}",
f" Method: {request.method}",
]
if request.GET:
lines.append(f" GET: {request.GET.dict()}")
if request.POST:
lines.append(f" POST: {request.POST.dict()}")
body = request.body
if body:
try:
decoded = body.decode("utf-8", errors="replace").strip()
lines.append(" Body:")
lines.append(f" {decoded[:500]}")
try:
parsed = json.loads(decoded)
pretty = json.dumps(parsed, indent=2)[:10_000]
lines.append(" JSON body:")
lines.extend(f" {line}" for line in pretty.splitlines())
except json.JSONDecodeError:
pass
except Exception as e:
lines.append(f" Body: <decode error: {e}>")
logger.info("\n".join(lines))
raise Http404("Unimplemented API endpoint: /api/" + subpath)