From e7ef251882b95d77d168c54b2b82d3278911da64 Mon Sep 17 00:00:00 2001 From: esefxdz Date: Sat, 17 Oct 2026 15:37:29 +0300 Subject: [PATCH] fix(runtime): keep Xray's startup output so a failed core can be diagnosed The monitor thread read the core's merged stdout/stderr and dropped every line: process.inputStream.bufferedReader().use { reader -> reader.forEachLine { _ -> } } so the only trace of a core that died at startup was "Xray process exited with code 23", with nothing to say why. That exit is real and intermittent - it showed up on both a handset and an emulator - and was undiagnosable in the field. Keep at most 40 lines, and only from the core's first 10 seconds, then log them when the exit code is non-zero. A core up longer than that may be carrying traffic and its log can name destinations, which must never reach the device log (rule 95), so past the window the buffer is dropped rather than kept. Co-Authored-By: Claude Opus 5 --- .../xray/core/XrayCoreManager.kt | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/local_packages/flutter_vless_android-1.1.5/android/src/main/kotlin/com/github/tfox/flutter_vless/xray/core/XrayCoreManager.kt b/local_packages/flutter_vless_android-1.1.5/android/src/main/kotlin/com/github/tfox/flutter_vless/xray/core/XrayCoreManager.kt index e7deba1..16a4348 100644 --- a/local_packages/flutter_vless_android-1.1.5/android/src/main/kotlin/com/github/tfox/flutter_vless/xray/core/XrayCoreManager.kt +++ b/local_packages/flutter_vless_android-1.1.5/android/src/main/kotlin/com/github/tfox/flutter_vless/xray/core/XrayCoreManager.kt @@ -39,6 +39,13 @@ object XrayCoreManager { private const val NOTIFICATION_ID = 1 private const val TAG = "XrayCoreManager" + + // The core's output is kept only for its first seconds, and only to explain + // a non-zero exit. A core that has been up longer than this may be carrying + // traffic, and its log can name destinations, which must never reach the + // device log (rule 95) - so past the window the buffer is dropped. + private const val STARTUP_LOG_WINDOW_MS = 10_000L + private const val STARTUP_LOG_MAX_LINES = 40 private var xrayProcess: Process? = null private var countDownTimer: CountDownTimer? = null private var seconds = 0 @@ -349,12 +356,29 @@ object XrayCoreManager { // Monitor process in a separate thread to detect crash Thread { try { + val startedAt = System.currentTimeMillis() + val startupLog = ArrayDeque() process.inputStream.bufferedReader().use { reader -> - reader.forEachLine { _ -> } + reader.forEachLine { line -> + if (System.currentTimeMillis() - startedAt <= STARTUP_LOG_WINDOW_MS) { + if (startupLog.size >= STARTUP_LOG_MAX_LINES) startupLog.removeFirst() + startupLog.addLast(line) + } else if (startupLog.isNotEmpty()) { + startupLog.clear() + } + } } - + val exitCode = process.waitFor() - Log.e(TAG, "Xray process exited with code $exitCode") + if (exitCode != 0 && startupLog.isNotEmpty()) { + Log.e( + TAG, + "Xray process exited with code $exitCode; startup output:\n" + + startupLog.joinToString("\n") + ) + } else { + Log.e(TAG, "Xray process exited with code $exitCode") + } if (xrayProcess === process && AppConfigs.V2RAY_CONFIG?.RUNTIME_TOKEN == config.RUNTIME_TOKEN && (AppConfigs.V2RAY_STATE == AppConfigs.V2RAY_STATES.V2RAY_CONNECTED ||