轉載位址:https://blog.csdn.net/laobai1015/article/details/80004504
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在項目中的使用較為常見,介紹如下:
Log4j以其簡單的使用方式(引入一個jar包,一行代碼即可調用),靈活(可通過配置檔案随意配置),功能強大(多個級别,可配置多個輸出目的地,Console,File,系統日志,遠端的LogServer等等,可訂制日志格式,自動産生,删除日志檔案)等特性,是Java日志系統的首選。
(1).C++版本Log4j - Log4cplus
Log4cplus日志系統是仿照Log4J,使用C++語言開發的日志系統,即為了打造C++版的Log4j。而且最重要的是Log4cplus是開源的。
(2).使用簡介
Log4cplus下載下傳位址:http://sourceforge.net/projects/log4cplus/
Log4j是一個C++庫,編譯以後即可使用。同時log4cplus支援windows和linux:
windows下:打開根目錄下的msvc10下面的vs工程,編譯即可;
Linux下:configure, make。
Log4j for C++ 實用指南
https://blog.csdn.net/liang19890820/article/details/80796813?_wv=1031
http://www.voidcn.com/article/p-bpbaoeix-bqa.html
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