logging and other stuff
This commit is contained in:
		@@ -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
									
								
							
							
						
						
									
										87
									
								
								src/logging.rs
									
									
									
									
									
										Normal 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");
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										139
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										139
									
								
								src/main.rs
									
									
									
									
									
								
							@@ -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);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										20
									
								
								src/utils.rs
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								src/utils.rs
									
									
									
									
									
								
							@@ -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 {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user