From cab8c69f729d0f20a395369a8183375bd04cbb79 Mon Sep 17 00:00:00 2001 From: Majjo <90888719+NoxRare@users.noreply.github.com> Date: Mon, 20 Apr 2026 01:15:25 +0200 Subject: [PATCH] feat: story debug panel, log level filters, verbose story event logging --- .../features/impl/global/CompanionServer.kt | 108 +++++++++++++++++- 1 file changed, 102 insertions(+), 6 deletions(-) diff --git a/core/src/main/kotlin/me/eternal/purrfectsnap/core/features/impl/global/CompanionServer.kt b/core/src/main/kotlin/me/eternal/purrfectsnap/core/features/impl/global/CompanionServer.kt index fbc42409..6e8c50dc 100644 --- a/core/src/main/kotlin/me/eternal/purrfectsnap/core/features/impl/global/CompanionServer.kt +++ b/core/src/main/kotlin/me/eternal/purrfectsnap/core/features/impl/global/CompanionServer.kt @@ -33,6 +33,10 @@ class CompanionServer : Feature("CompanionServer") { private val capturedStories = java.util.concurrent.ConcurrentHashMap>>() private val capturedStoryUrls = java.util.concurrent.ConcurrentHashMap>() + // Ring buffer of story debug events (last 100) — exposed at /story-debug + private val storyDebugLog = ArrayDeque>(100) + private val storyDebugLock = Any() + // Prevent re-entrant log capture while the server itself logs private val isCapturing = ThreadLocal() @@ -200,15 +204,34 @@ class CompanionServer : Feature("CompanionServer") { return true } + private fun pushStoryDebug(type: String, extra: Map = emptyMap()) { + val entry = mutableMapOf("ts" to System.currentTimeMillis().toString(), "type" to type) + entry.putAll(extra) + synchronized(storyDebugLock) { + if (storyDebugLog.size >= 100) storyDebugLog.removeFirst() + storyDebugLog.addLast(entry) + } + } + private fun subscribeToStoryEvents() { context.event.subscribe(NetworkApiRequestEvent::class) { event -> val urlPath = event.url.substringBefore("?") if (!urlPath.contains("df-mixer-prod")) return@subscribe val endpoint = urlPath.substringAfterLast("/") + // Log every df-mixer-prod URL hit for debugging + pushStoryDebug("seen", mapOf("url" to urlPath, "endpoint" to endpoint)) + context.log.info("CompanionServer: [story] seen url=$urlPath", "CompanionServer") if (endpoint !in setOf("stories", "batch_stories", "story_lookup", "batch_story_lookup")) return@subscribe + pushStoryDebug("matched", mapOf("endpoint" to endpoint)) + context.log.info("CompanionServer: [$endpoint] matched, awaiting response", "CompanionServer") event.onSuccess { buffer -> - buffer ?: return@onSuccess - context.log.verbose("CompanionServer: [$endpoint] response ${buffer.size} bytes", "CompanionServer") + if (buffer == null) { + pushStoryDebug("null_buffer", mapOf("endpoint" to endpoint)) + context.log.warn("CompanionServer: [$endpoint] null buffer in onSuccess", "CompanionServer") + return@onSuccess + } + context.log.info("CompanionServer: [$endpoint] response ${buffer.size} bytes", "CompanionServer") + pushStoryDebug("response", mapOf("endpoint" to endpoint, "bytes" to buffer.size)) runCatching { var parsed = 0 // story_lookup / batch_story_lookup: field 1 → repeated story entries @@ -248,9 +271,11 @@ class CompanionServer : Feature("CompanionServer") { } } } - context.log.verbose("CompanionServer: [$endpoint] parsed $parsed new stories", "CompanionServer") + context.log.info("CompanionServer: [$endpoint] parsed $parsed new stories (total=${capturedStories.values.sumOf { it.size }})", "CompanionServer") + pushStoryDebug("parsed", mapOf("endpoint" to endpoint, "count" to parsed, "totalStories" to capturedStories.values.sumOf { it.size })) }.onFailure { context.log.warn("CompanionServer: [$endpoint] proto parse failed: ${it.message}", "CompanionServer") + pushStoryDebug("parse_error", mapOf("endpoint" to endpoint, "error" to (it.message ?: "unknown"))) } } } @@ -359,6 +384,7 @@ class CompanionServer : Feature("CompanionServer") { } serveJson(socket, result) } + rawPath == "/story-debug" -> serveJson(socket, synchronized(storyDebugLock) { storyDebugLog.toList() }) else -> respond404(socket) } } @@ -537,7 +563,7 @@ button:hover{background:#333}