Merge #7457
7457: Add no-buffering file logging and wait for a debugger option. r=vsrs a=vsrs Adds two command line flags: `--no-buffering` and `--wait-dbg`. Not sure if someone else needs this, but personally I found both flags extremely useful trying to figure out why RA does not work with Visual Studio. Or better to say why Visual Studio does not work with RA. Co-authored-by: vsrs <vit@conrlab.com>
This commit is contained in:
commit
fc08fdaf5a
6
.vscode/launch.json
vendored
6
.vscode/launch.json
vendored
@ -120,6 +120,12 @@
|
|||||||
"sourceMaps": true,
|
"sourceMaps": true,
|
||||||
"outFiles": [ "${workspaceFolder}/editors/code/out/tests/unit/**/*.js" ],
|
"outFiles": [ "${workspaceFolder}/editors/code/out/tests/unit/**/*.js" ],
|
||||||
"preLaunchTask": "Pretest"
|
"preLaunchTask": "Pretest"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Win Attach to Server",
|
||||||
|
"type": "cppvsdbg",
|
||||||
|
"processId":"${command:pickProcess}",
|
||||||
|
"request": "attach"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,9 @@ 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) log_file: Option<PathBuf>,
|
||||||
|
pub(crate) no_buffering: bool,
|
||||||
pub(crate) command: Command,
|
pub(crate) command: Command,
|
||||||
|
pub(crate) wait_dbg: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) enum Command {
|
pub(crate) enum Command {
|
||||||
@ -47,11 +49,17 @@ FLAGS:
|
|||||||
-vv, --spammy
|
-vv, --spammy
|
||||||
-q, --quiet Set verbosity
|
-q, --quiet Set verbosity
|
||||||
|
|
||||||
--log-file <PATH> Log to the specified filed instead of stderr
|
--log-file <PATH> Log to the specified file instead of stderr
|
||||||
|
--no-log-buffering
|
||||||
|
Flush log records to the file immediately
|
||||||
|
|
||||||
|
--wait-dbg Wait until a debugger is attached to.
|
||||||
|
The flag is valid for debug builds only
|
||||||
|
|
||||||
ENVIRONMENTAL VARIABLES:
|
ENVIRONMENTAL VARIABLES:
|
||||||
RA_LOG Set log filter in env_logger format
|
RA_LOG Set log filter in env_logger format
|
||||||
RA_PROFILE Enable hierarchical profiler
|
RA_PROFILE Enable hierarchical profiler
|
||||||
|
RA_WAIT_DBG If set acts like a --wait-dbg flag
|
||||||
|
|
||||||
COMMANDS:
|
COMMANDS:
|
||||||
|
|
||||||
@ -114,6 +122,8 @@ impl Args {
|
|||||||
verbosity: Verbosity::Normal,
|
verbosity: Verbosity::Normal,
|
||||||
log_file: None,
|
log_file: None,
|
||||||
command: Command::Version,
|
command: Command::Version,
|
||||||
|
no_buffering: false,
|
||||||
|
wait_dbg: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,21 +140,41 @@ impl Args {
|
|||||||
(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 log_file = matches.opt_value_from_str("--log-file")?;
|
||||||
|
let no_buffering = matches.contains("--no-log-buffering");
|
||||||
|
let wait_dbg = matches.contains("--wait-dbg");
|
||||||
|
|
||||||
if matches.contains(["-h", "--help"]) {
|
if matches.contains(["-h", "--help"]) {
|
||||||
eprintln!("{}", HELP);
|
eprintln!("{}", HELP);
|
||||||
return Ok(Args { verbosity, log_file: None, command: Command::Help });
|
return Ok(Args {
|
||||||
|
verbosity,
|
||||||
|
log_file: None,
|
||||||
|
command: Command::Help,
|
||||||
|
no_buffering,
|
||||||
|
wait_dbg,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if matches.contains("--print-config-schema") {
|
if matches.contains("--print-config-schema") {
|
||||||
return Ok(Args { verbosity, log_file, command: Command::PrintConfigSchema });
|
return Ok(Args {
|
||||||
|
verbosity,
|
||||||
|
log_file,
|
||||||
|
command: Command::PrintConfigSchema,
|
||||||
|
no_buffering,
|
||||||
|
wait_dbg,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let subcommand = match matches.subcommand()? {
|
let subcommand = match matches.subcommand()? {
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
None => {
|
None => {
|
||||||
finish_args(matches)?;
|
finish_args(matches)?;
|
||||||
return Ok(Args { verbosity, log_file, command: Command::RunServer });
|
return Ok(Args {
|
||||||
|
verbosity,
|
||||||
|
log_file,
|
||||||
|
command: Command::RunServer,
|
||||||
|
no_buffering,
|
||||||
|
wait_dbg,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let command = match subcommand.as_str() {
|
let command = match subcommand.as_str() {
|
||||||
@ -219,11 +249,17 @@ impl Args {
|
|||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
eprintln!("{}", HELP);
|
eprintln!("{}", HELP);
|
||||||
return Ok(Args { verbosity, log_file: None, command: Command::Help });
|
return Ok(Args {
|
||||||
|
verbosity,
|
||||||
|
log_file: None,
|
||||||
|
command: Command::Help,
|
||||||
|
no_buffering,
|
||||||
|
wait_dbg,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
finish_args(matches)?;
|
finish_args(matches)?;
|
||||||
Ok(Args { verbosity, log_file, command })
|
Ok(Args { verbosity, log_file, command, no_buffering, wait_dbg })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fs::File,
|
fs::File,
|
||||||
io::{BufWriter, Write},
|
io::{self, BufWriter, Write},
|
||||||
};
|
};
|
||||||
|
|
||||||
use env_logger::filter::{Builder, Filter};
|
use env_logger::filter::{Builder, Filter};
|
||||||
@ -14,10 +14,11 @@ use parking_lot::Mutex;
|
|||||||
pub(crate) struct Logger {
|
pub(crate) struct Logger {
|
||||||
filter: Filter,
|
filter: Filter,
|
||||||
file: Option<Mutex<BufWriter<File>>>,
|
file: Option<Mutex<BufWriter<File>>>,
|
||||||
|
no_buffering: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Logger {
|
impl Logger {
|
||||||
pub(crate) fn new(log_file: Option<File>, filter: Option<&str>) -> Logger {
|
pub(crate) fn new(log_file: Option<File>, no_buffering: bool, filter: Option<&str>) -> Logger {
|
||||||
let filter = {
|
let filter = {
|
||||||
let mut builder = Builder::new();
|
let mut builder = Builder::new();
|
||||||
if let Some(filter) = filter {
|
if let Some(filter) = filter {
|
||||||
@ -28,7 +29,7 @@ impl Logger {
|
|||||||
|
|
||||||
let file = log_file.map(|it| Mutex::new(BufWriter::new(it)));
|
let file = log_file.map(|it| Mutex::new(BufWriter::new(it)));
|
||||||
|
|
||||||
Logger { filter, file }
|
Logger { filter, file, no_buffering }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn install(self) {
|
pub(crate) fn install(self) {
|
||||||
@ -46,7 +47,8 @@ impl Log for Logger {
|
|||||||
if !self.filter.matches(record) {
|
if !self.filter.matches(record) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
match &self.file {
|
|
||||||
|
let should_flush = match &self.file {
|
||||||
Some(w) => {
|
Some(w) => {
|
||||||
let _ = writeln!(
|
let _ = writeln!(
|
||||||
w.lock(),
|
w.lock(),
|
||||||
@ -55,19 +57,32 @@ impl Log for Logger {
|
|||||||
record.module_path().unwrap_or_default(),
|
record.module_path().unwrap_or_default(),
|
||||||
record.args(),
|
record.args(),
|
||||||
);
|
);
|
||||||
|
self.no_buffering
|
||||||
}
|
}
|
||||||
None => eprintln!(
|
None => {
|
||||||
|
eprintln!(
|
||||||
"[{} {}] {}",
|
"[{} {}] {}",
|
||||||
record.level(),
|
record.level(),
|
||||||
record.module_path().unwrap_or_default(),
|
record.module_path().unwrap_or_default(),
|
||||||
record.args(),
|
record.args(),
|
||||||
),
|
);
|
||||||
|
true // flush stderr unconditionally
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if should_flush {
|
||||||
|
self.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush(&self) {
|
fn flush(&self) {
|
||||||
if let Some(w) = &self.file {
|
match &self.file {
|
||||||
|
Some(w) => {
|
||||||
let _ = w.lock().flush();
|
let _ = w.lock().flush();
|
||||||
}
|
}
|
||||||
|
None => {
|
||||||
|
let _ = io::stderr().flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
if let Err(err) = try_main() {
|
if let Err(err) = try_main() {
|
||||||
|
log::error!("Unexpected error: {}", err);
|
||||||
eprintln!("{}", err);
|
eprintln!("{}", err);
|
||||||
process::exit(101);
|
process::exit(101);
|
||||||
}
|
}
|
||||||
@ -28,7 +29,17 @@ fn main() {
|
|||||||
|
|
||||||
fn try_main() -> Result<()> {
|
fn try_main() -> Result<()> {
|
||||||
let args = args::Args::parse()?;
|
let args = args::Args::parse()?;
|
||||||
setup_logging(args.log_file)?;
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
if args.wait_dbg || env::var("RA_WAIT_DBG").is_ok() {
|
||||||
|
#[allow(unused_mut)]
|
||||||
|
let mut d = 4;
|
||||||
|
while d == 4 {
|
||||||
|
d = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setup_logging(args.log_file, args.no_buffering)?;
|
||||||
match args.command {
|
match args.command {
|
||||||
args::Command::RunServer => run_server()?,
|
args::Command::RunServer => run_server()?,
|
||||||
args::Command::PrintConfigSchema => {
|
args::Command::PrintConfigSchema => {
|
||||||
@ -56,7 +67,7 @@ fn try_main() -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_logging(log_file: Option<PathBuf>) -> Result<()> {
|
fn setup_logging(log_file: Option<PathBuf>, no_buffering: bool) -> Result<()> {
|
||||||
env::set_var("RUST_BACKTRACE", "short");
|
env::set_var("RUST_BACKTRACE", "short");
|
||||||
|
|
||||||
let log_file = match log_file {
|
let log_file = match log_file {
|
||||||
@ -69,7 +80,7 @@ fn setup_logging(log_file: Option<PathBuf>) -> Result<()> {
|
|||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
let filter = env::var("RA_LOG").ok();
|
let filter = env::var("RA_LOG").ok();
|
||||||
logger::Logger::new(log_file, filter.as_deref()).install();
|
logger::Logger::new(log_file, no_buffering, filter.as_deref()).install();
|
||||||
|
|
||||||
tracing_setup::setup_tracing()?;
|
tracing_setup::setup_tracing()?;
|
||||||
|
|
||||||
|
@ -57,6 +57,14 @@ To apply changes to an already running debug process, press <kbd>Ctrl+Shift+P</k
|
|||||||
|
|
||||||
- Go back to the `[Extension Development Host]` instance and hover over a Rust variable and your breakpoint should hit.
|
- Go back to the `[Extension Development Host]` instance and hover over a Rust variable and your breakpoint should hit.
|
||||||
|
|
||||||
|
If you need to debug the server from the very beginning, including its initialization code, you can use the `--wait-dbg` command line argument or `RA_WAIT_DBG` environment variable. The server will spin at the beginning of the `try_main` function (see `crates\rust-analyzer\src\bin\main.rs`)
|
||||||
|
```rust
|
||||||
|
let mut d = 4;
|
||||||
|
while d == 4 { // set a breakpoint here and change the value
|
||||||
|
d = 4;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Demo
|
## Demo
|
||||||
|
|
||||||
- [Debugging TypeScript VScode extension](https://www.youtube.com/watch?v=T-hvpK6s4wM).
|
- [Debugging TypeScript VScode extension](https://www.youtube.com/watch?v=T-hvpK6s4wM).
|
||||||
|
Loading…
x
Reference in New Issue
Block a user