diff options
author | Aleksey Kladov <[email protected]> | 2020-08-26 11:21:12 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2020-08-26 12:20:46 +0100 |
commit | a53c6f6feef231ecfb4e66d0e446e4148e816a2c (patch) | |
tree | e2cbb7e27e4fb667ace5d59ca785c2eb24869e62 | |
parent | f647edcb080f50e01762a31eebd9ca94c982c768 (diff) |
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!
-rw-r--r-- | crates/rust-analyzer/src/bin/args.rs | 14 | ||||
-rw-r--r-- | crates/rust-analyzer/src/bin/logger.rs | 73 | ||||
-rw-r--r-- | crates/rust-analyzer/src/bin/main.rs | 25 | ||||
-rw-r--r-- | docs/user/manual.adoc | 2 |
4 files changed, 103 insertions, 11 deletions
diff --git a/crates/rust-analyzer/src/bin/args.rs b/crates/rust-analyzer/src/bin/args.rs index 0bc92431a..45dc62ea7 100644 --- a/crates/rust-analyzer/src/bin/args.rs +++ b/crates/rust-analyzer/src/bin/args.rs | |||
@@ -13,6 +13,7 @@ use vfs::AbsPathBuf; | |||
13 | 13 | ||
14 | pub(crate) struct Args { | 14 | pub(crate) struct Args { |
15 | pub(crate) verbosity: Verbosity, | 15 | pub(crate) verbosity: Verbosity, |
16 | pub(crate) log_file: Option<PathBuf>, | ||
16 | pub(crate) command: Command, | 17 | pub(crate) command: Command, |
17 | } | 18 | } |
18 | 19 | ||
@@ -53,7 +54,11 @@ impl Args { | |||
53 | 54 | ||
54 | if matches.contains("--version") { | 55 | if matches.contains("--version") { |
55 | matches.finish().or_else(handle_extra_flags)?; | 56 | matches.finish().or_else(handle_extra_flags)?; |
56 | return Ok(Args { verbosity: Verbosity::Normal, command: Command::Version }); | 57 | return Ok(Args { |
58 | verbosity: Verbosity::Normal, | ||
59 | log_file: None, | ||
60 | command: Command::Version, | ||
61 | }); | ||
57 | } | 62 | } |
58 | 63 | ||
59 | let verbosity = match ( | 64 | let verbosity = match ( |
@@ -68,8 +73,9 @@ impl Args { | |||
68 | (false, true, false) => Verbosity::Verbose, | 73 | (false, true, false) => Verbosity::Verbose, |
69 | (false, true, true) => bail!("Invalid flags: -q conflicts with -v"), | 74 | (false, true, true) => bail!("Invalid flags: -q conflicts with -v"), |
70 | }; | 75 | }; |
76 | let log_file = matches.opt_value_from_str("--log-file")?; | ||
71 | 77 | ||
72 | let help = Ok(Args { verbosity, command: Command::Help }); | 78 | let help = Ok(Args { verbosity, log_file: None, command: Command::Help }); |
73 | let subcommand = match matches.subcommand()? { | 79 | let subcommand = match matches.subcommand()? { |
74 | Some(it) => it, | 80 | Some(it) => it, |
75 | None => { | 81 | None => { |
@@ -78,7 +84,7 @@ impl Args { | |||
78 | return help; | 84 | return help; |
79 | } | 85 | } |
80 | matches.finish().or_else(handle_extra_flags)?; | 86 | matches.finish().or_else(handle_extra_flags)?; |
81 | return Ok(Args { verbosity, command: Command::RunServer }); | 87 | return Ok(Args { verbosity, log_file, command: Command::RunServer }); |
82 | } | 88 | } |
83 | }; | 89 | }; |
84 | let command = match subcommand.as_str() { | 90 | let command = match subcommand.as_str() { |
@@ -345,7 +351,7 @@ ARGS: | |||
345 | return help; | 351 | return help; |
346 | } | 352 | } |
347 | }; | 353 | }; |
348 | Ok(Args { verbosity, command }) | 354 | Ok(Args { verbosity, log_file, command }) |
349 | } | 355 | } |
350 | } | 356 | } |
351 | 357 | ||
diff --git a/crates/rust-analyzer/src/bin/logger.rs b/crates/rust-analyzer/src/bin/logger.rs new file mode 100644 index 000000000..3bcb1ae37 --- /dev/null +++ b/crates/rust-analyzer/src/bin/logger.rs | |||
@@ -0,0 +1,73 @@ | |||
1 | //! Simple logger that logs either to stderr or to a file, using `env_logger` | ||
2 | //! filter syntax. Amusingly, there's no crates.io crate that can do this and | ||
3 | //! only this. | ||
4 | |||
5 | use std::{ | ||
6 | fs::File, | ||
7 | io::{BufWriter, Write}, | ||
8 | }; | ||
9 | |||
10 | use env_logger::filter::{Builder, Filter}; | ||
11 | use log::{Log, Metadata, Record}; | ||
12 | use parking_lot::Mutex; | ||
13 | |||
14 | pub(crate) struct Logger { | ||
15 | filter: Filter, | ||
16 | file: Option<Mutex<BufWriter<File>>>, | ||
17 | } | ||
18 | |||
19 | impl Logger { | ||
20 | pub(crate) fn new(log_file: Option<File>, filter: Option<&str>) -> Logger { | ||
21 | let filter = { | ||
22 | let mut builder = Builder::new(); | ||
23 | if let Some(filter) = filter { | ||
24 | builder.parse(filter); | ||
25 | } | ||
26 | builder.build() | ||
27 | }; | ||
28 | |||
29 | let file = log_file.map(|it| Mutex::new(BufWriter::new(it))); | ||
30 | |||
31 | Logger { filter, file } | ||
32 | } | ||
33 | |||
34 | pub(crate) fn install(self) { | ||
35 | let max_level = self.filter.filter(); | ||
36 | let _ = log::set_boxed_logger(Box::new(self)).map(|()| log::set_max_level(max_level)); | ||
37 | } | ||
38 | } | ||
39 | |||
40 | impl Log for Logger { | ||
41 | fn enabled(&self, metadata: &Metadata) -> bool { | ||
42 | self.filter.enabled(metadata) | ||
43 | } | ||
44 | |||
45 | fn log(&self, record: &Record) { | ||
46 | if !self.filter.matches(record) { | ||
47 | return; | ||
48 | } | ||
49 | match &self.file { | ||
50 | Some(w) => { | ||
51 | let _ = writeln!( | ||
52 | w.lock(), | ||
53 | "[{} {}] {}", | ||
54 | record.level(), | ||
55 | record.module_path().unwrap_or_default(), | ||
56 | record.args(), | ||
57 | ); | ||
58 | } | ||
59 | None => eprintln!( | ||
60 | "[{} {}] {}", | ||
61 | record.level(), | ||
62 | record.module_path().unwrap_or_default(), | ||
63 | record.args(), | ||
64 | ), | ||
65 | } | ||
66 | } | ||
67 | |||
68 | fn flush(&self) { | ||
69 | if let Some(w) = &self.file { | ||
70 | let _ = w.lock().flush(); | ||
71 | } | ||
72 | } | ||
73 | } | ||
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index 0e03a0ca8..266768970 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs | |||
@@ -2,8 +2,9 @@ | |||
2 | //! | 2 | //! |
3 | //! Based on cli flags, either spawns an LSP server, or runs a batch analysis | 3 | //! Based on cli flags, either spawns an LSP server, or runs a batch analysis |
4 | mod args; | 4 | mod args; |
5 | mod logger; | ||
5 | 6 | ||
6 | use std::{convert::TryFrom, process}; | 7 | use std::{convert::TryFrom, env, fs, path::PathBuf, process}; |
7 | 8 | ||
8 | use lsp_server::Connection; | 9 | use lsp_server::Connection; |
9 | use project_model::ProjectManifest; | 10 | use project_model::ProjectManifest; |
@@ -26,8 +27,8 @@ fn main() { | |||
26 | } | 27 | } |
27 | 28 | ||
28 | fn try_main() -> Result<()> { | 29 | fn try_main() -> Result<()> { |
29 | setup_logging()?; | ||
30 | let args = args::Args::parse()?; | 30 | let args = args::Args::parse()?; |
31 | setup_logging(args.log_file)?; | ||
31 | match args.command { | 32 | match args.command { |
32 | args::Command::RunServer => run_server()?, | 33 | args::Command::RunServer => run_server()?, |
33 | args::Command::ProcMacro => proc_macro_srv::cli::run()?, | 34 | args::Command::ProcMacro => proc_macro_srv::cli::run()?, |
@@ -52,9 +53,21 @@ fn try_main() -> Result<()> { | |||
52 | Ok(()) | 53 | Ok(()) |
53 | } | 54 | } |
54 | 55 | ||
55 | fn setup_logging() -> Result<()> { | 56 | fn setup_logging(log_file: Option<PathBuf>) -> Result<()> { |
56 | std::env::set_var("RUST_BACKTRACE", "short"); | 57 | env::set_var("RUST_BACKTRACE", "short"); |
57 | env_logger::try_init_from_env("RA_LOG")?; | 58 | |
59 | let log_file = match log_file { | ||
60 | Some(path) => { | ||
61 | if let Some(parent) = path.parent() { | ||
62 | let _ = fs::create_dir_all(parent); | ||
63 | } | ||
64 | Some(fs::File::create(path)?) | ||
65 | } | ||
66 | None => None, | ||
67 | }; | ||
68 | let filter = env::var("RA_LOG").ok(); | ||
69 | logger::Logger::new(log_file, filter.as_deref()).install(); | ||
70 | |||
58 | profile::init(); | 71 | profile::init(); |
59 | Ok(()) | 72 | Ok(()) |
60 | } | 73 | } |
@@ -95,7 +108,7 @@ fn run_server() -> Result<()> { | |||
95 | { | 108 | { |
96 | Some(it) => it, | 109 | Some(it) => it, |
97 | None => { | 110 | None => { |
98 | let cwd = std::env::current_dir()?; | 111 | let cwd = env::current_dir()?; |
99 | AbsPathBuf::assert(cwd) | 112 | AbsPathBuf::assert(cwd) |
100 | } | 113 | } |
101 | }; | 114 | }; |
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc index 144130b51..8c966288b 100644 --- a/docs/user/manual.adoc +++ b/docs/user/manual.adoc | |||
@@ -351,7 +351,7 @@ Relative paths are interpreted relative to `rust-project.json` file location or | |||
351 | 351 | ||
352 | See https://github.com/rust-analyzer/rust-project.json-example for a small example. | 352 | See https://github.com/rust-analyzer/rust-project.json-example for a small example. |
353 | 353 | ||
354 | You can set `RA_LOG` environmental variable to `"'rust_analyzer=info"` to inspect how rust-analyzer handles config and project loading. | 354 | You can set `RA_LOG` environmental variable to `rust_analyzer=info` to inspect how rust-analyzer handles config and project loading. |
355 | 355 | ||
356 | == Features | 356 | == Features |
357 | 357 | ||