Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
baf0f9fc2e
|
|||
|
b2290db274
|
|||
|
c5c0ab2dad
|
|||
|
97a0b851b2
|
|||
|
68c6732c4b
|
|||
| c9f4ac9fe5 | |||
|
e5962ca543
|
|||
|
1151dc606f
|
44
Cargo.lock
generated
44
Cargo.lock
generated
@@ -49,6 +49,15 @@ dependencies = [
|
|||||||
"vec_map",
|
"vec_map",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "daemonize"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ab8bfdaacb3c887a54d41bdf48d3af8873b3f5566469f8ba21b92057509f116e"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dirs-next"
|
name = "dirs-next"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
@@ -98,15 +107,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.147"
|
version = "0.2.149"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
|
checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.66"
|
version = "1.0.69"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
|
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@@ -142,17 +151,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rotate-puts"
|
name = "rotate-puts"
|
||||||
version = "0.2.0"
|
version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
|
"daemonize",
|
||||||
"rust_util",
|
"rust_util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust_util"
|
name = "rust_util"
|
||||||
version = "0.6.41"
|
version = "0.6.45"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "df24005feacce81f4ae340464b39c380f7e01e7225bfdef62d40cb44cb1c11d7"
|
checksum = "45c3036388284b69995eb2864a943b0de55de76ad5b56fc2d3c60fecd7b9ad82"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -174,9 +184,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.29"
|
version = "2.0.38"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a"
|
checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -215,18 +225,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.47"
|
version = "1.0.50"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f"
|
checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.47"
|
version = "1.0.50"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b"
|
checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -235,15 +245,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.11"
|
version = "1.0.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-width"
|
name = "unicode-width"
|
||||||
version = "0.1.10"
|
version = "0.1.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vec_map"
|
name = "vec_map"
|
||||||
|
|||||||
11
Cargo.toml
11
Cargo.toml
@@ -1,12 +1,17 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rotate-puts"
|
name = "rotate-puts"
|
||||||
version = "0.2.0"
|
version = "0.3.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
authors = ["Hatter Jiang"]
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
repository = "https://git.hatter.ink/hatter/rotate-puts"
|
||||||
|
description = "Rotate outputs to files"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
keywords = ["log", "rotate"]
|
||||||
|
categories = ["log"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = "2.33"
|
||||||
|
daemonize = "0.5.0"
|
||||||
rust_util = "0.6.41"
|
rust_util = "0.6.41"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
|
|||||||
20
README.md
20
README.md
@@ -3,13 +3,33 @@
|
|||||||
Rotate outputs from std in or assigned file
|
Rotate outputs from std in or assigned file
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
command | rotate-puts
|
command | rotate-puts
|
||||||
```
|
```
|
||||||
|
|
||||||
Use `mkfifo`:
|
Use `mkfifo`:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
mkfido test-fifo
|
mkfido test-fifo
|
||||||
rotate-puts --file test-fifo
|
rotate-puts --file test-fifo
|
||||||
command > test-fifo
|
command > test-fifo
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Run as daemon:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
rotate-put --file test-fifo --daemon --iden test [--continue-read]
|
||||||
|
```
|
||||||
|
|
||||||
|
For nginx:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
mkfifo access.log
|
||||||
|
mkfifo error.log
|
||||||
|
```
|
||||||
|
|
||||||
|
```shell
|
||||||
|
./rotate-puts --daemon --file access.log --prefix nginx_access --continue-read --ident nginx_access
|
||||||
|
./rotate-puts --daemon --file error.log --prefix nginx_error --continue-read --ident nginx_error
|
||||||
|
```
|
||||||
5
justfile
5
justfile
@@ -1,7 +1,6 @@
|
|||||||
_:
|
_:
|
||||||
@just --list
|
@just --list
|
||||||
|
|
||||||
build-x64:
|
build-linux-x64-musl:
|
||||||
cross build --target x86_64-unknown-linux-musl --release
|
cargo zigbuild --release --target x86_64-unknown-linux-musl
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
131
src/main.rs
131
src/main.rs
@@ -1,10 +1,13 @@
|
|||||||
|
use std::fs;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{BufWriter, Read, Write};
|
use std::io::{BufWriter, Read, Write};
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
use std::sync::mpsc::Sender;
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
|
|
||||||
use clap::{App, AppSettings, Arg};
|
use clap::{App, AppSettings, Arg};
|
||||||
use rust_util::{iff, information, util_size};
|
use daemonize::Daemonize;
|
||||||
|
use rust_util::{failure_and_exit, iff, information, util_size};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let app = App::new(env!("CARGO_PKG_NAME"))
|
let app = App::new(env!("CARGO_PKG_NAME"))
|
||||||
@@ -12,29 +15,62 @@ fn main() {
|
|||||||
.about(env!("CARGO_PKG_DESCRIPTION"))
|
.about(env!("CARGO_PKG_DESCRIPTION"))
|
||||||
.long_about("Rotate standard in to log files")
|
.long_about("Rotate standard in to log files")
|
||||||
.arg(Arg::with_name("prefix")
|
.arg(Arg::with_name("prefix")
|
||||||
.long("prefix").takes_value(true).default_value("temp").help("Log file prefix"))
|
.long("prefix").short("P").takes_value(true).default_value("temp").help("Log file prefix"))
|
||||||
.arg(Arg::with_name("suffix")
|
.arg(Arg::with_name("suffix")
|
||||||
.long("suffix").takes_value(true).default_value("log").help("Log file suffix"))
|
.long("suffix").short("S").takes_value(true).default_value("log").help("Log file suffix"))
|
||||||
.arg(Arg::with_name("file-size")
|
.arg(Arg::with_name("file-size")
|
||||||
.long("file-size").takes_value(true).default_value("10m").help("Single log file size"))
|
.long("file-size").short("s").takes_value(true).default_value("10m").help("Single log file size"))
|
||||||
.arg(Arg::with_name("file-count")
|
.arg(Arg::with_name("file-count")
|
||||||
.long("file-count").takes_value(true).default_value("10").help("Keep file count (from 0 to 1000)"))
|
.long("file-count").short("c").takes_value(true).default_value("10").help("Keep file count (from 0 to 1000)"))
|
||||||
.arg(Arg::with_name("file")
|
.arg(Arg::with_name("file")
|
||||||
.long("file").takes_value(true).required(false).help("Read from file, default stdin"))
|
.long("file").short("F").takes_value(true).required(false).help("Read from file, default stdin"))
|
||||||
|
.arg(Arg::with_name("continue-read")
|
||||||
|
.long("continue-read").short("r").help("Continue read"))
|
||||||
|
.arg(Arg::with_name("ident")
|
||||||
|
.long("ident").short("i").takes_value(true).help("Identity when run in daemon mode"))
|
||||||
|
.arg(Arg::with_name("daemon")
|
||||||
|
.long("daemon").short("d").help("Run in daemon mode"))
|
||||||
.setting(AppSettings::ColoredHelp);
|
.setting(AppSettings::ColoredHelp);
|
||||||
|
|
||||||
let arg_matchers = app.get_matches();
|
let arg_matchers = app.get_matches();
|
||||||
let prefix = arg_matchers.value_of("prefix").unwrap().to_string();
|
let prefix = arg_matchers.value_of("prefix").unwrap().to_string();
|
||||||
let suffix = arg_matchers.value_of("suffix").unwrap().to_string();
|
let suffix = arg_matchers.value_of("suffix").unwrap().to_string();
|
||||||
let file_size = arg_matchers.value_of("file-size").unwrap();
|
let file_size = arg_matchers.value_of("file-size").unwrap();
|
||||||
let file_size = util_size::parse_size(file_size).unwrap_or_else(|_| 10 * 1024 * 1028) as usize;
|
let file_size = util_size::parse_size(file_size).unwrap_or(10 * 1024 * 1028) as usize;
|
||||||
let file_count = arg_matchers.value_of("file-count").unwrap();
|
let file_count = arg_matchers.value_of("file-count").unwrap();
|
||||||
let file_count = file_count.parse().unwrap_or_else(|_| 10);
|
let file_count = file_count.parse().unwrap_or(10);
|
||||||
let file_count = if file_count < 0 {
|
let file_count: i32 = if file_count < 0 { 0 } else if file_count > 1000 { 1000 } else { file_count };
|
||||||
0
|
let continue_read = arg_matchers.is_present("continue-read");
|
||||||
} else if file_count > 1000 {
|
|
||||||
1000
|
let daemon_mode = arg_matchers.is_present("daemon");
|
||||||
} else { file_count as i32 };
|
if daemon_mode {
|
||||||
|
let ident = match arg_matchers.value_of("ident") {
|
||||||
|
Some(ident) => ident,
|
||||||
|
None => failure_and_exit!("--ident is required when running in daemon mode"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let stdout = File::create(format!("/tmp/rotate-puts-daemon-{}-out.log", ident))
|
||||||
|
.expect("Create daemon out file failed");
|
||||||
|
let stderr = File::create(format!("/tmp/rotate-puts-daemon-{}-err.log", ident))
|
||||||
|
.expect("Create daemon err file failed");
|
||||||
|
let current_dir = std::env::current_dir().expect("Get current dir failed");
|
||||||
|
let daemonize = Daemonize::new()
|
||||||
|
.pid_file(format!("/tmp/rotate-puts-daemon-{}.pid", ident))
|
||||||
|
// .chown_pid_file(true) // is optional, see `Daemonize` documentation
|
||||||
|
.working_directory(current_dir) // for default behaviour.
|
||||||
|
// .user("nobody")
|
||||||
|
// .group("daemon") // Group name
|
||||||
|
// .group(2) // or group id.
|
||||||
|
.umask(0o777) // Set umask, `0o027` by default.
|
||||||
|
.stdout(stdout) // Redirect stdout to `/tmp/daemon.out`.
|
||||||
|
.stderr(stderr) // Redirect stderr to `/tmp/daemon.err`.
|
||||||
|
.privileged_action(|| "Executed before drop privileges");
|
||||||
|
|
||||||
|
match daemonize.start() {
|
||||||
|
Ok(child) => information!("Success, daemonized: {}", child),
|
||||||
|
Err(e) => failure_and_exit!("Error, {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
information!("Prefix: {}, suffix: {}, file size: {}, file count: {}",
|
information!("Prefix: {}, suffix: {}, file size: {}, file count: {}",
|
||||||
prefix, suffix, util_size::get_display_size(file_size as i64), file_count
|
prefix, suffix, util_size::get_display_size(file_size as i64), file_count
|
||||||
@@ -46,7 +82,7 @@ fn main() {
|
|||||||
|
|
||||||
let file_name = make_new_file_name(&prefix, &suffix, file_count, &mut file_index);
|
let file_name = make_new_file_name(&prefix, &suffix, file_count, &mut file_index);
|
||||||
let mut out_file = BufWriter::new(File::create(&file_name)
|
let mut out_file = BufWriter::new(File::create(&file_name)
|
||||||
.expect(&format!("Create file failed: {}", file_name)));
|
.unwrap_or_else(|_| panic!("Create file failed: {}", file_name)));
|
||||||
|
|
||||||
let mut last_write_time = SystemTime::now();
|
let mut last_write_time = SystemTime::now();
|
||||||
let mut write_buffer = Vec::with_capacity(1024 * 8);
|
let mut write_buffer = Vec::with_capacity(1024 * 8);
|
||||||
@@ -95,8 +131,10 @@ fn main() {
|
|||||||
if written_len >= file_size {
|
if written_len >= file_size {
|
||||||
written_len = 0;
|
written_len = 0;
|
||||||
let file_name = make_new_file_name(&prefix, &suffix, file_count, &mut file_index);
|
let file_name = make_new_file_name(&prefix, &suffix, file_count, &mut file_index);
|
||||||
out_file = BufWriter::new(File::create(&file_name)
|
out_file = BufWriter::new(
|
||||||
.expect(&format!("Create file failed: {}", file_name)));
|
File::create(&file_name)
|
||||||
|
.unwrap_or_else(|_| panic!("Create file failed: {}", file_name))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -105,28 +143,49 @@ fn main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let file_opt = arg_matchers.value_of("file");
|
let file_opt = arg_matchers.value_of("file");
|
||||||
let mut read_in: Box<dyn Read> = if let Some(file) = file_opt {
|
loop_read_and_send(&file_opt, sender, continue_read);
|
||||||
|
|
||||||
|
information!("End rotate-puts")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn loop_read_and_send(file_opt: &Option<&str>, sender: Sender<Vec<u8>>, continue_read: bool) {
|
||||||
|
let mut continue_read_count = 0;
|
||||||
|
information!("Continue read: {}", continue_read);
|
||||||
|
'read_open_loop: while continue_read || (continue_read_count == 0) {
|
||||||
|
if continue_read_count > 0 { information!("Continue read at #{}", continue_read_count); }
|
||||||
|
continue_read_count += 1;
|
||||||
|
|
||||||
|
let mut read_in = get_read_in(file_opt);
|
||||||
|
|
||||||
|
let mut buff = [0_u8; 128];
|
||||||
|
loop {
|
||||||
|
match read_in.read(&mut buff) {
|
||||||
|
Ok(len) if len == 0 => {
|
||||||
|
if continue_read {
|
||||||
|
continue 'read_open_loop; // continue and reopen file or stdin
|
||||||
|
} else {
|
||||||
|
sender.send(Vec::new()).ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(len) => {
|
||||||
|
let mut vec = Vec::with_capacity(len);
|
||||||
|
vec.extend_from_slice(&buff[0..len]);
|
||||||
|
if let Err(e) = sender.send(vec) {
|
||||||
|
eprintln!("[ERROR] Send error: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => eprintln!("[ERROR] Send error: {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_read_in(file_opt: &Option<&str>) -> Box<dyn Read> {
|
||||||
|
if let Some(file) = file_opt {
|
||||||
let file_read = File::open(file).expect("Open file failed!");
|
let file_read = File::open(file).expect("Open file failed!");
|
||||||
Box::new(file_read)
|
Box::new(file_read)
|
||||||
} else {
|
} else {
|
||||||
Box::new(std::io::stdin())
|
Box::new(std::io::stdin())
|
||||||
};
|
|
||||||
|
|
||||||
let mut buff = [0_u8; 128];
|
|
||||||
loop {
|
|
||||||
match read_in.read(&mut buff) {
|
|
||||||
Ok(len) if len == 0 => {
|
|
||||||
sender.send(Vec::new()).ok();
|
|
||||||
}
|
|
||||||
Ok(len) => {
|
|
||||||
let mut vec = Vec::with_capacity(len);
|
|
||||||
vec.extend_from_slice(&buff[0..len]);
|
|
||||||
if let Err(e) = sender.send(vec) {
|
|
||||||
eprintln!("[ERROR] Send error: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => eprintln!("[ERROR] Send error: {}", e),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,9 +194,9 @@ fn make_new_file_name(prefix: &str, suffix: &str, file_count: i32, index: &mut i
|
|||||||
*index = i + 1;
|
*index = i + 1;
|
||||||
|
|
||||||
let pending_rm = generate_file_name(prefix, suffix, i - file_count);
|
let pending_rm = generate_file_name(prefix, suffix, i - file_count);
|
||||||
if let Ok(_) = std::fs::metadata(&pending_rm) {
|
if fs::metadata(&pending_rm).is_ok() {
|
||||||
println!("[INFO] Remove log file: {}", &pending_rm);
|
println!("[INFO] Remove log file: {}", &pending_rm);
|
||||||
std::fs::remove_file(&pending_rm).ok();
|
fs::remove_file(&pending_rm).ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
let file_name = generate_file_name(prefix, suffix, i);
|
let file_name = generate_file_name(prefix, suffix, i);
|
||||||
|
|||||||
Reference in New Issue
Block a user