tdengine 0.1.2

game server for Rust
Documentation
use std::io::prelude::*;
use std::fs::{self, File};
use std::path::Path;
use std::sync::Arc;

use time;

use td_rthreadpool::ReentrantMutex;

use FileUtils;

const KEEP_ROLE_SECOND: u64 = 60 * 60 * 24;

pub const LOG_ERROR: u8 = 1;
pub const LOG_WARN:  u8 = 2;
pub const LOG_INFO:  u8 = 3;
pub const LOG_DEBUG: u8 = 4;
pub const LOG_TRACE: u8 = 5;

pub struct LogUtils {
    file: Option<File>,
    mutex: Arc<ReentrantMutex<i32>>,
    log_path: String,
    basename: String,
    roll_size: usize,
    flush_interval: u64,
    check_every_n: u64,
    force_flush_interval: u64,

    cur_file_size: usize,
    cur_count: u64,
    last_append: u64,
    last_flush: u64,
    start_of_period: u64,

    cache_time: u64,
    date_cache: String,
}

static mut ins: *mut LogUtils = 0 as *mut _;
impl LogUtils {
    pub fn instance() -> &'static mut LogUtils {
        unsafe {
            if ins == 0 as *mut _ {
                ins = Box::into_raw(Box::new(LogUtils::new()));
            }
            &mut *ins
        }
    }

    pub fn new() -> LogUtils {
        LogUtils {
            file: None,
            mutex: Arc::new(ReentrantMutex::new(0)),
            log_path: String::new(),
            basename: "TDEngine".to_string(),
            roll_size: 1024 * 1024 * 50,
            flush_interval: 3,
            check_every_n: 1024,
            force_flush_interval: 180,
            cur_file_size: 0,
            cur_count: 0,
            last_append: 0,
            last_flush: 0,
            start_of_period: 0,

            cache_time: 0,
            date_cache: String::new(),
        }
    }

    pub fn set_log_path(&mut self, path: String) {
        self.log_path = path;
        let _ = fs::create_dir_all(Path::new(&*self.log_path));

        if self.file.is_none() {
            self.file = self.role_file();
        }
    }

    pub fn role_file(&mut self) -> Option<File> {
        let filename = self.get_log_filename(time::now());
        let file = unwrap_or!(File::create(filename).ok(), return None);
        let now = time::precise_time_s() as u64;
        let start = now / KEEP_ROLE_SECOND;
        self.start_of_period = start;
        self.last_flush = now;
        self.last_append = now;
        self.cur_count = 0;
        Some(file)
    }

    pub fn get_can_use_filename(&mut self, filename: String) -> String {
        if !FileUtils::is_file_exists(&*filename) {
            return filename;
        }

        for i in 1.. {
            let name = format!("{}.{}", filename, i);
            if !FileUtils::is_file_exists(&*name) {
                return name;
            }
        }
        unreachable!("find no use file");
    }

    pub fn get_log_filename(&mut self, now: time::Tm) -> String {
        let name = now.strftime(".%Y-%m-%d-%H_%M_%S").unwrap().to_string();
        let filename = self.log_path.clone() + &*self.basename + &*name + ".log";
        self.get_can_use_filename(filename)
    }

    pub fn append(&mut self, method : u8, log: &str) {
        let mutex = self.mutex.clone();
        let _guard = mutex.lock().unwrap();
        self.append_unlock(method, log);
    }

    pub fn write_bytes(&mut self, bytes: &[u8]) {
        if self.file.is_none() {
            return;
        }
        let _ = self.file.as_ref().unwrap().write(bytes);
        self.cur_file_size += bytes.len();
    }

    pub fn write_date(&mut self) {
        if self.file.is_none() {
            return;
        }
        let _ = self.file.as_ref().unwrap().write(self.date_cache.as_bytes());
        self.cur_file_size += self.date_cache.as_bytes().len();
    }

    pub fn write_log_method(&mut self, method : u8) {
        let ret = match method {
            LOG_ERROR => "[error] ",
            LOG_WARN  => "[warn!] ",
            LOG_INFO  => "[info!] ",
            LOG_DEBUG => "[debug] ",
            LOG_TRACE | _ => "[trace] ",
        };
        let _ = self.file.as_ref().unwrap().write(ret.as_bytes());
        self.cur_file_size += ret.as_bytes().len();
    }

    pub fn append_unlock(&mut self, method : u8, log: &str) {
        let mutex = self.mutex.clone();
        let _guard = mutex.lock().unwrap();
        if self.file.is_none() {
            return;
        }
        let now = time::precise_time_s() as u64;
        if now != self.cache_time {
            self.cache_time = now;
            let tm = time::now();
            self.date_cache = format!("[{:4}-{:02}-{:02} {:02}:{:02}:{:02}] ",
                                      tm.tm_year + 1900,
                                      tm.tm_mon + 1,
                                      tm.tm_mday,
                                      tm.tm_hour,
                                      tm.tm_min,
                                      tm.tm_sec);
        }
        self.write_date();
        self.write_log_method(method);
        self.write_bytes(log.as_bytes());
        self.write_bytes(b"\r\n");
        self.check_file_status();
    }

    pub fn check_file_status(&mut self) {
        let now = time::precise_time_s() as u64;
        if self.cur_file_size > self.roll_size {
            self.file = self.role_file();
        } else {
            self.cur_count += 1;
            if self.cur_count > self.check_every_n {
                let this_period = now / KEEP_ROLE_SECOND;
                if this_period != self.start_of_period {
                    self.file = self.role_file();
                } else if now - self.last_flush > self.flush_interval {
                    self.last_flush = now;
                    let _ = self.file.as_ref().unwrap().flush();
                }
            } else if now - self.last_flush > self.force_flush_interval {
                self.cur_count = 0;
                self.last_flush = now;
                let _ = self.file.as_ref().unwrap().flush();
            }
        }
    }
}