diff --git a/crates/zed/src/logger.rs b/crates/zed/src/logger.rs new file mode 100644 index 0000000000000000000000000000000000000000..5a889424d06ae0c581ee5e680059030622f5a24a --- /dev/null +++ b/crates/zed/src/logger.rs @@ -0,0 +1,122 @@ +use chrono::Offset; +use env_logger::Builder; +use log::LevelFilter; +use simplelog::ConfigBuilder; +use std::fs::{self, File, OpenOptions}; +use std::io::{self, Write}; +use time::UtcOffset; + +pub fn init_logger() { + let level = LevelFilter::Info; + + // Prevent log file from becoming too large. + const KIB: u64 = 1024; + const MIB: u64 = 1024 * KIB; + const MAX_LOG_BYTES: u64 = MIB; + if std::fs::metadata(paths::log_file()).map_or(false, |metadata| metadata.len() > MAX_LOG_BYTES) + { + let _ = std::fs::rename(paths::log_file(), paths::old_log_file()); + } + + match LogWriter::new(MAX_LOG_BYTES) { + Ok(writer) => { + let mut config_builder = ConfigBuilder::new(); + + config_builder.set_time_format_rfc3339(); + let local_offset = chrono::Local::now().offset().fix().local_minus_utc(); + if let Ok(offset) = UtcOffset::from_whole_seconds(local_offset) { + config_builder.set_time_offset(offset); + } + + #[cfg(any(target_os = "linux", target_os = "freebsd"))] + { + config_builder.add_filter_ignore_str("zbus"); + config_builder.add_filter_ignore_str("blade_graphics::hal::resource"); + config_builder.add_filter_ignore_str("naga::back::spv::writer"); + } + + let config = config_builder.build(); + simplelog::WriteLogger::init(level, config, writer) + .expect("could not initialize logger"); + } + Err(err) => { + init_stdout_logger(); + log::error!( + "could not open log file, defaulting to stdout logging: {}", + err + ); + } + } +} + +pub fn init_stdout_logger() { + Builder::new() + .parse_default_env() + .format(|buf, record| { + use env_logger::fmt::style::{AnsiColor, Style}; + + let subtle = Style::new().fg_color(Some(AnsiColor::BrightBlack.into())); + write!(buf, "{subtle}[{subtle:#}")?; + write!( + buf, + "{} ", + chrono::Local::now().format("%Y-%m-%dT%H:%M:%S%:z") + )?; + let level_style = buf.default_level_style(record.level()); + write!(buf, "{level_style}{:<5}{level_style:#}", record.level())?; + if let Some(path) = record.module_path() { + write!(buf, " {path}")?; + } + write!(buf, "{subtle}]{subtle:#}")?; + writeln!(buf, " {}", record.args()) + }) + .init(); +} + +struct LogWriter { + file: File, + max_size: u64, + current_size: u64, +} + +impl LogWriter { + fn new(max_size: u64) -> io::Result { + let file = OpenOptions::new() + .create(true) + .append(true) + .open(paths::log_file())?; + let current_size = file.metadata()?.len(); + + Ok(LogWriter { + file, + max_size, + current_size, + }) + } + + fn replace(&mut self) -> io::Result<()> { + self.file.sync_all()?; + fs::rename(paths::log_file(), paths::old_log_file())?; + self.file = OpenOptions::new() + .create(true) + .append(true) + .open(paths::log_file())?; + self.current_size = 0; + Ok(()) + } +} + +impl Write for LogWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + if self.current_size + buf.len() as u64 > self.max_size { + self.replace()?; + } + let bytes = self.file.write(buf)?; + self.current_size += bytes as u64; + Ok(bytes) + } + + fn flush(&mut self) -> io::Result<()> { + self.file.flush() + } +} diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index b7ea61651c5b7f3729923cdba16e6ad58958e516..a66f298f3925f4d50bae5ae6ee1bc2b363f32791 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -1,11 +1,11 @@ // Disable command line from opening on release mode #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] +mod logger; mod reliability; mod zed; use anyhow::{anyhow, Context as _, Result}; -use chrono::Offset; use clap::{command, Parser}; use cli::FORCE_CLI_MODE_ENV_VAR_NAME; use client::{parse_zed_link, Client, ProxySettings, UserStore}; @@ -13,7 +13,6 @@ use collab_ui::channel_view::ChannelView; use collections::HashMap; use db::kvp::{GLOBAL_KEY_VALUE_STORE, KEY_VALUE_STORE}; use editor::Editor; -use env_logger::Builder; use extension::ExtensionHostProxy; use extension_host::ExtensionStore; use fs::{Fs, RealFs}; @@ -24,11 +23,11 @@ use gpui::{App, AppContext as _, Application, AsyncApp, UpdateGlobal as _}; use gpui_tokio::Tokio; use http_client::{read_proxy_from_env, Uri}; use language::LanguageRegistry; -use log::LevelFilter; use prompt_library::PromptBuilder; use reqwest_client::ReqwestClient; use assets::Assets; +use logger::{init_logger, init_stdout_logger}; use node_runtime::{NodeBinaryOptions, NodeRuntime}; use parking_lot::Mutex; use project::project_settings::ProjectSettings; @@ -36,11 +35,9 @@ use recent_projects::{open_ssh_project, SshSettings}; use release_channel::{AppCommitSha, AppVersion, ReleaseChannel}; use session::{AppSession, Session}; use settings::{watch_config_file, Settings, SettingsStore}; -use simplelog::ConfigBuilder; use std::{ env, - fs::OpenOptions, - io::{self, IsTerminal, Write}, + io::{self, IsTerminal}, path::{Path, PathBuf}, process, sync::Arc, @@ -49,7 +46,6 @@ use theme::{ ActiveTheme, IconThemeNotFoundError, SystemAppearance, ThemeNotFoundError, ThemeRegistry, ThemeSettings, }; -use time::UtcOffset; use util::{maybe, ResultExt, TryFutureExt}; use uuid::Uuid; use welcome::{show_welcome_view, BaseKeymap, FIRST_OPEN}; @@ -198,7 +194,11 @@ fn main() { return; } - init_logger(); + if stdout_is_a_pty() { + init_stdout_logger(); + } else { + init_logger(); + } log::info!("========== starting zed =========="); @@ -936,82 +936,6 @@ fn init_paths() -> HashMap> { }) } -fn init_logger() { - if stdout_is_a_pty() { - init_stdout_logger(); - } else { - let level = LevelFilter::Info; - - // Prevent log file from becoming too large. - const KIB: u64 = 1024; - const MIB: u64 = 1024 * KIB; - const MAX_LOG_BYTES: u64 = MIB; - if std::fs::metadata(paths::log_file()) - .map_or(false, |metadata| metadata.len() > MAX_LOG_BYTES) - { - let _ = std::fs::rename(paths::log_file(), paths::old_log_file()); - } - - match OpenOptions::new() - .create(true) - .append(true) - .open(paths::log_file()) - { - Ok(log_file) => { - let mut config_builder = ConfigBuilder::new(); - - config_builder.set_time_format_rfc3339(); - let local_offset = chrono::Local::now().offset().fix().local_minus_utc(); - if let Ok(offset) = UtcOffset::from_whole_seconds(local_offset) { - config_builder.set_time_offset(offset); - } - - #[cfg(any(target_os = "linux", target_os = "freebsd"))] - { - config_builder.add_filter_ignore_str("zbus"); - config_builder.add_filter_ignore_str("blade_graphics::hal::resource"); - config_builder.add_filter_ignore_str("naga::back::spv::writer"); - } - - let config = config_builder.build(); - simplelog::WriteLogger::init(level, config, log_file) - .expect("could not initialize logger"); - } - Err(err) => { - init_stdout_logger(); - log::error!( - "could not open log file, defaulting to stdout logging: {}", - err - ); - } - } - } -} - -fn init_stdout_logger() { - Builder::new() - .parse_default_env() - .format(|buf, record| { - use env_logger::fmt::style::{AnsiColor, Style}; - - let subtle = Style::new().fg_color(Some(AnsiColor::BrightBlack.into())); - write!(buf, "{subtle}[{subtle:#}")?; - write!( - buf, - "{} ", - chrono::Local::now().format("%Y-%m-%dT%H:%M:%S%:z") - )?; - let level_style = buf.default_level_style(record.level()); - write!(buf, "{level_style}{:<5}{level_style:#}", record.level())?; - if let Some(path) = record.module_path() { - write!(buf, " {path}")?; - } - write!(buf, "{subtle}]{subtle:#}")?; - writeln!(buf, " {}", record.args()) - }) - .init(); -} - fn stdout_is_a_pty() -> bool { std::env::var(FORCE_CLI_MODE_ENV_VAR_NAME).ok().is_none() && io::stdout().is_terminal() }