來自mooon的最簡單的日誌類CSimpleLogger

 /**

 * 單個頭文件,可即時獨立使用,只要定義了宏NOT_WITH_MOOON,即不依賴於mooon
 * 簡單的寫日誌類,非線程安全,提供按大小滾動功能
 * 不追求功能,也不追求性能,只求簡單,若要功能強、性能高,可以使用CLogger
 *
 * 使用方法:
 * 1) 構造一個CSimpleLogger對象
 *    CSimpleLogger logger(".", "test.log", 1024*1024, 10);
 * 2) 調用print方法寫日誌
 *    logger.print("%s\n", "test");
 */
#ifndef MOOON_SYS_SIMPLE_LOGGER_H
#define MOOON_SYS_SIMPLE_LOGGER_H
 
// 只要定義了NOT_WITH_MOOON宏,
// 則本文件和mooon無任何關係,方便集成到自己的代碼中
#define NOT_WITH_MOOON
#if !defined(NOT_WITH_MOOON)
#include <sys/config.h>
#endif // NOT_WITH_MOOON
 
#include <stdio.h>
#include <stdarg.h>
#include <time.h>
#include <sstream>
#if !defined(NOT_WITH_MOOON)
SYS_NAMESPACE_BEGIN
#endif // NOT_WITH_MOOON
 
/***
  * 萬能型類型轉換函數
  */
template <typename AnyType>
inline std::string any2string(AnyType any_value)
{
    std::stringstream result_stream;
    result_stream << any_value;
 
    return result_stream.str();
}
 
/***
  * 取當前時間,和date_util.h有重複,但爲保持simple_logger.h的獨立性,在所難免
  */
inline void get_current_datetime(char* datetime_buffer, size_t datetime_buffer_size)
{
    struct tm result;
    time_t now = time(NULL);
 
    localtime_r(&now, &result);
    snprintf(datetime_buffer, datetime_buffer_size
        ,"%04d-%02d-%02d %02d:%02d:%02d"
        ,result.tm_year+1900, result.tm_mon+1, result.tm_mday
        ,result.tm_hour, result.tm_min, result.tm_sec);
}
 
class CSimpleLogger
{
public:
    /***
      * 構造一個CSimpleLogger,並創建或打開日誌文件
      * @log_dir 日誌存放的目錄,不需要以斜槓結尾,目錄必須已經存在
      * @filename 日誌的文件名,不包含目錄部分,
      *           由log_dir和filename共同組成日誌文件路徑
      * @log_size 每個日誌文件的大小,單位爲字節數,如果小於1024,則會被強制爲1024
      * @log_numer 日誌滾動的個數
      * @record_size 單條日誌的大小,超過會被截斷,單位爲字節數,如果小於1024,則會被強制爲1024
      */
    CSimpleLogger(const std::string& log_dir
                 ,const std::string& filename
                 ,unsigned int log_size = 1024*1024*100
                 ,unsigned char log_numer = 10
                 ,unsigned short record_size = 8192);
    ~CSimpleLogger();
 
    /** 日誌文件是否創建或打開成功 */
    bool is_ok() const;
    
    /** 輸出日誌,象printf一樣使用,不自動加換行符 */
    void print(const char* format, ...);
 
    /** 刷新日誌,因爲使用FILE是帶緩存的 */
    void flush();
 
private:
    void reset();    /** 復位狀態值 */
    void roll_log(); /** 滾動日誌 */
 
private:
    FILE* _fp;                    /** 當前正在寫的日誌文件描述符 */
    char* _log_buffer;            /** 存放日誌的Buffer */
    int _bytes_writed;            /** 已經寫入的字節數 */
    std::string _log_dir;         /** 日誌存放目錄 */
    std::string _filename;        /** 日誌文件名,不包含目錄部分 */
    unsigned int _log_size;       /** 單個日誌文件的大小 */
    unsigned char _log_numer;     /** 日誌滾動的個數 */
    unsigned short _record_size;  /** 單條日誌的大小,單位爲字節數 */
};
 
