Why the env_logger doesn't work in my cli program - rust

In Cargo.toml, I write this:
log = "0.4.0"
env_logger = "0.8.4"
In my test code, I write this:
use log::{info, warn};
fn main() {
env_logger::init();
info!("starting up");
warn!("oops, nothing done");
}
And then I set the env like this:
$ env | grep RUST
RUST_LOG=output_log=info
Every thing was done, and the test code worked well:
$ cargo run --bin output-log
Finished dev [unoptimized + debuginfo] target(s) in 0.04s
Running `target/debug/output-log`
[2021-11-06T04:05:42Z INFO output_log] starting up
[2021-11-06T04:05:42Z WARN output_log] oops, nothing done
But When I did this in my program:
use log::*;
use std::fs::File;
use std::io::{self, BufRead, BufReader, BufWriter, Write};
use std::path::PathBuf;
...
fn main() -> Result<()> {
let stdout = io::stdout();
let mut stdout = BufWriter::new(stdout);
env_logger::init();
info!("This is info");
warn!("This is warn");
error!("This is error");
...
// write something to the stdout and flush
...
Ok(())
}
Then, the env_logger didn't print any message to the terminal as before
Why this happens and what should I do to fix it?

export RUST_LOG=`executable_name`=`log_level`,...
So I should write
export RUST_LOG=grrs=info

Related

Handling piped data stdin with Rust

I'm having trouble with stdin in Rust. I'm trying to process stdin comming from a pipe on a linux terminal, something like grep for example.
echo "lorem ipsum" | grep <text>
Im using this in rust:
fn load_stdin() -> Result<String> {
let mut buffer = String::new();
let stdin = stdin();
stdin.read_line(&mut buffer)?;
return Ok(buffer);
}
But the problem is that if I don't bring in any piped data I get prompted to write, I would instead like to return Err.
So basically, if I do something like:
ls | cargo run
user#machine: ~ $
All is good. But if I do not pipe any stdin:
cargo run
The program halts and waits for user input.
You can use the atty crate to test whether your standard input is redirected:
use std::io;
use atty::Stream;
fn load_stdin() -> io::Result<String> {
if atty::is(Stream::Stdin) {
return Err(io::Error::new(io::ErrorKind::Other, "stdin not redirected"));
}
let mut buffer = String::new();
io::stdin().read_line(&mut buffer)?;
return Ok(buffer);
}
fn main() -> io::Result<()> {
println!("line: {}", load_stdin()?);
Ok(())
}
This results in the desired behavior:
$ echo "lorem ipsum" | cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.02s
Running `target/debug/playground`
line: lorem ipsum
$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.02s
Running `target/debug/playground`
Error: Custom { kind: Other, error: "stdin not redirected" }

Read lines from std input using Rust

