diff --git a/src/app.cpp b/src/app.cpp index 9828be1..304d60b 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -5742,6 +5742,39 @@ void App::beginShutdown() }); } +std::vector App::tailDaemonDebugLog(int maxLines) const +{ + std::vector out; + if (maxLines <= 0) return out; + const std::string path = util::Platform::getDataDir() + "debug.log"; + std::error_code ec; + const auto sz = std::filesystem::file_size(path, ec); + if (ec || sz == 0) return out; + std::ifstream f(path, std::ios::binary); + if (!f) return out; + + // Read only the last ~16 KB — plenty for a handful of lines, cheap even for a multi-GB log. + const std::uintmax_t kTailBytes = 16 * 1024; + const std::uintmax_t start = sz > kTailBytes ? sz - kTailBytes : 0; + f.seekg(static_cast(start), std::ios::beg); + std::string chunk(static_cast(sz - start), '\0'); + f.read(&chunk[0], static_cast(chunk.size())); + chunk.resize(static_cast(f.gcount())); + + std::vector lines; + std::string cur; + for (char c : chunk) { + if (c == '\n') { if (!cur.empty()) lines.push_back(cur); cur.clear(); } + else if (c != '\r') cur.push_back(c); + } + if (!cur.empty()) lines.push_back(cur); + // When we seeked into the middle of the file the first line is a fragment — drop it. + if (start > 0 && !lines.empty()) lines.erase(lines.begin()); + if (static_cast(lines.size()) > maxLines) + lines.erase(lines.begin(), lines.end() - static_cast(maxLines)); + return lines; +} + void App::renderShutdownScreen() { using namespace ui::material; @@ -5974,6 +6007,10 @@ void App::renderShutdownScreen() // ------------------------------------------------------------------- if (daemon_controller_) { auto lines = daemon_controller_->recentLines(8); + // External daemon (attached, not spawned) has no captured stdout — tail its debug.log directly + // so the user can still watch the node flush the block index and exit. + if (lines.empty()) + lines = tailDaemonDebugLog(8); if (!lines.empty()) { float panelW = vp_size.x * shutElem("panel-width-fraction", 0.70f); float panelX = cx - panelW * 0.5f; diff --git a/src/app.h b/src/app.h index 93cc22a..ba1f41c 100644 --- a/src/app.h +++ b/src/app.h @@ -175,6 +175,13 @@ public: */ void renderShutdownScreen(); + /** + * @brief Tail the last N lines of the daemon's debug.log (best-effort, reads only the file tail). + * Fallback for the shutdown screen when we have no captured stdout — e.g. an external daemon we + * attached to rather than spawned — so the user can still see the node flushing/exiting. + */ + std::vector tailDaemonDebugLog(int maxLines) const; + /** * @brief Render loading overlay in content area while daemon is starting/syncing * @param contentH Height of the content area child window diff --git a/src/util/i18n.cpp b/src/util/i18n.cpp index 3230589..d1c9351 100644 --- a/src/util/i18n.cpp +++ b/src/util/i18n.cpp @@ -1456,8 +1456,8 @@ void I18n::loadBuiltinEnglish() strings_["sb_daemon_extract_failed"] = "Failed to write daemon files — check free disk space and permissions."; strings_["sb_daemon_files_failed"] = "Failed to write daemon files to %s — check free disk space and permissions."; strings_["loading_stall_title"] = "Taking longer than expected"; - strings_["loading_stall_body"] = "The daemon has been initializing for %.0fs. This can be normal after an update or on first launch (loading the block index or rescanning) — it will connect automatically once ready."; - strings_["loading_stall_hint"] = "Still stuck? Open Settings and use Restart Daemon, or check the Console for details."; + strings_["loading_stall_body"] = "Initializing for %.0fs — normal after an update or first launch. Connects automatically when ready."; + strings_["loading_stall_hint"] = "Stuck? Settings → Restart Daemon, or check the Console."; strings_["sb_plaintext_remote_blocked"] = "Refusing to send RPC credentials over plaintext to a remote host. Add rpcallowplaintext=1 to DRAGONX.conf to allow it, or enable TLS with rpctls=1."; strings_["rpc_plaintext_remote_warning"] = "Remote RPC is using plaintext HTTP. Add rpctls=1 to DRAGONX.conf if your daemon supports TLS."; strings_["settings_open_log_folder"] = "Open log folder";