implement match_time argument, make some optimizations

This commit is contained in:
yggverse 2025-06-08 19:48:10 +03:00
parent c35e07986f
commit 7d0db3f8ec
5 changed files with 36 additions and 12 deletions

View file

@ -12,4 +12,5 @@ repository = "https://github.com/YGGverse/htcount"
[dependencies] [dependencies]
anyhow = "1.0" anyhow = "1.0"
chrono = "0.4"
clap = { version = "4.5", features = ["derive"] } clap = { version = "4.5", features = ["derive"] }

View file

@ -50,19 +50,22 @@ htcount --source /var/log/nginx/access.log\
[default: nginx] [default: nginx]
--export-json <EXPORT_JSON> --export-json <EXPORT_JSON>
Export results to JSON file (e.g. `/path/to/stats.json`) Export results to JSON file (e.g. `/path/to/stats.json`)
--export-svg <EXPORT_SVG> --export-svg <EXPORT_SVG>
Export results to SVG file (e.g. `/path/to/badge.svg`) Export results to SVG file (e.g. `/path/to/badge.svg`)
* use `{hits}` / `{hosts}` pattern to replace parsed values * use `{hits}` / `{hosts}` pattern to replace parsed values
--template-svg <TEMPLATE_SVG> --template-svg <TEMPLATE_SVG>
Use custom SVG file template with `{hits}` / `{hosts}` placeholders Use custom SVG file template with `{hits}` / `{hosts}` placeholders
[default: default/counter.svg] [default: default/counter.svg]
-m, --match-time <MATCH_TIME>
Filter records match time pattern (e.g. `%d/%b/%Y`)
-c, --capacity <CAPACITY> -c, --capacity <CAPACITY>
Expected memory index capacity Expected memory index capacity
@ -86,7 +89,6 @@ htcount --source /var/log/nginx/access.log\
Print version Print version
``` ```
### systemd ### systemd
``` /etc/systemd/system/htcount.service ``` /etc/systemd/system/htcount.service
@ -101,8 +103,9 @@ Type=simple
ExecStart=/usr/local/bin/htcount --source /var/log/nginx/access.log\ ExecStart=/usr/local/bin/htcount --source /var/log/nginx/access.log\
--export-svg /var/www/htcount/visitors.svg\ --export-svg /var/www/htcount/visitors.svg\
--template-svg /path/to/default/template.svg\ --template-svg /path/to/default/template.svg\
--ignore-host 127.0.0.1 --ignore-host 127.0.0.1\
--ignore-host 127.0.0.2 --ignore-host 127.0.0.2\
--match-time %d/%b/%Y\
--update 3600\ --update 3600\
--debug n --debug n
StandardOutput=null StandardOutput=null
@ -114,6 +117,7 @@ WantedBy=multi-user.target
* make sure `/var/www/htcount` exists * make sure `/var/www/htcount` exists
* replace `/path/to/default/template.svg` with your value * replace `/path/to/default/template.svg` with your value
* use `ignore-host` to skip local host requests * use `ignore-host` to skip local host requests
* `match-time %d/%b/%Y` today records only
* `systemctl daemon-reload` - update configuration * `systemctl daemon-reload` - update configuration
* `systemctl enable` - launch on system startup * `systemctl enable` - launch on system startup

View file

@ -30,6 +30,10 @@ pub struct Argument {
#[arg(long, default_value_t = String::from("default/counter.svg"))] #[arg(long, default_value_t = String::from("default/counter.svg"))]
pub template_svg: String, pub template_svg: String,
/// Filter records match time pattern (e.g. `%d/%b/%Y`)
#[arg(short, long)]
pub match_time: Option<String>,
/// Expected memory index capacity /// Expected memory index capacity
#[arg(short, long, default_value_t = 100)] #[arg(short, long, default_value_t = 100)]
pub capacity: usize, pub capacity: usize,

View file

@ -1,4 +1,4 @@
pub fn info(message: String) { pub fn info(message: &str) {
println!("[{}] [info] {message}", now()) println!("[{}] [info] {message}", now())
} }

View file

@ -15,6 +15,10 @@ fn main() -> anyhow::Result<()> {
argument::Argument::parse() argument::Argument::parse()
}; };
let match_time = argument
.match_time
.map(|ref t| chrono::Local::now().format(t).to_string());
// parse some arguments once // parse some arguments once
let is_debug_i = argument.debug.contains("i"); let is_debug_i = argument.debug.contains("i");
let is_debug_d = argument.debug.contains("d"); let is_debug_d = argument.debug.contains("d");
@ -24,12 +28,12 @@ fn main() -> anyhow::Result<()> {
} }
if is_debug_i { if is_debug_i {
debug::info("Crawler started".into()); debug::info("Crawler started");
} }
loop { loop {
if is_debug_i { if is_debug_i {
debug::info("Index queue begin...".into()); debug::info("Index queue begin...");
} }
let file = File::open(&argument.source)?; let file = File::open(&argument.source)?;
@ -38,7 +42,18 @@ fn main() -> anyhow::Result<()> {
let mut index: HashMap<String, usize> = HashMap::with_capacity(argument.capacity); let mut index: HashMap<String, usize> = HashMap::with_capacity(argument.capacity);
'l: for line in reader.lines() { 'l: for line in reader.lines() {
let host = line? let l = line?;
if let Some(ref t) = match_time {
if !l.contains(t) {
if is_debug_d {
debug::info(&format!("Record time mismatch time filter {t}"))
}
continue;
}
}
let host = l
.split_whitespace() .split_whitespace()
.next() .next()
.map(|s| s.into()) .map(|s| s.into())
@ -47,7 +62,7 @@ fn main() -> anyhow::Result<()> {
for h in &argument.ignore_host { for h in &argument.ignore_host {
if h == &host { if h == &host {
if is_debug_d { if is_debug_d {
debug::info(format!("Host `{h}` ignored by settings")) debug::info(&format!("Host `{h}` ignored by settings"))
} }
continue 'l; continue 'l;
} }
@ -60,7 +75,7 @@ fn main() -> anyhow::Result<()> {
let hits: usize = index.values().sum(); let hits: usize = index.values().sum();
if is_debug_i { if is_debug_i {
debug::info(format!( debug::info(&format!(
"Index queue completed:\n{}\n\thosts: {} / hits: {}, await {} seconds to continue...", "Index queue completed:\n{}\n\thosts: {} / hits: {}, await {} seconds to continue...",
if is_debug_d { if is_debug_d {
let mut b = Vec::with_capacity(hosts); let mut b = Vec::with_capacity(hosts);