Hi I want to be able to read a file which contains json lines into a rust app like this
$ cargo run < users.json
and then read those lines as an iterator. As of now I have this code but i don't want the file hard coded but piped into the process as in the line above.
use std::fs::File;
use std::io::{self, prelude::*, BufReader};
fn main() -> io::Result<()> {
let file = File::open("users.json")?;
let reader = BufReader::new(file);
for line in reader.lines() {
println!("{}", line);
}
Ok(())
}
I just solved it this makes the trick
use std::io::{self, BufRead};
fn main() {
let stdin = io::stdin();
for line in stdin.lock().lines() {
println!("{}", line.unwrap());
}
}
cargo help run reveals:
NAME
cargo-run - Run the current package
SYNOPSIS
cargo run [options] [-- args]
DESCRIPTION
Run a binary or example of the local package.
All the arguments following the two dashes (--) are passed to the binary to run. If you're passing arguments to both Cargo and the binary, the ones after -- go to the binary, the ones before go to Cargo.
So to pass arguments the syntax would be:
cargo run -- foo bar baz
You can then access the values like this:
let args: Vec<String> = env::args().collect();
A complete minimal example would be:
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
dbg!(&args);
}
Running cargo run -- users.json would result in:
$ cargo run -- users.json
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/sandbox users.json`
[src/main.rs:5] &args = [
"target/debug/sandbox",
"users.json",
]
use std::io::{self, BufRead};
fn main() {
let stdin = io::stdin();
for line in stdin.lock().lines() {
println!("{}", line.unwrap());
}
}

thread 'main' panicked at 'Box<Any>'

I am trying to learn Rust. I am following a book online which implements the unix program cat. Right now I trying to read the content of files passed as an argument like that cargo run file1.txt file2.txt but the program panics:
D:\rust\cat> cargo run .\src\test.txt
Compiling cat v0.1.0 (D:\rust\cat)
Finished dev [unoptimized + debuginfo] target(s) in 0.62s
Running `target\debug\cat.exe .\src\test.txt`
thread 'main' panicked at 'Box<Any>', src\main.rs:12:28
this is my program:
use std::env;
use std::fs::File;
use std::io;
use std::io::prelude::*;
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() > 1 {
match read_file(&args) {
Ok(content) => println!("{}", content),
Err(reason) => panic!(reason),
}
}
}
fn read_file(filenames: &Vec<String>) -> Result<String, io::Error> {
let mut content = String::new();
for filename in filenames {
let mut file = File::open(filename)?;
file.read_to_string(&mut content)?;
}
Ok(content)
}
Can anyone explain what I am missing here?
The first element of the Args iterator returned by std::env::args is tipically the path of executable (see the docs
for more details).
The error arises because you do not skip the first arg: the program binary is not a sequence of valid UTF-8 bytes.
The apparently non sense error thread 'main' panicked at 'Box<Any>' is because panic! is not used with the same arguments of
the format! syntax.
use std::env;
use std::fs::File;
use std::io;
use std::io::prelude::*;
fn main() {
for filename in env::args().skip(1) {
match read_file(filename) {
Ok(content) => println!("{}", content),
Err(reason) => panic!("{}", reason),
}
}
}
fn read_file(filename: String) -> Result<String, io::Error> {
let mut content = String::new();
let mut file = File::open(filename)?;
file.read_to_string(&mut content)?;
Ok(content)
}

Repeatedly read subprocess output with tokio-process

For a GUI tool I'm writing in Rust I'd like to kick off long-lived subprocesses and then repeatedly poll them for output. I don't want to block indefinitely waiting for output from any given one, so I'm using tokio and tokio-process to run the process and timeout the output reading. I need to store the subprocess in a struct as well.
I'm running into problems because it seems I need to consume the subprocess's output stream in order to read from it, so after a single read I'm unable to access it again.
I've included a simplified reproduction of my problem below. It prints actual output for the first invocation of print_output, but then prints No process_output_fut for the second invocation since I had to take the output future out of the Plugin struct and consume it in order to read from it.
Any suggestions for how to refactor this code to avoid consuming the process output future with each attempt to fetch the output?
extern crate tokio;
use std::process::{Command, Stdio};
use std::time::Duration;
use tokio::prelude::*;
use tokio::codec::{FramedRead, LinesCodec};
use tokio::prelude::stream::StreamFuture;
use tokio::timer::Timeout;
use tokio_process::{Child, ChildStdout, CommandExt};
pub struct Plugin {
process: Option<Child>,
process_output_fut: Option<Timeout<StreamFuture<FramedRead<ChildStdout, LinesCodec>>>>,
tokio_runtime: tokio::runtime::Runtime,
}
impl Plugin {
pub fn new() -> Self {
return Plugin {
process: None,
process_output_fut: None,
tokio_runtime: tokio::runtime::Runtime::new().expect(
"Could not create tokio runtime"
),
}
}
pub fn spawn(&mut self, arg: String) {
let mut process = Command::new("/bin/ls")
.arg(arg)
.stdout(Stdio::piped())
.spawn_async()
.expect("Failed to spawn command");
if let Some(plugin_output) = process.stdout().take() {
let lines_codec = LinesCodec::new();
self.process_output_fut = Some(FramedRead::new(plugin_output, lines_codec)
.into_future()
.timeout(Duration::from_millis(3000)));
}
self.process = Some(process);
}
pub fn print_output(&mut self) {
if let Some(process_output_fut) = self.process_output_fut.take() {
let result = self.tokio_runtime.block_on(process_output_fut).unwrap();
if let Some(output) = result.0 {
println!("{:?}", output);
};
} else {
println!("No process_output_fut");
}
}
}
fn main() {
let mut p = Plugin::new();
p.spawn("/");
p.print_output();
p.print_output();
}

Does Rust have bindings for tee(2)?

Does Rust have bindings for tee(2) in std::io or otherwise? And if there are no bindings, how would I get that functionality in a Rust program?
The tee method existed in the standard library, but it was deprecated in 1.6.
You can use the tee crate to get the same functionality:
extern crate tee;
use tee::TeeReader;
use std::io::Read;
fn main() {
let mut reader = "It's over 9000!".as_bytes();
let mut teeout = Vec::new();
let mut stdout = Vec::new();
{
let mut tee = TeeReader::new(&mut reader, &mut teeout);
let _ = tee.read_to_end(&mut stdout);
}
println!("tee out -> {:?}", teeout);
println!("std out -> {:?}", stdout);
}
(example from the repo)

Resources