inline CSimpleLogger::CSimpleLogger(
                     const std::string& log_dir 
                    ,const std::string& filename
                    ,unsigned int log_size
                    ,unsigned char log_numer
                    ,unsigned short record_size)
 :_fp(NULL)
 ,_log_buffer(NULL)
 ,_bytes_writed(0)
 ,_log_dir(log_dir)
 ,_filename(filename)
 ,_log_size(log_size)
 ,_log_numer(log_numer)
 ,_record_size(record_size)
{
    std::string log_path = log_dir + std::string("/") + filename;
    _fp = fopen(log_path.c_str(), "a");
    
    if (_fp != NULL)
    {
        if (-1 == fseek(_fp, 0, SEEK_END))
        {
            // 失敗,將不會寫日誌
            fclose(_fp);
            _fp = NULL;
        }
        else
        {
            // 取得已有大小
            _bytes_writed = ftell(_fp);
 
            // 不能太小氣了          
            if (_log_size < 1024)
            {
                _log_size = 1024;
            }
            
            // 同樣不能太小氣
            if (_record_size < 1024)
            {
                _record_size = 1024;
            }
            
            _log_buffer = new char[_record_size];
        }
    }
}
 
inline CSimpleLogger::~CSimpleLogger()
{
    if (_fp != NULL)
        fclose(_fp);
        
    delete []_log_buffer;
}
 
inline bool CSimpleLogger::is_ok() const
{
    return _fp != NULL;
}
 
inline void CSimpleLogger::print(const char* format, ...)
{
    if (_fp != NULL)
    {
        va_list ap;
        va_start(ap, format);
 
        char datetime_buffer[sizeof("2012-12-21 00:00:00")]; // 剛好世界末日
        get_current_datetime(datetime_buffer, sizeof(datetime_buffer));
 
        vsnprintf(_log_buffer, _record_size, format, ap);
        int bytes_writed = fprintf(_fp, "[%s]%s", datetime_buffer, _log_buffer);
        if (bytes_writed > 0)
            _bytes_writed += bytes_writed;
 
        if (_bytes_writed > static_cast<int>(_log_size))
        {
            roll_log();
        }
 
        va_end(ap);
    }
}
 
inline void CSimpleLogger::roll_log()
{
    std::string new_path; // 滾動後的文件路徑,包含目錄和文件名
    std::string old_path; // 滾動前的文件路徑,包含目錄和文件名
    
    reset(); // 輪迴,一切重新開始
    
    // 歷史滾動
    for (int i=_log_numer-1; i>0; --i)
    {
        new_path = _log_dir + std::string("/") + _filename + std::string(".") + any2string(i);
        old_path = _log_dir + std::string("/") + _filename + std::string(".") + any2string(i-1);
 
        if (0 == access(old_path.c_str(), F_OK))
        {
            rename(old_path.c_str(), new_path.c_str());
        }
    }
 
    if (_log_numer > 0)
    {
        // 當前滾動
        new_path = _log_dir + std::string("/") + _filename + std::string(".1");
        old_path = _log_dir + std::string("/") + _filename;
        if (0 == access(old_path.c_str(), F_OK))
        {
            rename(old_path.c_str(), new_path.c_str());
        }
    }
 
    // 重新創建
    _fp = fopen(old_path.c_str(), "w+");
}
 
inline void CSimpleLogger::reset()
{
    _bytes_writed = 0;
 
    if (_fp != NULL)
    {
        fclose(_fp);
        _fp = NULL;
    }
}
 
inline void CSimpleLogger::flush()
{
    if (_fp != NULL)
        fflush(_fp);
}
 
/***
  * 測試代碼
#include "simple_logger.h"
int main()
{
    CSimpleLogger logger(".", "test.log", 10240);
    for (int i=0; i<100000; ++i)
        logger.print("%d ==> abcdefghijklmnopqrestuvwxyz.\n", i);
        
    return 0;
}
*/
 
#if !defined(NOT_WITH_MOOON)
SYS_NAMESPACE_END
#endif // NOT_WITH_MOOON
#endif // MOOON_SYS_SIMPLE_LOGGER_H
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章