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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏ThoughtWorks

Gradle 命令行黑魔法

#ThoughtWorkers好声音# 第十五期 关于 Gradle ,我们常常会想到的是如何编写一个 Task ,怎么写一个 Plugin ,但是对于 Gr...

2725
来自专栏吴伟祥

activeMQ 原

1023
来自专栏玩转JavaEE

Spring Cloud Stream使用细节

上篇文章我们看了Spring Cloud Stream的基本使用,小伙伴们对Spring Cloud Stream应该也有了一个基本的了解,但是上篇文章中的消息...

4356
来自专栏JetpropelledSnake

RESTful源码学习笔记之RPC和Restful深入理解

RPC 即远程过程调用(Remote Procedure Call Protocol,简称RPC),像调用本地服务(方法)一样调用服务器的服务(方法)。通常的实...

1033
来自专栏陈树义

Java日志框架那些事儿

在项目开发过程中,我们可以通过 debug 查找问题。而在线上环境我们查找问题只能通过打印日志的方式查找问题。因此对于一个项目而言,日志记录是一个非常重要的问...

7059
来自专栏张善友的专栏

将Windows日志转换为Syslog

无论是Unix、Linux、FreeBSD、Ubuntu,还是路由器、交换机,都会产生大量的日志,而这些,一般会以syslog的形式存在。在RFC 3164中定...

45010
来自专栏晓晨的专栏

使用 JMeter 进行压力测试

3762
来自专栏杨建荣的学习笔记

Linux中的configure,make,make install到底在做些什么

在Linux下经常要安装部署一些软件包或者工具,拿到安装包之后一看,简单,configure,make, make install即可搞定。 有时...

3553
来自专栏FreeBuf

利用Web查询文件(.iqy)有效钓鱼

几天前我在Casey Smith的twitter上看到了有关iqy文件的一些介绍,和大多数渗透测试人员或者技术狂热者一样我也在探寻它的价值。能够通过使用本地/可...

2926
来自专栏我是攻城师

理解Java中锁的状态与优化

关于锁的知识,按大类来说,通常我们只分乐观锁和悲观锁。但在Java语言里对同步锁的状态又进行了细化通常有无锁状态,偏向锁,自旋锁,轻量级锁,重量级锁,这么做的目...

1634

扫码关注云+社区

领取腾讯云代金券