logging and other stuff

This commit is contained in:
2024-07-14 15:40:59 +02:00
parent 7b419ab8b7
commit 2d31f61af9
6 changed files with 410 additions and 214 deletions

View File

@ -2,10 +2,13 @@ mod cloudflare;
mod config;
mod public_ip;
pub mod utils;
mod logging;
pub use cloudflare::CloudflareClient;
pub use config::{get_config_path, read_config, Config};
pub use public_ip::get_current_public_ipv4;
pub use logging::init_logger;
pub const PROGRAM_NAME: &'static str = "dynip-cloudflare";
pub const MAX_ERORS_IN_ROW_DEFAULT: usize = 10;
const LOG_DIR: &str = "logs";

87
src/logging.rs Normal file
View File

@ -0,0 +1,87 @@
use chrono::Local;
use colored::*;
use log::LevelFilter;
use log4rs::append::console::{ConsoleAppender, Target};
use log4rs::append::rolling_file::policy::compound::roll::fixed_window::FixedWindowRoller;
use log4rs::append::rolling_file::policy::compound::trigger::size::SizeTrigger;
use log4rs::append::rolling_file::policy::compound::CompoundPolicy;
use log4rs::config::{Appender, Config, Root};
use log4rs::encode::pattern::PatternEncoder;
use log4rs::encode::{self, Encode};
use log4rs::filter::threshold::ThresholdFilter;
use crate::{LOG_DIR, PROGRAM_NAME};
#[derive(Debug)]
struct ColoredPatternEncoder;
impl ColoredPatternEncoder {
pub fn new() -> Self {
ColoredPatternEncoder {}
}
}
impl Encode for ColoredPatternEncoder {
fn encode(&self, w: &mut dyn encode::Write, record: &log::Record) -> anyhow::Result<()> {
let now = Local::now().format("%Y-%m-%d %H:%M:%S").to_string();
let level = match record.level() {
log::Level::Error => record.level().to_string().red().bold(),
log::Level::Warn => record.level().to_string().yellow().bold(),
log::Level::Info => record.level().to_string().green().bold(),
log::Level::Debug => record.level().to_string().blue().bold(),
log::Level::Trace => record.level().to_string().purple().bold(),
};
let message = record.args().to_string().cyan();
Ok(writeln!(w, "{} [{}] {}", now.bold(), level, message)?)
}
}
pub fn init_logger() {
let today = Local::now().format("%Y-%m-%d").to_string();
if std::fs::metadata(LOG_DIR).is_err() {
std::fs::create_dir(LOG_DIR).expect("Failed to create log dir");
}
let log_file_path = format!("{}/{}-{}.log", LOG_DIR, PROGRAM_NAME, today);
let log_file_pattern = format!("{}/{}-{}-{{}}.log", LOG_DIR, PROGRAM_NAME, today);
let level = log::LevelFilter::Info;
let stderr = ConsoleAppender::builder()
.encoder(Box::new(ColoredPatternEncoder::new()))
.target(Target::Stderr)
.build();
const TRIGGER_FILE_SIZE: u64 = 5 * 1024; // 5KB as max log file size to roll
let trigger = SizeTrigger::new(TRIGGER_FILE_SIZE);
let roller = FixedWindowRoller::builder()
.base(0)
.build(&log_file_pattern, 3)
.unwrap();
let policy = CompoundPolicy::new(Box::new(trigger), Box::new(roller));
let logfile = log4rs::append::rolling_file::RollingFileAppender::builder()
.encoder(Box::new(PatternEncoder::new(
"{d(%Y-%m-%d %H:%M:%S)} [{l}] {m}\n",
)))
.build(log_file_path, Box::new(policy))
.unwrap();
let config = Config::builder()
.appender(Appender::builder().build("logfile", Box::new(logfile)))
.appender(
Appender::builder()
.filter(Box::new(ThresholdFilter::new(level)))
.build("stderr", Box::new(stderr)),
)
.build(
Root::builder()
.appender("logfile")
.appender("stderr")
.build(LevelFilter::Trace),
)
.unwrap();
log4rs::init_config(config).expect("Failed to initialize logger");
}

View File

