1. 程式人生 > >C++實現log日誌系統

C++實現log日誌系統

1.log日誌的作用

在軟體開發週期中,不管是前臺還是後臺,系統一般會採用一個持久化的日誌系統來記錄執行情況。

在程式碼中嵌入log程式碼資訊,主要記錄下列資訊:

(1)記錄系統執行異常資訊。

(2)記錄系統執行狀態資訊。

(3)記錄系統執行效能指標。 

通過對上述資訊分析和診斷,我們能採取正確的手段來提高系統質量和系統性能。由此可見log日誌在系統中的重要地位和存在的必要性。

2.log日誌的型別與級別

2.1日誌的型別

主要分三大類:

安全類資訊:記錄系統邊界互動行為和資訊;

業務類資訊:記錄系統內部業務處理行為和資訊;

效能類資訊:記錄系統硬體對業務處理的支撐能力。

2.2日誌的級別

一般分五級:

ERROR(錯誤):此資訊輸出後,主體系統核心模組不能正常工作,需要修復才能正常工作。

WARN(警告):此資訊輸出後,系統一般模組存在問題,不影響系統執行。

INFO(通知):此資訊輸出後,主要是記錄系統執行狀態等關聯資訊。

DEBUG(除錯):最細粒度的輸出,除卻上面各種情況後,你希望輸出的相關資訊,都可以在這裡輸出。

TRACE(跟蹤):最細粒度的輸出,除卻上面各種情況後,你希望輸出的相關資訊,都可以在這裡輸出。

         在本文實現的簡單日誌系統中不包括DEBUG和TRACE。DEBUG在編碼過程中進行,TRACE不太需要。

2.3常見的開源log工具

         C/C++實現的開源log常見有:C++版的log4j 的log4cplus、快速的 C++ 日誌庫——spdlog、純C日誌函式庫 ——zlog、C++日誌框架——GoogleGlog等。

         其中開源log工具log4cplus在專案中的使用較為常見,具體用法和原始碼請參考網路的資源,不再贅述。

3.自實現log工具

         主要針對ERROR(錯誤)、WARN(警告)和INFO(通知)這三種日誌型別實現瞭如下的C++簡易log工具。由原始檔(logger.cpp)和標頭檔案(logger.h)組成。原始碼如下。

logger.h檔案:

/*
 *\logger.h
 *\brief 日記模組
 */
 
#ifndef  __logger__
#define  __logger__
 
#include <iostream>
#include <iomanip>
#include <fstream>
#include <string>
#include <cstdlib>
#include <stdint.h>
 
///
/// \brief 日誌檔案的型別
///
typedef enum log_rank {
   INFO,
   WARNING,
   ERROR,
   FATAL
}log_rank_t;
 
///
/// \brief 初始化日誌檔案
/// \param info_log_filename 資訊檔案的名字
/// \param warn_log_filename 警告檔案的名字
/// \param error_log_filename 錯誤檔案的名字
void initLogger(const std::string&info_log_filename,
                const std::string&warn_log_filename,
                const std::string&error_log_filename);
 
///
/// \brief 日誌系統類
///
class Logger {
   friend void initLogger(const std::string& info_log_filename,
                           conststd::string& warn_log_filename,
                           conststd::string& erro_log_filename);
   
public:
         //建構函式
   Logger(log_rank_t log_rank) : m_log_rank(log_rank) {};
   
   ~Logger();   
   ///
   /// \brief 寫入日誌資訊之前先寫入的原始碼檔名, 行號, 函式名
   /// \param log_rank 日誌的等級
   /// \param line 日誌發生的行號
   /// \param function 日誌發生的函式
   static std::ostream& start(log_rank_t log_rank,
                               const int32line,
                               conststd::string& function);
   
private:
   ///
   /// \brief 根據等級獲取相應的日誌輸出流
   ///
   static std::ostream& getStream(log_rank_t log_rank);
   
   static std::ofstream m_info_log_file;                   ///< 資訊日子的輸出流
   static std::ofstream m_warn_log_file;                  ///< 警告資訊的輸出流
   static std::ofstream m_error_log_file;                  ///< 錯誤資訊的輸出流
   log_rank_t m_log_rank;                             ///< 日誌的資訊的等級
};
 
 
///
/// \brief 根據不同等級進行用不同的輸出流進行讀寫
///
#define LOG(log_rank)   \
Logger(log_rank).start(log_rank, __LINE__,__FUNCTION__)
 
///
/// \brief 利用日記進行檢查的各種巨集
///
#define CHECK(a)                                            \
   if(!(a)) {                                              \
       LOG(ERROR) << " CHECK failed " << endl              \
                   << #a << "= " << (a) << endl;          \
       abort();                                            \
   }                                                      \
 
