desktop: use tracing for logging (#13135)

This commit is contained in:
Brendan Allan
2026-02-11 19:36:27 +08:00
committed by GitHub
parent 8bfd6fdba2
commit a25b2af05a
10 changed files with 265 additions and 155 deletions

View File

@@ -535,8 +535,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
dependencies = [
"iana-time-zone",
"js-sys",
"num-traits",
"serde",
"wasm-bindgen",
"windows-link 0.2.1",
]
@@ -2491,6 +2493,15 @@ dependencies = [
"syn 2.0.110",
]
[[package]]
name = "matchers"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
dependencies = [
"regex-automata",
]
[[package]]
name = "matches"
version = "0.1.10"
@@ -2691,6 +2702,15 @@ dependencies = [
"zbus",
]
[[package]]
name = "nu-ansi-term"
version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "num-conv"
version = "0.1.0"
@@ -3065,6 +3085,7 @@ dependencies = [
name = "opencode-desktop"
version = "0.0.0"
dependencies = [
"chrono",
"comrak",
"dirs",
"futures",
@@ -3096,6 +3117,9 @@ dependencies = [
"tauri-plugin-window-state",
"tauri-specta",
"tokio",
"tracing",
"tracing-appender",
"tracing-subscriber",
"uuid",
"webkit2gtk",
"windows 0.61.3",
@@ -4412,6 +4436,15 @@ dependencies = [
"digest",
]
[[package]]
name = "sharded-slab"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
dependencies = [
"lazy_static",
]
[[package]]
name = "shared_child"
version = "1.1.1"
@@ -5472,6 +5505,15 @@ dependencies = [
"syn 2.0.110",
]
[[package]]
name = "thread_local"
version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
dependencies = [
"cfg-if",
]
[[package]]
name = "tiff"
version = "0.10.3"
@@ -5745,9 +5787,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
[[package]]
name = "tracing"
version = "0.1.41"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
dependencies = [
"pin-project-lite",
"tracing-attributes",
@@ -5755,10 +5797,22 @@ dependencies = [
]
[[package]]
name = "tracing-attributes"
version = "0.1.30"
name = "tracing-appender"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
checksum = "786d480bce6247ab75f005b14ae1624ad978d3029d9113f0a22fa1ac773faeaf"
dependencies = [
"crossbeam-channel",
"thiserror 2.0.17",
"time",
"tracing-subscriber",
]
[[package]]
name = "tracing-attributes"
version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
dependencies = [
"proc-macro2",
"quote",
@@ -5767,11 +5821,41 @@ dependencies = [
[[package]]
name = "tracing-core"
version = "0.1.34"
version = "0.1.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
dependencies = [
"once_cell",
"valuable",
]
[[package]]
name = "tracing-log"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
dependencies = [
"log",
"once_cell",
"tracing-core",
]
[[package]]
name = "tracing-subscriber"
version = "0.3.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e"
dependencies = [
"matchers",
"nu-ansi-term",
"once_cell",
"regex-automata",
"sharded-slab",
"smallvec",
"thread_local",
"tracing",
"tracing-core",
"tracing-log",
]
[[package]]
@@ -5964,6 +6048,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "valuable"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
[[package]]
name = "version-compare"
version = "0.2.1"

View File

@@ -47,6 +47,10 @@ specta = "=2.0.0-rc.22"
specta-typescript = "0.0.9"
tauri-specta = { version = "=2.0.0-rc.21", features = ["derive", "typescript"] }
dirs = "6.0.0"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
tracing-appender = "0.2"
chrono = "0.4"
[target.'cfg(target_os = "linux")'.dependencies]
gtk = "0.18.2"

View File

@@ -6,10 +6,7 @@ use tauri_plugin_shell::{
use tauri_plugin_store::StoreExt;
use tokio::sync::oneshot;
use crate::{
LogState,
constants::{MAX_LOG_ENTRIES, SETTINGS_STORE, WSL_ENABLED_KEY},
};
use crate::constants::{SETTINGS_STORE, WSL_ENABLED_KEY};
const CLI_INSTALL_DIR: &str = ".opencode/bin";
const CLI_BINARY_NAME: &str = "opencode";
@@ -29,7 +26,7 @@ pub async fn get_config(app: &AppHandle) -> Option<Config> {
create_command(app, "debug config", &[])
.output()
.await
.inspect_err(|e| eprintln!("Failed to read OC config: {e}"))
.inspect_err(|e| tracing::warn!("Failed to read OC config: {e}"))
.ok()
.and_then(|out| String::from_utf8(out.stdout.to_vec()).ok())
.and_then(|s| serde_json::from_str::<Config>(&s).ok())
@@ -104,12 +101,12 @@ pub fn install_cli(app: tauri::AppHandle) -> Result<String, String> {
pub fn sync_cli(app: tauri::AppHandle) -> Result<(), String> {
if cfg!(debug_assertions) {
println!("Skipping CLI sync for debug build");
tracing::debug!("Skipping CLI sync for debug build");
return Ok(());
}
if !is_cli_installed() {
println!("No CLI installation found, skipping sync");
tracing::info!("No CLI installation found, skipping sync");
return Ok(());
}
@@ -132,21 +129,21 @@ pub fn sync_cli(app: tauri::AppHandle) -> Result<(), String> {
let app_version = app.package_info().version.clone();
if cli_version >= app_version {
println!(
"CLI version {} is up to date (app version: {}), skipping sync",
cli_version, app_version
tracing::info!(
%cli_version, %app_version,
"CLI is up to date, skipping sync"
);
return Ok(());
}
println!(
"CLI version {} is older than app version {}, syncing",
cli_version, app_version
tracing::info!(
%cli_version, %app_version,
"CLI is older than app version, syncing"
);
install_cli(app)?;
println!("Synced installed CLI");
tracing::info!("Synced installed CLI");
Ok(())
}
@@ -207,7 +204,7 @@ pub fn create_command(app: &tauri::AppHandle, args: &str, extra_env: &[(&str, St
if cfg!(windows) {
if is_wsl_enabled(app) {
println!("WSL is enabled, spawning CLI server in WSL.");
tracing::info!("WSL is enabled, spawning CLI server in WSL");
let version = app.package_info().version.to_string();
let mut script = vec![
"set -e".to_string(),
@@ -280,38 +277,9 @@ pub fn serve(
port: u32,
password: &str,
) -> (CommandChild, oneshot::Receiver<TerminatedPayload>) {
let log_state = app.state::<LogState>();
let log_state_clone = log_state.inner().clone();
let (exit_tx, exit_rx) = oneshot::channel::<TerminatedPayload>();
println!("spawning sidecar on port {port}");
if let Ok(mut logs) = log_state_clone.0.lock() {
let args =
format!("--print-logs --log-level WARN serve --hostname {hostname} --port {port}");
#[cfg(target_os = "windows")]
{
logs.push_back(format!("[SPAWN] sidecar=opencode-cli args=\"{args}\"\n"));
}
#[cfg(not(target_os = "windows"))]
{
let sidecar = get_sidecar_path(app);
let shell = get_user_shell();
let cmd = if shell.ends_with("/nu") {
format!("^\"{}\" {}", sidecar.display(), args)
} else {
format!("\"{}\" {}", sidecar.display(), args)
};
logs.push_back(format!("[SPAWN] shell=\"{shell}\" argv=\"-il -c {cmd}\"\n"));
}
while logs.len() > MAX_LOG_ENTRIES {
logs.pop_front();
}
}
tracing::info!(port, "Spawning sidecar");
let envs = [
("OPENCODE_SERVER_USERNAME", "opencode".to_string()),
@@ -332,50 +300,22 @@ pub fn serve(
match event {
CommandEvent::Stdout(line_bytes) => {
let line = String::from_utf8_lossy(&line_bytes);
print!("{line}");
// Store log in shared state
if let Ok(mut logs) = log_state_clone.0.lock() {
logs.push_back(format!("[STDOUT] {}", line));
// Keep only the last MAX_LOG_ENTRIES
while logs.len() > MAX_LOG_ENTRIES {
logs.pop_front();
}
}
tracing::info!(target: "sidecar", "{line}");
}
CommandEvent::Stderr(line_bytes) => {
let line = String::from_utf8_lossy(&line_bytes);
eprint!("{line}");
// Store log in shared state
if let Ok(mut logs) = log_state_clone.0.lock() {
logs.push_back(format!("[STDERR] {}", line));
// Keep only the last MAX_LOG_ENTRIES
while logs.len() > MAX_LOG_ENTRIES {
logs.pop_front();
}
}
tracing::info!(target: "sidecar", "{line}");
}
CommandEvent::Error(err) => {
eprintln!("{err}");
if let Ok(mut logs) = log_state_clone.0.lock() {
logs.push_back(format!("[ERROR] {err}\n"));
while logs.len() > MAX_LOG_ENTRIES {
logs.pop_front();
}
}
tracing::error!(target: "sidecar", "{err}");
}
CommandEvent::Terminated(payload) => {
if let Ok(mut logs) = log_state_clone.0.lock() {
logs.push_back(format!(
"[EXIT] code={:?} signal={:?}\n",
payload.code, payload.signal
));
while logs.len() > MAX_LOG_ENTRIES {
logs.pop_front();
}
}
tracing::info!(
target: "sidecar",
code = ?payload.code,
signal = ?payload.signal,
"Sidecar terminated"
);
if let Some(tx) = exit_tx.take() {
let _ = tx.send(payload);

View File

@@ -4,7 +4,6 @@ pub const SETTINGS_STORE: &str = "opencode.settings.dat";
pub const DEFAULT_SERVER_URL_KEY: &str = "defaultServerUrl";
pub const WSL_ENABLED_KEY: &str = "wslEnabled";
pub const UPDATER_ENABLED: bool = option_env!("TAURI_SIGNING_PRIVATE_KEY").is_some();
pub const MAX_LOG_ENTRIES: usize = 200;
pub fn window_state_flags() -> StateFlags {
StateFlags::all() - StateFlags::DECORATIONS - StateFlags::VISIBLE

View File

@@ -15,9 +15,9 @@ use std::io::{Error, Result};
use std::sync::Mutex;
use windows::Win32::Foundation::{CloseHandle, HANDLE};
use windows::Win32::System::JobObjects::{
AssignProcessToJobObject, CreateJobObjectW, JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE,
JOBOBJECT_EXTENDED_LIMIT_INFORMATION, JobObjectExtendedLimitInformation,
SetInformationJobObject,
AssignProcessToJobObject, CreateJobObjectW, JobObjectExtendedLimitInformation,
SetInformationJobObject, JOBOBJECT_EXTENDED_LIMIT_INFORMATION,
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE,
};
use windows::Win32::System::Threading::{OpenProcess, PROCESS_SET_QUOTA, PROCESS_TERMINATE};
@@ -111,7 +111,7 @@ impl JobObjectState {
error: Mutex::new(None),
},
Err(e) => {
eprintln!("Failed to create job object: {e}");
tracing::error!("Failed to create job object: {e}");
Self {
job: Mutex::new(None),
error: Mutex::new(Some(format!("Failed to create job object: {e}"))),
@@ -123,11 +123,11 @@ impl JobObjectState {
pub fn assign_pid(&self, pid: u32) {
if let Some(job) = self.job.lock().unwrap().as_ref() {
if let Err(e) = job.assign_pid(pid) {
eprintln!("Failed to assign process {pid} to job object: {e}");
tracing::error!(pid, "Failed to assign process to job object: {e}");
*self.error.lock().unwrap() =
Some(format!("Failed to assign process to job object: {e}"));
} else {
println!("Assigned process {pid} to job object for automatic cleanup");
tracing::info!(pid, "Assigned process to job object for automatic cleanup");
}
}
}

View File

@@ -4,6 +4,7 @@ mod constants;
mod job_object;
#[cfg(target_os = "linux")]
pub mod linux_display;
mod logging;
mod markdown;
mod server;
mod window_customizer;
@@ -16,7 +17,6 @@ use futures::{
#[cfg(windows)]
use job_object::*;
use std::{
collections::VecDeque,
env,
net::TcpListener,
path::PathBuf,
@@ -85,14 +85,11 @@ impl ServerState {
}
}
#[derive(Clone)]
struct LogState(Arc<Mutex<VecDeque<String>>>);
#[tauri::command]
#[specta::specta]
fn kill_sidecar(app: AppHandle) {
let Some(server_state) = app.try_state::<ServerState>() else {
println!("Server not running");
tracing::info!("Server not running");
return;
};
@@ -102,24 +99,17 @@ fn kill_sidecar(app: AppHandle) {
.expect("Failed to acquire mutex lock")
.take()
else {
println!("Server state missing");
tracing::info!("Server state missing");
return;
};
let _ = server_state.kill();
println!("Killed server");
tracing::info!("Killed server");
}
async fn get_logs(app: AppHandle) -> Result<String, String> {
let log_state = app.try_state::<LogState>().ok_or("Log state not found")?;
let logs = log_state
.0
.lock()
.map_err(|_| "Failed to acquire log lock")?;
Ok(logs.iter().cloned().collect::<Vec<_>>().join(""))
fn get_logs() -> String {
logging::tail()
}
#[tauri::command]
@@ -715,10 +705,18 @@ pub fn run() {
.plugin(tauri_plugin_decorum::init())
.invoke_handler(builder.invoke_handler())
.setup(move |app| {
let app = app.handle().clone();
let handle = app.handle().clone();
builder.mount_events(&app);
tauri::async_runtime::spawn(initialize(app));
let log_dir = app
.path()
.app_log_dir()
.expect("failed to resolve app log dir");
// Hold the guard in managed state so it lives for the app's lifetime,
// ensuring all buffered logs are flushed on shutdown.
handle.manage(logging::init(&log_dir));
builder.mount_events(&handle);
tauri::async_runtime::spawn(initialize(handle));
Ok(())
});
@@ -732,7 +730,7 @@ pub fn run() {
.expect("error while running tauri application")
.run(|app, event| {
if let RunEvent::Exit = event {
println!("Received Exit");
tracing::info!("Received Exit");
kill_sidecar(app.clone());
}
@@ -780,9 +778,8 @@ fn test_export_types() {
#[derive(tauri_specta::Event, serde::Deserialize, specta::Type)]
struct LoadingWindowComplete;
// #[tracing::instrument(skip_all)]
async fn initialize(app: AppHandle) {
println!("Initializing app");
tracing::info!("Initializing app");
let (init_tx, init_rx) = watch::channel(InitStep::ServerWaiting);
@@ -795,7 +792,7 @@ async fn initialize(app: AppHandle) {
let loading_window_complete = event_once_fut::<LoadingWindowComplete>(&app);
println!("Main and loading windows created");
tracing::info!("Main and loading windows created");
let sqlite_enabled = option_env!("OPENCODE_SQLITE").is_some();
@@ -806,7 +803,7 @@ async fn initialize(app: AppHandle) {
async move {
let mut sqlite_exists = sqlite_file_exists();
println!("Setting up server connection");
tracing::info!("Setting up server connection");
let server_connection = setup_server_connection(app.clone()).await;
// we delay spawning this future so that the timeout is created lazily
@@ -831,16 +828,13 @@ async fn initialize(app: AppHandle) {
if let Some(err) = err {
let _ = child.kill();
let logs = get_logs(app.clone())
.await
.unwrap_or_else(|e| format!("[DESKTOP] Failed to read sidecar logs: {e}\n"));
return Err(format!(
"Failed to spawn OpenCode Server ({err}). Logs:\n{logs}"
"Failed to spawn OpenCode Server ({err}). Logs:\n{}",
get_logs()
));
}
println!("CLI health check OK");
tracing::info!("CLI health check OK");
#[cfg(windows)]
{
@@ -868,11 +862,11 @@ async fn initialize(app: AppHandle) {
if let Some(cli_health_check) = cli_health_check {
if sqlite_enabled {
println!("Does sqlite file exist: {sqlite_exists}");
tracing::debug!(sqlite_exists, "Checking sqlite file existence");
if !sqlite_exists {
println!(
"Sqlite file not found at {}, waiting for it to be generated",
opencode_db_path().expect("failed to get db path").display()
tracing::info!(
path = %opencode_db_path().expect("failed to get db path").display(),
"Sqlite file not found, waiting for it to be generated"
);
let _ = init_tx.send(InitStep::SqliteWaiting);
@@ -897,7 +891,7 @@ async fn initialize(app: AppHandle) {
.await
.is_err()
{
println!("Loading task timed out, showing loading window");
tracing::debug!("Loading task timed out, showing loading window");
let app = app.clone();
let loading_window = LoadingWindow::create(&app).expect("Failed to create loading window");
sleep(Duration::from_secs(1)).await;
@@ -910,14 +904,14 @@ async fn initialize(app: AppHandle) {
let _ = loading_task.await;
println!("Loading done, completing initialisation");
tracing::info!("Loading done, completing initialisation");
let _ = init_tx.send(InitStep::Done);
if loading_window.is_some() {
loading_window_complete.await;
println!("Loading window completed");
tracing::info!("Loading window completed");
}
MainWindow::create(&app).expect("Failed to create main window");
@@ -931,9 +925,6 @@ fn setup_app(app: &tauri::AppHandle, init_rx: watch::Receiver<InitStep>) {
#[cfg(any(target_os = "linux", all(debug_assertions, windows)))]
app.deep_link().register_all().ok();
// Initialize log state
app.manage(LogState(Arc::new(Mutex::new(VecDeque::new()))));
#[cfg(windows)]
app.manage(JobObjectState::new());
@@ -943,7 +934,7 @@ fn setup_app(app: &tauri::AppHandle, init_rx: watch::Receiver<InitStep>) {
fn spawn_cli_sync_task(app: AppHandle) {
tokio::spawn(async move {
if let Err(e) = sync_cli(app) {
eprintln!("Failed to sync CLI: {e}");
tracing::error!("Failed to sync CLI: {e}");
}
});
}
@@ -963,12 +954,12 @@ enum ServerConnection {
async fn setup_server_connection(app: AppHandle) -> ServerConnection {
let custom_url = get_saved_server_url(&app).await;
println!("Attempting server connection to custom url: {custom_url:?}");
tracing::info!(?custom_url, "Attempting server connection");
if let Some(url) = custom_url
&& server::check_health_or_ask_retry(&app, &url).await
{
println!("Connected to custom server: {}", url);
tracing::info!(%url, "Connected to custom server");
return ServerConnection::Existing { url: url.clone() };
}
@@ -976,15 +967,15 @@ async fn setup_server_connection(app: AppHandle) -> ServerConnection {
let hostname = "127.0.0.1";
let local_url = format!("http://{hostname}:{local_port}");
println!("Checking health of server '{}'", local_url);
tracing::debug!(url = %local_url, "Checking health of local server");
if server::check_health(&local_url, None).await {
println!("Health check OK, using existing server");
tracing::info!(url = %local_url, "Health check OK, using existing server");
return ServerConnection::Existing { url: local_url };
}
let password = uuid::Uuid::new_v4().to_string();
println!("Spawning new local server");
tracing::info!("Spawning new local server");
let (child, health_check) =
server::spawn_local_server(app, hostname.to_string(), local_port, password.clone());

View File

@@ -14,7 +14,11 @@ struct DisplayConfig {
}
fn dir() -> Option<PathBuf> {
Some(dirs::data_dir()?.join(if cfg!(debug_assertions) { "ai.opencode.desktop.dev" } else { "ai.opencode.desktop" }))
Some(dirs::data_dir()?.join(if cfg!(debug_assertions) {
"ai.opencode.desktop.dev"
} else {
"ai.opencode.desktop"
}))
}
fn path() -> Option<PathBuf> {
@@ -22,13 +26,12 @@ fn path() -> Option<PathBuf> {
}
pub fn read_wayland() -> Option<bool> {
let raw = std::fs::read_to_string(dbg!(path()?)).ok()?;
let raw = std::fs::read_to_string(path()?).ok()?;
let root = serde_json::from_str::<serde_json::Value>(&raw)
.ok()?
.get(LINUX_DISPLAY_CONFIG_KEY).cloned()?;
serde_json::from_value::<DisplayConfig>(root)
.ok()?
.wayland
.get(LINUX_DISPLAY_CONFIG_KEY)
.cloned()?;
serde_json::from_value::<DisplayConfig>(root).ok()?.wayland
}
pub fn write_wayland(app: &AppHandle, value: bool) -> Result<(), String> {

View File

@@ -0,0 +1,83 @@
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::{Path, PathBuf};
use tracing_appender::non_blocking::WorkerGuard;
use tracing_subscriber::{EnvFilter, fmt, layer::SubscriberExt, util::SubscriberInitExt};
const MAX_LOG_AGE_DAYS: u64 = 7;
const TAIL_LINES: usize = 1000;
static LOG_PATH: std::sync::OnceLock<PathBuf> = std::sync::OnceLock::new();
pub fn init(log_dir: &Path) -> WorkerGuard {
std::fs::create_dir_all(log_dir).expect("failed to create log directory");
cleanup(log_dir);
let timestamp = chrono::Local::now().format("%Y-%m-%d_%H-%M-%S");
let filename = format!("opencode-desktop_{timestamp}.log");
let log_path = log_dir.join(&filename);
LOG_PATH
.set(log_path.clone())
.expect("logging already initialized");
let file = File::create(&log_path).expect("failed to create log file");
let (non_blocking, guard) = tracing_appender::non_blocking(file);
let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| {
if cfg!(debug_assertions) {
EnvFilter::new("opencode_lib=debug,opencode_desktop=debug,sidecar=debug")
} else {
EnvFilter::new("opencode_lib=info,opencode_desktop=info,sidecar=info")
}
});
tracing_subscriber::registry()
.with(filter)
.with(fmt::layer().with_writer(std::io::stderr))
.with(
fmt::layer()
.with_writer(non_blocking)
.with_ansi(false),
)
.init();
guard
}
pub fn tail() -> String {
let Some(path) = LOG_PATH.get() else {
return String::new();
};
let Ok(file) = File::open(path) else {
return String::new();
};
let lines: Vec<String> = BufReader::new(file)
.lines()
.map_while(Result::ok)
.collect();
let start = lines.len().saturating_sub(TAIL_LINES);
lines[start..].join("\n")
}
fn cleanup(log_dir: &Path) {
let cutoff = std::time::SystemTime::now()
- std::time::Duration::from_secs(MAX_LOG_AGE_DAYS * 24 * 60 * 60);
let Ok(entries) = std::fs::read_dir(log_dir) else {
return;
};
for entry in entries.flatten() {
if let Ok(meta) = entry.metadata()
&& let Ok(modified) = meta.modified()
&& modified < cutoff
{
let _ = std::fs::remove_file(entry.path());
}
}
}

View File

@@ -43,7 +43,7 @@ fn configure_display_backend() -> Option<String> {
set_env_if_absent("WEBKIT_DISABLE_DMABUF_RENDERER", "1");
return Some(
"Wayland session detected; forcing X11 backend to avoid compositor protocol errors. \
Set OC_ALLOW_WAYLAND=1 to keep native Wayland."
Set OC_ALLOW_WAYLAND=1 to keep native Wayland."
.into(),
);
}
@@ -86,7 +86,7 @@ fn main() {
#[cfg(target_os = "linux")]
{
if let Some(backend_note) = configure_display_backend() {
eprintln!("{backend_note:?}");
eprintln!("{backend_note}");
}
}

View File

@@ -93,14 +93,14 @@ pub fn set_wsl_config(app: AppHandle, config: WslConfig) -> Result<(), String> {
pub async fn get_saved_server_url(app: &tauri::AppHandle) -> Option<String> {
if let Some(url) = get_default_server_url(app.clone()).ok().flatten() {
println!("Using desktop-specific custom URL: {url}");
tracing::info!(%url, "Using desktop-specific custom URL");
return Some(url);
}
if let Some(cli_config) = cli::get_config(app).await
&& let Some(url) = get_server_url_from_config(&cli_config)
{
println!("Using custom server URL from config: {url}");
tracing::info!(%url, "Using custom server URL from config");
return Some(url);
}
@@ -124,7 +124,7 @@ pub fn spawn_local_server(
tokio::time::sleep(Duration::from_millis(100)).await;
if check_health(&url, Some(&password)).await {
println!("Server ready after {:?}", timestamp.elapsed());
tracing::info!(elapsed = ?timestamp.elapsed(), "Server ready");
return Ok(());
}
}
@@ -216,7 +216,7 @@ fn normalize_hostname_for_url(hostname: &str) -> String {
fn get_server_url_from_config(config: &cli::Config) -> Option<String> {
let server = config.server.as_ref()?;
let port = server.port?;
println!("server.port found in OC config: {port}");
tracing::debug!(port, "server.port found in OC config");
let hostname = server
.hostname
.as_ref()
@@ -227,7 +227,7 @@ fn get_server_url_from_config(config: &cli::Config) -> Option<String> {
}
pub async fn check_health_or_ask_retry(app: &AppHandle, url: &str) -> bool {
println!("Checking health for {url}");
tracing::debug!(%url, "Checking health");
loop {
if check_health(url, None).await {
return true;