mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-25 12:33:33 +00:00
Allow redirecting logs to a specific file
There's a surprising lack of crates which are like env_logger, but also allow writing to a file. Let's write our own then!
This commit is contained in:
parent
f647edcb08
commit
a53c6f6fee
4 changed files with 103 additions and 11 deletions
|
@ -13,6 +13,7 @@ use vfs::AbsPathBuf;
|
||||||
|
|
||||||
pub(crate) struct Args {
|
pub(crate) struct Args {
|
||||||
pub(crate) verbosity: Verbosity,
|
pub(crate) verbosity: Verbosity,
|
||||||
|
pub(crate) log_file: Option<PathBuf>,
|
||||||
pub(crate) command: Command,
|
pub(crate) command: Command,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +54,11 @@ impl Args {
|
||||||
|
|
||||||
if matches.contains("--version") {
|
if matches.contains("--version") {
|
||||||
matches.finish().or_else(handle_extra_flags)?;
|
matches.finish().or_else(handle_extra_flags)?;
|
||||||
return Ok(Args { verbosity: Verbosity::Normal, command: Command::Version });
|
return Ok(Args {
|
||||||
|
verbosity: Verbosity::Normal,
|
||||||
|
log_file: None,
|
||||||
|
command: Command::Version,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let verbosity = match (
|
let verbosity = match (
|
||||||
|
@ -68,8 +73,9 @@ impl Args {
|
||||||
(false, true, false) => Verbosity::Verbose,
|
(false, true, false) => Verbosity::Verbose,
|
||||||
(false, true, true) => bail!("Invalid flags: -q conflicts with -v"),
|
(false, true, true) => bail!("Invalid flags: -q conflicts with -v"),
|
||||||
};
|
};
|
||||||
|
let log_file = matches.opt_value_from_str("--log-file")?;
|
||||||
|
|
||||||
let help = Ok(Args { verbosity, command: Command::Help });
|
let help = Ok(Args { verbosity, log_file: None, command: Command::Help });
|
||||||
let subcommand = match matches.subcommand()? {
|
let subcommand = match matches.subcommand()? {
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
None => {
|
None => {
|
||||||
|
@ -78,7 +84,7 @@ impl Args {
|
||||||
return help;
|
return help;
|
||||||
}
|
}
|
||||||
matches.finish().or_else(handle_extra_flags)?;
|
matches.finish().or_else(handle_extra_flags)?;
|
||||||
return Ok(Args { verbosity, command: Command::RunServer });
|
return Ok(Args { verbosity, log_file, command: Command::RunServer });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let command = match subcommand.as_str() {
|
let command = match subcommand.as_str() {
|
||||||
|
@ -345,7 +351,7 @@ ARGS:
|
||||||
return help;
|
return help;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(Args { verbosity, command })
|
Ok(Args { verbosity, log_file, command })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
73
crates/rust-analyzer/src/bin/logger.rs
Normal file
73
crates/rust-analyzer/src/bin/logger.rs
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
//! Simple logger that logs either to stderr or to a file, using `env_logger`
|
||||||
|
//! filter syntax. Amusingly, there's no crates.io crate that can do this and
|
||||||
|
//! only this.
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
fs::File,
|
||||||
|
io::{BufWriter, Write},
|
||||||
|
};
|
||||||
|
|
||||||
|
use env_logger::filter::{Builder, Filter};
|
||||||
|
use log::{Log, Metadata, Record};
|
||||||
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
|
pub(crate) struct Logger {
|
||||||
|
filter: Filter,
|
||||||
|
file: Option<Mutex<BufWriter<File>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Logger {
|
||||||
|
pub(crate) fn new(log_file: Option<File>, filter: Option<&str>) -> Logger {
|
||||||
|
let filter = {
|
||||||
|
let mut builder = Builder::new();
|
||||||
|
if let Some(filter) = filter {
|
||||||
|
builder.parse(filter);
|
||||||
|
}
|
||||||
|
builder.build()
|
||||||
|
};
|
||||||
|
|
||||||
|
let file = log_file.map(|it| Mutex::new(BufWriter::new(it)));
|
||||||
|
|
||||||
|
Logger { filter, file }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn install(self) {
|
||||||
|
let max_level = self.filter.filter();
|
||||||
|
let _ = log::set_boxed_logger(Box::new(self)).map(|()| log::set_max_level(max_level));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Log for Logger {
|
||||||
|
fn enabled(&self, metadata: &Metadata) -> bool {
|
||||||
|
self.filter.enabled(metadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log(&self, record: &Record) {
|
||||||
|
if !self.filter.matches(record) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
match &self.file {
|
||||||
|
Some(w) => {
|
||||||
|
let _ = writeln!(
|
||||||
|
w.lock(),
|
||||||
|
"[{} {}] {}",
|
||||||
|
record.level(),
|
||||||
|
record.module_path().unwrap_or_default(),
|
||||||
|
record.args(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
None => eprintln!(
|
||||||
|
"[{} {}] {}",
|
||||||
|
record.level(),
|
||||||
|
record.module_path().unwrap_or_default(),
|
||||||
|
record.args(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&self) {
|
||||||
|
if let Some(w) = &self.file {
|
||||||
|
let _ = w.lock().flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,8 +2,9 @@
|
||||||
//!
|
//!
|
||||||
//! Based on cli flags, either spawns an LSP server, or runs a batch analysis
|
//! Based on cli flags, either spawns an LSP server, or runs a batch analysis
|
||||||
mod args;
|
mod args;
|
||||||
|
mod logger;
|
||||||
|
|
||||||
use std::{convert::TryFrom, process};
|
use std::{convert::TryFrom, env, fs, path::PathBuf, process};
|
||||||
|
|
||||||
use lsp_server::Connection;
|
use lsp_server::Connection;
|
||||||
use project_model::ProjectManifest;
|
use project_model::ProjectManifest;
|
||||||
|
@ -26,8 +27,8 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_main() -> Result<()> {
|
fn try_main() -> Result<()> {
|
||||||
setup_logging()?;
|
|
||||||
let args = args::Args::parse()?;
|
let args = args::Args::parse()?;
|
||||||
|
setup_logging(args.log_file)?;
|
||||||
match args.command {
|
match args.command {
|
||||||
args::Command::RunServer => run_server()?,
|
args::Command::RunServer => run_server()?,
|
||||||
args::Command::ProcMacro => proc_macro_srv::cli::run()?,
|
args::Command::ProcMacro => proc_macro_srv::cli::run()?,
|
||||||
|
@ -52,9 +53,21 @@ fn try_main() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_logging() -> Result<()> {
|
fn setup_logging(log_file: Option<PathBuf>) -> Result<()> {
|
||||||
std::env::set_var("RUST_BACKTRACE", "short");
|
env::set_var("RUST_BACKTRACE", "short");
|
||||||
env_logger::try_init_from_env("RA_LOG")?;
|
|
||||||
|
let log_file = match log_file {
|
||||||
|
Some(path) => {
|
||||||
|
if let Some(parent) = path.parent() {
|
||||||
|
let _ = fs::create_dir_all(parent);
|
||||||
|
}
|
||||||
|
Some(fs::File::create(path)?)
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
let filter = env::var("RA_LOG").ok();
|
||||||
|
logger::Logger::new(log_file, filter.as_deref()).install();
|
||||||
|
|
||||||
profile::init();
|
profile::init();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -95,7 +108,7 @@ fn run_server() -> Result<()> {
|
||||||
{
|
{
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
None => {
|
None => {
|
||||||
let cwd = std::env::current_dir()?;
|
let cwd = env::current_dir()?;
|
||||||
AbsPathBuf::assert(cwd)
|
AbsPathBuf::assert(cwd)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -351,7 +351,7 @@ Relative paths are interpreted relative to `rust-project.json` file location or
|
||||||
|
|
||||||
See https://github.com/rust-analyzer/rust-project.json-example for a small example.
|
See https://github.com/rust-analyzer/rust-project.json-example for a small example.
|
||||||
|
|
||||||
You can set `RA_LOG` environmental variable to `"'rust_analyzer=info"` to inspect how rust-analyzer handles config and project loading.
|
You can set `RA_LOG` environmental variable to `rust_analyzer=info` to inspect how rust-analyzer handles config and project loading.
|
||||||
|
|
||||||
== Features
|
== Features
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue