1.log的作用
在软件开发周期中,不管是前台还是后台,系统一般会采用一个持久化的日志系统来记录运行情况。
在代码中嵌入log代码信息,主要记录下列信息:
(1)记录系统运行异常信息。
(2)记录系统运行状态信息。
(3)记录系统运行性能指标。
通过对上述信息分析和诊断,我们能采取正确的手段来提高系统质量和系统性能。由此可见log日志在系统中的重要地位和存在的必要性。
2.log的类型与级别
2.1 log类型
主要分三大类:
安全类信息:记录系统边界交互行为和信息;
业务类信息:记录系统内部业务处理行为和信息;
性能类信息:记录系统硬件对业务处理的支撑能力。
2.2 log级别
日志级别通常分为以下几个常见类别,从最高级到最低级排列:
FATAL(致命): FATAL级别的日志用于记录严重的错误,通常表示应用程序遇到了无法继续运行的情况,可能会导致应用程序崩溃。这些日志级别用于标识需要立即处理的严重问题。
ERROR(错误):ERROR级别的日志用于记录错误情况,例如操作失败、异常抛出等。这些日志表明应用程序遇到了一些问题,但通常不会导致应用程序崩溃。ERROR级别的日志在生产环境中非常重要,以及时发现和修复错误。
WARN(警告):WARNING级别的日志用于记录警告信息,例如潜在的问题或不正常的情况,但不会导致应用程序终止。这些日志可以帮助监控和排查问题。
INFO(通知):INFO级别的日志用于记录应用程序的一般信息,例如应用程序启动、关键事件、操作完成等。这些日志对于了解应用程序的运行状态和性能是有用的,通常会在生产环境中启用。
DEBUG(调试):DEBUG级别的日志用于记录应用程序的调试信息,例如变量的值、函数的执行路径等。它有助于开发人员查找和修复问题。DEBUG级别的日志在开发和测试阶段启用,但在生产环境中通常禁用,因为它可能会产生大量的输出。
TRACE(跟踪):这是最详细的日志级别,通常用于记录应用程序中最细微的操作和事件,以帮助调试和追踪问题。TRACE级别的日志包含非常详细的信息,通常只在开发和调试阶段启用。
在本文实现的简单日志系统中不包括DEBUG和TRACE。DEBUG在编码过程中进行,TRACE不太需要。
2.3 常见log库
C/C++实现的开源log常见有:
快速的 C++ 日志库 spdlog
Google 的 C++日志库 glog
C++版的 log4j 的 log4cplus
纯C日志函数库 zlog 等。
其中 spdlog 和 glog 在项目中的使用较为常见,具体用法和源码请参考官方或网络资源,不再赘述。
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