feat: reveal log file from settings; quiet per-tick DHT peer log
The "DHT: got N peers" line fired at info every 20s per torrent and would dominate the rotated desktop log; drop it to debug. Add a Logs section to the Sources/settings dialog showing the resolved log path with a "Reveal in file manager" button, backed by two new Tauri commands (get_log_path, open_log_file). open_log_file reveals the file via tauri-plugin-opener (reveal rather than open, so it doesn't depend on a .log handler); the opener:allow-reveal-item-in-dir permission is granted. The resolved log path is stored on AppState at construction. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1081,7 +1081,7 @@ fn dht_peer_discovery(
|
||||
let query = || {
|
||||
let all_peers = dht.get_peers(info_hash_id);
|
||||
for peers in all_peers {
|
||||
log::info!("DHT: got {} peers", peers.len());
|
||||
log::debug!("DHT: got {} peers", peers.len());
|
||||
if cmd_tx.send(Command::ConnectToPeers(peers)).is_err() {
|
||||
return false; // torrent stopped
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"core:default",
|
||||
"dialog:default",
|
||||
"notification:default",
|
||||
"opener:default"
|
||||
"opener:default",
|
||||
"opener:allow-reveal-item-in-dir"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"default":{"identifier":"default","description":"Default capabilities for Monsoon","local":true,"windows":["main"],"permissions":["core:default","dialog:default","notification:default","opener:default"]}}
|
||||
{"default":{"identifier":"default","description":"Default capabilities for Monsoon","local":true,"windows":["main"],"permissions":["core:default","dialog:default","notification:default","opener:default","opener:allow-reveal-item-in-dir"]}}
|
||||
@@ -166,3 +166,21 @@ pub fn remove_remote_server(name: String, state: State<'_, Arc<AppState>>) -> Re
|
||||
pub fn take_pending_magnet(state: State<'_, Arc<AppState>>) -> Option<String> {
|
||||
state.pending_magnet.lock().ok().and_then(|mut p| p.take())
|
||||
}
|
||||
|
||||
/// The resolved path to the desktop app's rotating log file, for display.
|
||||
#[tauri::command]
|
||||
pub fn get_log_path(state: State<'_, Arc<AppState>>) -> String {
|
||||
state.log_file.to_string_lossy().into_owned()
|
||||
}
|
||||
|
||||
/// Reveal the log file in the system file manager. Revealing (rather than
|
||||
/// opening) avoids depending on a `.log` handler being registered.
|
||||
#[tauri::command]
|
||||
pub fn open_log_file(state: State<'_, Arc<AppState>>) -> Result<(), String> {
|
||||
use tauri_plugin_opener::OpenerExt;
|
||||
state
|
||||
.app
|
||||
.opener()
|
||||
.reveal_item_in_dir(&state.log_file)
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
@@ -107,6 +107,7 @@ pub fn run(initial_magnet: Option<String>) {
|
||||
let event_rx = manager.event_rx.clone();
|
||||
|
||||
let desktop_cfg = desktop_config::DesktopConfig::load();
|
||||
let log_file = paths.log_file.clone();
|
||||
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_dialog::init())
|
||||
@@ -151,13 +152,15 @@ pub fn run(initial_magnet: Option<String>) {
|
||||
commands::add_remote_server,
|
||||
commands::remove_remote_server,
|
||||
commands::take_pending_magnet,
|
||||
commands::get_log_path,
|
||||
commands::open_log_file,
|
||||
])
|
||||
.setup(move |app| {
|
||||
let app_handle = app.handle().clone();
|
||||
|
||||
// Build and manage the unified AppState (local manager + config
|
||||
// + active view). All commands resolve through this.
|
||||
let state = source::AppState::new(manager, desktop_cfg, app_handle.clone());
|
||||
let state = source::AppState::new(manager, desktop_cfg, app_handle.clone(), log_file);
|
||||
app.manage(state.clone());
|
||||
|
||||
// Scheduler tick for the local manager: independent of the event
|
||||
|
||||
@@ -20,6 +20,8 @@ pub struct AppState {
|
||||
/// up yet. Set on first-launch (before the GUI mounts) and as a fallback
|
||||
/// for second-instance handoff. Cleared by `take_pending_magnet`.
|
||||
pub pending_magnet: Mutex<Option<String>>,
|
||||
/// Resolved path to the rotating log file, surfaced in the UI.
|
||||
pub log_file: std::path::PathBuf,
|
||||
}
|
||||
|
||||
/// The "currently viewed source" — drives what `get_torrents` returns and
|
||||
@@ -46,13 +48,19 @@ impl View {
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
pub fn new(local: TorrentManager, config: DesktopConfig, app: AppHandle) -> Arc<Self> {
|
||||
pub fn new(
|
||||
local: TorrentManager,
|
||||
config: DesktopConfig,
|
||||
app: AppHandle,
|
||||
log_file: std::path::PathBuf,
|
||||
) -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
local: Mutex::new(local),
|
||||
config: Mutex::new(config),
|
||||
view: AsyncMutex::new(View::local()),
|
||||
app,
|
||||
pending_magnet: Mutex::new(None),
|
||||
log_file,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
addRemoteServer,
|
||||
removeRemoteServer,
|
||||
setDefaultSource,
|
||||
getLogPath,
|
||||
openLogFile,
|
||||
} from "./ipc";
|
||||
import { defaultSource, refreshSources, sources } from "./stores";
|
||||
|
||||
@@ -12,6 +14,22 @@
|
||||
let labelInput = $state("");
|
||||
let urlInput = $state("");
|
||||
let error = $state<string | null>(null);
|
||||
let logPath = $state("");
|
||||
|
||||
$effect(() => {
|
||||
getLogPath()
|
||||
.then((p) => (logPath = p))
|
||||
.catch(() => {});
|
||||
});
|
||||
|
||||
async function revealLog() {
|
||||
error = null;
|
||||
try {
|
||||
await openLogFile();
|
||||
} catch (e) {
|
||||
error = String(e);
|
||||
}
|
||||
}
|
||||
|
||||
async function setDefault(id: string) {
|
||||
error = null;
|
||||
@@ -123,6 +141,14 @@
|
||||
<button class="primary" onclick={addRemote}>Add</button>
|
||||
</div>
|
||||
|
||||
<h3>Logs</h3>
|
||||
<div class="logs">
|
||||
<code class="log-path">{logPath || "…"}</code>
|
||||
<button onclick={revealLog} disabled={!logPath}>
|
||||
Reveal in file manager
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if error}
|
||||
<p class="error">{error}</p>
|
||||
{/if}
|
||||
@@ -262,6 +288,28 @@
|
||||
background: rgba(199, 67, 67, 0.1);
|
||||
}
|
||||
|
||||
.logs {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.log-path {
|
||||
flex: 1;
|
||||
font-size: 12px;
|
||||
color: var(--fg-secondary);
|
||||
background: var(--bg-secondary);
|
||||
padding: 6px 10px;
|
||||
border-radius: var(--radius);
|
||||
overflow-x: auto;
|
||||
white-space: nowrap;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
.logs button {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #c74343;
|
||||
font-size: 13px;
|
||||
|
||||
@@ -138,3 +138,13 @@ export async function removeRemoteServer(name: string): Promise<void> {
|
||||
export async function takePendingMagnet(): Promise<string | null> {
|
||||
return invoke("take_pending_magnet");
|
||||
}
|
||||
|
||||
/** The resolved path to the desktop app's rotating log file. */
|
||||
export async function getLogPath(): Promise<string> {
|
||||
return invoke("get_log_path");
|
||||
}
|
||||
|
||||
/** Reveal the log file in the system file manager. */
|
||||
export async function openLogFile(): Promise<void> {
|
||||
return invoke("open_log_file");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user