#define CHECK_NOTNULL(a)                                    \
   if( NULL == (a)) {                                      \
       LOG(ERROR) << " CHECK_NOTNULL failed "              \
                   << #a << "== NULL " << endl;           \
       abort();                                            \
    }
 
#define CHECK_NULL(a)                                       \
   if( NULL != (a)) {                                      \
       LOG(ERROR) << " CHECK_NULL failed " << endl         \
                   << #a << "!= NULL " << endl;           \
       abort();                                            \
    }
 
 
#define CHECK_EQ(a, b)                                      \
   if(!((a) == (b))) {                                     \
       LOG(ERROR) << " CHECK_EQ failed "  << endl          \
                   << #a << "= " << (a) << endl           \
                   << #b << "= " << (b) << endl;          \
       abort();                                            \
    }
 
#define CHECK_NE(a, b)                                      \
   if(!((a) != (b))) {                                     \
       LOG(ERROR) << " CHECK_NE failed " << endl           \
                   << #a << "= " << (a) << endl           \
                   << #b << "= " << (b) << endl;          \
       abort();                                            \
    }
 
#define CHECK_LT(a, b)                                      \
   if(!((a) < (b))) {                                      \
       LOG(ERROR) << " CHECK_LT failed "                   \
                   << #a << "= " << (a) << endl           \
                   << #b << "= " << (b) << endl;          \
       abort();                                            \
    }
 
#define CHECK_GT(a, b)                                      \
   if(!((a) > (b))) {                                      \
       LOG(ERROR) << " CHECK_GT failed "  << endl          \
                  << #a <<" = " << (a) << endl            \
                   << #b << "= " << (b) << endl;          \
       abort();                                            \
    }
 
#define CHECK_LE(a, b)                                      \
   if(!((a) <= (b))) {                                     \
       LOG(ERROR) << " CHECK_LE failed "  << endl          \
                   << #a << "= " << (a) << endl           \
                   << #b << "= " << (b) << endl;          \
       abort();                                            \
    }
 
#define CHECK_GE(a, b)                                      \
   if(!((a) >= (b))) {                                     \
       LOG(ERROR) << " CHECK_GE failed "  << endl          \
                   << #a << " = "<< (a) << endl            \
                   << #b << "= " << (b) << endl;          \
       abort();                                            \
    }
 
#define CHECK_DOUBLE_EQ(a, b)                               \
   do {                                                    \
       CHECK_LE((a), (b)+0.000000000000001L);              \
       CHECK_GE((a), (b)-0.000000000000001L);              \
    }while (0)
 
#endif

logger.cpp檔案原始碼:

#include "logger.h"
#include <cstdlib>
#include <ctime>
 
std::ofstream Logger::m_error_log_file;
std::ofstream Logger::m_info_log_file;
std::ofstream Logger::m_warn_log_file;
 
void initLogger(const std::string&info_log_filename,
                const std::string&warn_log_filename,
               const std::string&error_log_filename){
   Logger::m_info_log_file.open(info_log_filename.c_str());
   Logger::m_warn_log_file.open(warn_log_filename.c_str());
   Logger::m_error_log_file.open(error_log_filename.c_str());
}
 
std::ostream& Logger::getStream(log_rank_tlog_rank){
   return (INFO == log_rank) ?
                (m_info_log_file.is_open() ?m_info_log_file : std::cout) :
                (WARNING == log_rank ?
                    (m_warn_log_file.is_open()? m_warn_log_file : std::cerr) :
                    (m_error_log_file.is_open()? m_error_log_file : std::cerr));
}
 
std::ostream& Logger::start(log_rank_tlog_rank,
                            const int32 line,
                            const std::string&function) {
   time_t tm;
   time(&tm);
   char time_string[128];
   ctime_r(&tm, time_string);
   return getStream(log_rank) << time_string
                               << "function (" << function << ")"
                               << "line " << line
                               <<std::flush;
}
 
Logger::~Logger(){
   getStream(m_log_rank) << std::endl << std::flush;
   
   if (FATAL == m_log_rank) {
       m_info_log_file.close();
       m_info_log_file.close();
       m_info_log_file.close();
       abort();
    }
}

使用方法如下:

第一步,通過給定三個日誌檔案的路徑,呼叫初始化函式initLogger進行日誌檔案的建立。

第二步,在需要插入日誌的地方呼叫LOG(TYPE)<<”yourinfo”;即可。your info表示你要輸入到日誌檔案中的資訊。

以WARN日誌為例,輸出的資訊大致如下:

Sun Jul  5 09:49:48 2015

 function (getNextTask) line 75 no task to berun

Sun Jul  5 09:49:53 2015

 function (getNextTask) line 75 no task to berun

Sun Jul  5 09:49:58 2015

 function (getNextTask) line 75 no task to berun

Sun Jul  5 09:50:03 2015

 function (getNextTask) line 75 no task to berun