Fix windows open log viewer

- There was tendency where webview would just freeze on windows, lets ensure this doesn't happen.
This commit is contained in:
Tommy Verrall
2026-05-06 16:55:58 +02:00
parent 664782c0c6
commit 4aabb4ed56
7 changed files with 119 additions and 56 deletions
+17 -5
View File
@@ -1,8 +1,13 @@
use std::str::FromStr;
// Log webview emit chain: keep `cfg(not(windows))` here aligned with
// `platform_constants::SECONDARY_LOG_WEBVIEW_SUPPORTED` (compile-time strip on Windows).
use fern::colors::{Color, ColoredLevelConfig};
#[cfg(not(target_os = "windows"))]
use serde::Serialize;
#[cfg(not(target_os = "windows"))]
use serde_repr::{Deserialize_repr, Serialize_repr};
#[cfg(not(target_os = "windows"))]
use tauri::{Emitter, Manager};
use time::{format_description, OffsetDateTime};
@@ -23,7 +28,9 @@ fn formatted_time() -> String {
_now.format(&format).unwrap()
}
#[cfg_attr(target_os = "windows", allow(unused_variables))]
pub fn setup_logging(app_handle: tauri::AppHandle) -> Result<(), log::SetLoggerError> {
#[cfg(not(target_os = "windows"))]
let log_window_app = app_handle.clone();
let colors = ColoredLevelConfig::new()
.trace(Color::Magenta)
@@ -48,6 +55,7 @@ pub fn setup_logging(app_handle: tauri::AppHandle) -> Result<(), log::SetLoggerE
})
.chain(std::io::stdout());
#[cfg(not(target_os = "windows"))]
let tauri_event_config = fern::Dispatch::new()
.format(move |out, message, record| {
out.finish(format_args!(
@@ -76,10 +84,12 @@ pub fn setup_logging(app_handle: tauri::AppHandle) -> Result<(), log::SetLoggerE
}
}));
base_config
.chain(stdout_config)
.chain(tauri_event_config)
.apply()
#[cfg(not(target_os = "windows"))]
let dispatch = base_config.chain(stdout_config).chain(tauri_event_config);
#[cfg(target_os = "windows")]
let dispatch = base_config.chain(stdout_config);
dispatch.apply()
}
trait FernExt {
@@ -118,13 +128,14 @@ fn global_level() -> log::LevelFilter {
}
}
#[cfg(not(target_os = "windows"))]
#[derive(Debug, Serialize, Clone)]
struct LogMessage {
message: String,
level: LogLevel,
}
// Serialize to u16 instead of strings.
#[cfg(not(target_os = "windows"))]
#[derive(Debug, Clone, Deserialize_repr, Serialize_repr)]
#[repr(u16)]
enum LogLevel {
@@ -135,6 +146,7 @@ enum LogLevel {
Error,
}
#[cfg(not(target_os = "windows"))]
impl From<log::Level> for LogLevel {
fn from(level: log::Level) -> Self {
match level {
+4 -1
View File
@@ -213,6 +213,7 @@ fn main() {
signatures::ed25519_signing_payload::generate_nym_node_bonding_msg_payload,
signatures::ed25519_signing_payload::vesting_generate_gateway_bonding_msg_payload,
help::log::help_log_toggle_window,
help::log::log_viewer_window_supported,
app::window::create_main_window,
app::window::create_auth_window,
app::react::set_react_state,
@@ -221,7 +222,9 @@ fn main() {
.menu(menu::build_app_menu)
.on_menu_event(|app, event| {
if event.id() == SHOW_LOG_WINDOW {
let _r = help::log::help_log_toggle_window(app.app_handle().clone());
if let Err(err) = help::log::help_log_toggle_window(app.app_handle().clone()) {
::log::warn!("Show logs menu action failed: {err}");
}
}
})
.setup(|app| Ok(log::setup_logging(app.app_handle().clone())?))
+3 -1
View File
@@ -1,6 +1,8 @@
use tauri::menu::{Menu, MenuBuilder, MenuItemBuilder, SubmenuBuilder};
use tauri::{AppHandle, Runtime};
use crate::platform_constants::SECONDARY_LOG_WEBVIEW_SUPPORTED;
pub const SHOW_LOG_WINDOW: &str = "show_log_window";
pub fn build_app_menu<R: Runtime>(app: &AppHandle<R>) -> tauri::Result<Menu<R>> {
@@ -13,7 +15,7 @@ pub fn build_app_menu<R: Runtime>(app: &AppHandle<R>) -> tauri::Result<Menu<R>>
let mut menu_builder = MenuBuilder::new(app).item(&edit_submenu);
if std::env::var("NYM_WALLET_ENABLE_LOG").is_ok() {
if std::env::var("NYM_WALLET_ENABLE_LOG").is_ok() && SECONDARY_LOG_WEBVIEW_SUPPORTED {
let help_text = MenuItemBuilder::with_id(SHOW_LOG_WINDOW, "Show logs").build(app)?;
let help_submenu = SubmenuBuilder::new(app, "Help")
+50 -33
View File
@@ -1,8 +1,16 @@
use crate::error::BackendError;
use crate::platform_constants::SECONDARY_LOG_WEBVIEW_SUPPORTED;
use crate::webview_theme::NYM_WALLET_WEBVIEW_BG;
use tauri::webview::PageLoadEvent;
use tauri::Manager;
/// Separate log viewer window is disabled on Windows due to WebView2 freezes / "(Not Responding)".
#[tauri::command]
#[must_use]
pub fn log_viewer_window_supported() -> bool {
SECONDARY_LOG_WEBVIEW_SUPPORTED
}
#[tauri::command]
pub fn help_log_toggle_window(app_handle: tauri::AppHandle) -> Result<(), BackendError> {
if let Some(current_log_window) = app_handle.get_webview_window("log") {
@@ -13,41 +21,50 @@ pub fn help_log_toggle_window(app_handle: tauri::AppHandle) -> Result<(), Backen
return Ok(());
}
log::info!("Creating log window...");
match tauri::WebviewWindowBuilder::new(
&app_handle,
"log",
tauri::WebviewUrl::App("log.html".into()),
)
.title("Nym Wallet Logs")
.background_color(NYM_WALLET_WEBVIEW_BG)
.use_https_scheme(true)
.on_page_load(|window, payload| match payload.event() {
PageLoadEvent::Started => {
log::debug!("Log webview load started: {}", payload.url());
}
PageLoadEvent::Finished => {
log::info!("Log webview load finished: {}", payload.url());
if std::env::var("NYM_WALLET_LOG_WEBVIEW_DEVTOOLS")
.ok()
.as_deref()
== Some("1")
{
window.open_devtools();
}
}
})
.build()
#[cfg(target_os = "windows")]
{
Ok(window) => {
if let Err(err) = window.set_focus() {
log::error!("Unable to focus log window: {err}");
log::info!("Log viewer window is disabled on Windows; use stdout or RUST_LOG.");
return Ok(());
}
#[cfg(not(target_os = "windows"))]
{
log::info!("Creating log window...");
match tauri::WebviewWindowBuilder::new(
&app_handle,
"log",
tauri::WebviewUrl::App("log.html".into()),
)
.title("Nym Wallet Logs")
.background_color(NYM_WALLET_WEBVIEW_BG)
.use_https_scheme(true)
.on_page_load(|window, payload| match payload.event() {
PageLoadEvent::Started => {
log::debug!("Log webview load started: {}", payload.url());
}
PageLoadEvent::Finished => {
log::info!("Log webview load finished: {}", payload.url());
if std::env::var("NYM_WALLET_LOG_WEBVIEW_DEVTOOLS")
.ok()
.as_deref()
== Some("1")
{
window.open_devtools();
}
}
})
.build()
{
Ok(window) => {
if let Err(err) = window.set_focus() {
log::error!("Unable to focus log window: {err}");
}
Ok(())
}
Err(err) => {
log::error!("Unable to create log window: {err}");
Err(BackendError::NewWindowError)
}
Ok(())
}
Err(err) => {
log::error!("Unable to create log window: {err}");
Err(BackendError::NewWindowError)
}
}
}
@@ -1,6 +1,9 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
/// Secondary log viewer `WebviewWindow` is disabled on Windows due to WebView2 freezes.
pub const SECONDARY_LOG_WEBVIEW_SUPPORTED: bool = cfg!(not(target_os = "windows"));
pub const CONFIG_DIR_NAME: &str = "nym-wallet";
pub const CONFIG_FILENAME: &str = "config.toml";
pub const STORAGE_DIR_NAME: &str = "nym-wallet";
@@ -1,12 +1,27 @@
import React, { useContext } from 'react';
import React, { useContext, useEffect, useState } from 'react';
import { Box, Button, Divider, Stack, Typography } from '@mui/material';
import { helpLogToggleWindow } from '../../requests';
import { helpLogToggleWindow, logViewerWindowSupported } from '../../requests';
import { AppContext } from '../../context';
import { config } from '../../config';
import SelectValidator from '../../components/Settings/SelectValidator';
const AdvancedSettings = () => {
const { handleShowTerminal, appEnv } = useContext(AppContext);
const [logViewerOk, setLogViewerOk] = useState<boolean | null>(null);
useEffect(() => {
let cancelled = false;
logViewerWindowSupported()
.then((ok) => {
if (!cancelled) setLogViewerOk(ok);
})
.catch(() => {
if (!cancelled) setLogViewerOk(false);
});
return () => {
cancelled = true;
};
}, []);
return (
<Box pb={3}>
@@ -28,20 +43,28 @@ const AdvancedSettings = () => {
<Divider />
</>
)}
<Stack direction="row" justifyContent="space-between" padding={3}>
<Stack direction="column" gap={1}>
<Typography variant="h6">Logs</Typography>
<Typography variant="body2" sx={{ color: 'text.secondary' }}>
Open logs to monitor all actions in the wallet
</Typography>
</Stack>
<Box alignSelf="flex-end">
<Button variant="text" onClick={() => helpLogToggleWindow()}>
Open logs
</Button>
</Box>
</Stack>
<Divider />
{logViewerOk !== null && (
<>
<Stack direction="row" justifyContent="space-between" padding={3}>
<Stack direction="column" gap={1}>
<Typography variant="h6">Logs</Typography>
<Typography variant="body2" sx={{ color: 'text.secondary' }}>
{logViewerOk
? 'Open logs to monitor all actions in the wallet'
: 'The in-app log viewer window is not available on Windows. Run the wallet from a terminal or set RUST_LOG to capture logs on stdout.'}
</Typography>
</Stack>
{logViewerOk ? (
<Box alignSelf="flex-end">
<Button variant="text" onClick={() => helpLogToggleWindow()}>
Open logs
</Button>
</Box>
) : null}
</Stack>
<Divider />
</>
)}
<SelectValidator />
</Box>
);
+3
View File
@@ -1,3 +1,6 @@
import { invokeWrapper } from './wrapper';
export const helpLogToggleWindow = async () => invokeWrapper<void>('help_log_toggle_window', {});
export const logViewerWindowSupported = async () =>
invokeWrapper<boolean>('log_viewer_window_supported', {});