@ -1,3 +1,8 @@
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
use futures::stream::StreamExt;
use log::{debug, error, info, log_enabled, Level};
use netlink_packet_core::NetlinkPayload;
@ -6,6 +11,8 @@ use netlink_sys::{AsyncSocket, SocketAddr};
use rtnetlink::new_connection;
use dynip_cloudflare::{utils, CloudflareClient, MAX_ERORS_IN_ROW_DEFAULT};
use scopeguard::defer;
use tokio::{signal, sync::Notify};
const RTNLGRP_LINK: u32 = 1;
const RTNLGRP_IPV4_IFADDR: u32 = 5;
@ -23,7 +30,24 @@ const fn nl_mgrp(group: u32) -> u32 {
#[tokio::main]
async fn main() {
utils::init_logger();
dynip_cloudflare::init_logger();
defer! {
log::logger().flush();
}
let should_exit = Arc::new(AtomicBool::new(false));
let notify = Arc::new(Notify::new());
let should_exit_clone = should_exit.clone();
let notify_clone = notify.clone();
tokio::spawn(async move {
signal::ctrl_c()
.await
.expect("Failed to install CTRL+C signal handler");
should_exit_clone.store(true, Ordering::SeqCst);
notify_clone.notify_one();
});
let config = if let Some(aux) = utils::get_config().await {
aux
} else {
@ -55,63 +79,72 @@ async fn main() {
let mut errs_counter: usize = 0;
let errs_max = config.max_errors_in_row.unwrap_or(MAX_ERORS_IN_ROW_DEFAULT);
while let Some((message, _)) = messages.next().await {
match message.payload {
NetlinkPayload::InnerMessage(RtnlMessage::NewAddress(msg)) => {
if log_enabled!(Level::Debug) {
debug!("New IPv4 address message: {:?}", msg);
} else {
info!("New IPv4 address");
while !should_exit.load(Ordering::SeqCst) {
tokio::select! {
_ = notify.notified() => {
break;
}
message = messages.next() => {
if let Some((message, _)) = message {
match message.payload {
NetlinkPayload::InnerMessage(RtnlMessage::NewAddress(msg)) => {
if log_enabled!(Level::Debug) {
debug!("New IPv4 address message: {:?}", msg);
} else {
info!("New IPv4 address");
if let Err(e) = cloudflare.check().await {
errs_counter += 1;
error!(
"Failed to check cloudflare ({}/{}): {:?}",
errs_counter, errs_max, &e
);
if errs_counter >= errs_max {
return;
if let Err(e) = cloudflare.check().await {
errs_counter += 1;
error!(
"Failed to check cloudflare ({}/{}): {:?}",
errs_counter, errs_max, &e
);
if errs_counter >= errs_max {
return;
}
}
}
}
NetlinkPayload::InnerMessage(RtnlMessage::DelAddress(msg)) => {
if log_enabled!(Level::Debug) {
debug!("Deleted IPv4 address message: {:?}", msg);
} else {
info!("Deleted IPv4 address");
}
}
NetlinkPayload::InnerMessage(RtnlMessage::NewLink(link)) => {
if log_enabled!(Level::Debug) {
debug!("New link message (interface connected): {:?}", link);
} else {
info!("New link (interface connected)");
if let Err(e) = cloudflare.check().await {
errs_counter += 1;
error!(
"Failed to check cloudflare ({}/{}): {:?}",
errs_counter, errs_max, &e
);
if errs_counter >= errs_max {
return;
}
}
}
}
NetlinkPayload::InnerMessage(RtnlMessage::DelLink(link)) => {
if log_enabled!(Level::Debug) {
debug!("Deleted link message (interface disconnected): {:?}", link);
} else {
info!("Deleted link (interface disconnected)");
}
}
_ => {
if log_enabled!(Level::Debug) {
debug!("Unhandled message payload: {:?}", message.payload);
}
}
}
}
}
NetlinkPayload::InnerMessage(RtnlMessage::DelAddress(msg)) => {
if log_enabled!(Level::Debug) {
debug!("Deleted IPv4 address message: {:?}", msg);
} else {
info!("Deleted IPv4 address");
}
}
NetlinkPayload::InnerMessage(RtnlMessage::NewLink(link)) => {
if log_enabled!(Level::Debug) {
debug!("New link message (interface connected): {:?}", link);
} else {
info!("New link (interface connected)");
if let Err(e) = cloudflare.check().await {
errs_counter += 1;
error!(
"Failed to check cloudflare ({}/{}): {:?}",
errs_counter, errs_max, &e
);
if errs_counter >= errs_max {
return;
}
}
}
}
NetlinkPayload::InnerMessage(RtnlMessage::DelLink(link)) => {
if log_enabled!(Level::Debug) {
debug!("Deleted link message (interface disconnected): {:?}", link);
} else {
info!("Deleted link (interface disconnected)");
}
}
_ => {
if log_enabled!(Level::Debug) {
debug!("Unhandled message payload: {:?}", message.payload);
}
}
}
}
}

View File

@ -1,23 +1,5 @@
use env_logger::{Builder, Env};
use log::{error, LevelFilter};
use std::io::Write;
use crate::{get_config_path, read_config, Config};
pub fn init_logger() {
Builder::from_env(Env::default().default_filter_or("info"))
.format(|buf, record| {
writeln!(
buf,
"{} [{}] - {}",
chrono::Local::now().format("%Y:%m:%d %H:%M:%S"),
record.level(),
record.args()
)
})
.filter(None, LevelFilter::Info)
.init();
}
use log::error;
pub async fn get_config() -> Option<Config> {
let config_path = match get_config_path().await {