精华内容
下载资源
问答
  • C++通用日志系统
    2022-03-25 10:36:06

    日志系统是成熟软件中的一个重要组成部分,其记录软件的使用和运行行为,方便事后进行故障分析、数据统计等。本日志模块的主旨是提供一个基于Qt框架,简单易用、可拓展性好、支持多种输出目标和格式的底层日志库,方便上层调用。模块设计过程中参考了Log4j等软件,并结合C++特性,具有高度可拓展、使用灵活、高性能等优点。下载

    更多相关内容
  • c++实现日志系统

    2017-01-24 16:27:34
    软件开发中常常将关键的操作、一般性警告以及严重的错误,通过日志文件的形式输出字符串,本子系统实现bootlog,debuglog,warning,fatallog等输出形式,支持linux、windows两种编译环境,支持多线程。有不到之处...
  • 本篇文章是对C++日志系统log4cxx的使用进行了详细的分析介绍,需要的朋友参考下
  • C++ 开源日志系统

    2015-03-20 10:15:26
    里面包含 log4cplus 类库文件 说明书 我自己画的类图 使用方式
  • C++设计实现日志系统

    千次阅读 多人点赞 2019-11-16 20:32:18
    日志系统几乎是每一个实际的软件项目从开发、测试到交付,再到后期的维护...本文Jungle将用C++设计实现一个日志系统。 1.为什么需要日志 为什么需要日志?其实在引言中已经提到了,实际的软件项目的几乎每个过...

    日志系统几乎是每一个实际的软件项目从开发、测试到交付,再到后期的维护过程中极为重要的查看软件代码运行流程还原错误现场记录运行错误位置及上下文等的重要依据。一个高性能的日志系统,能够准确记录重要的变量信息,同时又没有冗余的打印导致日志文件记录无效的数据。本文Jungle将用C++设计实现一个日志系统。

    1.为什么需要日志

    为什么需要日志?其实在引言中已经提到了,实际的软件项目的几乎每个过程,都离不开日志。初学代码时,Jungle的第一行代码是实现打印“hello world”,打印到控制台。在后来的学习中,Jungle又学会了设断点调试代码,在适当的地方通过断点来观察变量的值。但在实际的软件项目中,试想一下,通过输出到控制台或者通过设断点来调试代码,可能吗?

    • 客户现场,会让你现场打印到控制台上调试吗?
    • 报了error的软件项目,你能够明确知道软件crash的位置吗?
    • 你能保证设断点可以还原error时候的现场吗?
    • 概率性的error事件,设断点还奏效吗?
    • 如果是时效性的代码(比如USB连接) ,设断点调试还合理吗?
    • ……

    日志,可以记录每一时刻软件的运行情况,记录error或者crash时的信息(时间、关键变量的值、出错位置、线程等);另一方面,对于概率性error事件,可以在重复测试时通过日志来查询错误复现时候的情况。简言之,日志是跟踪和回忆某个时刻或者时间段内的程序行为进而定位问题的一种重要手段

    2.日志系统设计

    软件运行过程中,需要记录的有什么呢?前述已经提到,关键变量的值、运行的位置(哪个文件、哪个函数、哪一行)、时间、线程号、进程号。本文Jungle采用C++设计了LOG类,介绍LOG类的设计之前,需要提及的是log的级别和log位置。

    2.1.1.log级别

    Log级别是什么意思呢?在开发阶段,Jungle可能想尽可能详细地跟踪代码运行过程,所以可以打印尽可能多的信息到日志文件中;测试过程中,测试部可能不需要这么详细的信息,所以这时候有的信息可能不必输出到Log文件;产品交付客户使用时,为了软件运行更快、客户体验更好,这时候就只需打印关键信息到日志文件了,因为过多的写文件会耗费大量时间,影响软件运行速度。所以Jungle为LOG类定义了如下级别:

    enum LOGLEVEL
    {
    	LOG_LEVEL_NONE,
    	LOG_LEVEL_ERROR,     // error
    	LOG_LEVEL_WARNING,   // warning
    	LOG_LEVEL_DEBUG,     // debug
    	LOG_LEVEL_INFO,      // info	
    };

    在软件设计中,可以通过某些方法或者预留一些开关来设置Log级别,方便在开发、调试、测试和客户现场灵活地调整日志级别,以获取到有用的日志信息。 

    2.1.2.log输出位置

    Log文件可以输出到控制台(其实也是不错的方法),也可以输出到指定路径下的某个文件里,也可能有别的需求。比如,开发或调试时,简单的信息直接就打印到软件某个界面上;测试或者交付客户时,最好将日志保存到文件里,这样可以保存尽可能多的信息。因此,Jungle进行了如下设计:

    enum LOGTARGET
    {
    	LOG_TARGET_NONE      = 0x00,
    	LOG_TARGET_CONSOLE   = 0x01,
    	LOG_TARGET_FILE      = 0x10
    };

    2.1.3.log的作用域

    一个软件系统,要在哪儿输出日志呢?Everywhere!只要是你想打印日志的地方,任何一个函数、任何一个文件,都应该而且必须可以打印。也就是说这个log类的对象(不妨叫做日志记录器),日志记录器必须是全局的

    光是全局的就够了吗?你这个文件里有一个全局的日志记录器,输出日志到file.log文件里;另一个文件里也有一个日志记录器,也输出到file.log文件里……多个日志记录器同时往一个文件里写日志,这显然不合理。所以还必须保证日志记录器全局且唯一

    怎么保证日志记录器唯一呢?即Log类在具体的软件系统中有且仅有一个实例化对象。答案是采用单例模式!(设计模式(九)——单例模式

    2.2.日志类的设计

    综上所述,Jungle设计的日志类LOG如下:

    class LOG
    {
    public:
    
    	// 初始化
    	void init(LOGLEVEL loglevel, LOGTARGET logtarget);
    
    	// 
    	void uninit();
    
    	// file
    	int createFile();
    
    	static LOG* getInstance();
    
    	// Log级别
    	LOGLEVEL getLogLevel();
    	void setLogLevel(LOGLEVEL loglevel);
    
    	// Log输出位置
    	LOGTARGET getLogTarget();
    	void setLogTarget(LOGTARGET logtarget);
    
    	// 打log
    	static int writeLog(
    		LOGLEVEL loglevel,         // Log级别
    		unsigned char* fileName,   // 函数所在文件名
    		unsigned char* function,   // 函数名
    		int lineNumber,            // 行号
    		char* format,              // 格式化
    		...);                      // 变量
    
    	// 输出log
    	static void outputToTarget();
    
    private:
    	LOG();
    	~LOG();
    	static LOG* Log;
    
    	// 互斥锁
    	static mutex log_mutex;
    
    	// 存储log的buffer
    	static string logBuffer;
    
    	// Log级别
    	LOGLEVEL logLevel;
    
    	// Log输出位置
    	LOGTARGET logTarget;
    
    	// Handle
    	static HANDLE mFileHandle;
    };

    其中,互斥锁log_mutex是用于在多线程环境下保证只创建一个LOG类的实例 (设计模式(九)——单例模式);mFileHandle是log文件的句柄。

    2.3.日志类的实现

    2.3.1.初始化

    LOG*             LOG::Log         = NULL;
    string           LOG::logBuffer   = "";
    HANDLE           LOG::mFileHandle = INVALID_HANDLE_VALUE;
    mutex            LOG::log_mutex;
    
    LOG::LOG()
    {
    	// 初始化
    	init(LOG_LEVEL_NONE, LOG_TARGET_FILE);
    } 
    
    void LOG::init(LOGLEVEL loglevel, LOGTARGET logtarget)
    {
    	setLogLevel(loglevel);
    	setLogTarget(logtarget);
    	createFile();
    }
    
    void LOG::uninit()
    {
    	if (INVALID_HANDLE_VALUE != mFileHandle)
    	{
    		CloseHandle(mFileHandle);
    	}
    }
    
    LOG* LOG::getInstance()
    {
    	if (NULL == Log)
    	{
    		log_mutex.lock();
    		if (NULL == Log)
    		{
    			Log = new LOG();
    		}
    		log_mutex.unlock();
    	}
    	return Log;
    }
    
    LOGLEVEL LOG::getLogLevel()
    {
    	return this->logLevel;
    }
    
    void LOG::setLogLevel(LOGLEVEL iLogLevel)
    {
    	this->logLevel = iLogLevel;
    }
    
    LOGTARGET LOG::getLogTarget()
    {
    	return this->logTarget;
    }
    
    void LOG::setLogTarget(LOGTARGET iLogTarget)
    {
    	this->logTarget = iLogTarget;
    }

    初始化工作设置了日志的级别和输出位置(代码中提供了日志级别和输出位置的setter、getter方法)。函数createFile()是创建日志文件位置,并获取日志文件的句柄mFileHandle。代码如下:

    int LOG::createFile()
    {
    	TCHAR fileDirectory[256];
    	GetCurrentDirectory(256, fileDirectory);
    
    	// 创建log文件的路径
    	TCHAR logFileDirectory[256];
    	_stprintf_s(logFileDirectory, _T("%s\\Test\\"), fileDirectory);// 使用_stprintf_s需要包含头文件<TCHAR.H>
    
    	// 文件夹不存在则创建文件夹
    	if (_taccess(logFileDirectory, 0) == -1)
    	{
    		_tmkdir(logFileDirectory);
    	}
    
    	TCHAR cTmpPath[MAX_PATH] = { 0 };
    	TCHAR* lpPos = NULL;
    	TCHAR cTmp = _T('\0');
    
    	WCHAR pszLogFileName[256];
    	// wcscat:连接字符串
    	wcscat(logFileDirectory, _T("test.log"));
    	_stprintf_s(pszLogFileName, _T("%s"), logFileDirectory);
    	mFileHandle = CreateFile(
    		pszLogFileName,
    		GENERIC_READ | GENERIC_WRITE,
    		FILE_SHARE_READ,
    		NULL,
    		OPEN_ALWAYS,
    		FILE_ATTRIBUTE_NORMAL,
    		NULL);
    	if (INVALID_HANDLE_VALUE == mFileHandle)
    	{
    		return -1;
    	}
    	return 0;
    }

    其中,需要介绍的是下述函数:

    • GetCurrentDirectory:在一个缓冲区中装载当前目录
    • _stprintf_s:将若干个参数按照format格式存到buffer中
    • _taccess:判断文件是否存在,返回值0表示该文件存在,返回-1表示文件不存在或者该模式下没有访问权限
    • _tmkdir:创建一个目录

    2.3.2.写日志

    以下是writeLog()方法的实现:

    int LOG::writeLog(
    	LOGLEVEL loglevel,         // Log级别
    	unsigned char* fileName,   // 函数所在文件名
    	unsigned char* function,   // 函数名
    	int lineNumber,            // 行号
    	char* format,              // 格式化
    	...)
    {
    	int ret = 0;
    
    	// 获取日期和时间
    	char timeBuffer[100];
    	ret = getSystemTime(timeBuffer);
    	logBuffer += string(timeBuffer);
    
    	// LOG级别
    	char* logLevel;
    	if (loglevel == LOG_LEVEL_DEBUG){
    		logLevel = "DEBUG";
    	}
    	else if (loglevel == LOG_LEVEL_INFO){
    		logLevel = "INFO";
    	}
    	else if (loglevel == LOG_LEVEL_WARNING){
    		logLevel = "WARNING";
    	}
    	else if (loglevel == LOG_LEVEL_ERROR){
    		logLevel = "ERROR";
    	}
    
    	// [进程号][线程号][Log级别][文件名][函数名:行号]
    	char locInfo[100];
    	char* format2 = "[PID:%4d][TID:%4d][%s][%-s][%s:%4d]";
    	ret = printfToBuffer(locInfo, 100, format2,
    		GetCurrentProcessId(),
    		GetCurrentThreadId(),
    		logLevel,
    		fileName,
    		function,
    		lineNumber);
    	logBuffer += string(locInfo);	
    
    	// 日志正文
    	char logInfo2[256];
    	va_list ap;
    	va_start(ap, format);
    	ret = vsnprintf(logInfo2, 256, format, ap);
    	va_end(ap);
    
    	logBuffer += string(logInfo2);
    	logBuffer += string("\n");
    
    	outputToTarget();
    
    	return 0;
    }

    2.3.3.输出日志

    void LOG::outputToTarget()
    {
    	if (LOG::getInstance()->getLogTarget() & LOG_TARGET_FILE)
    	{
    		SetFilePointer(mFileHandle, 0, NULL, FILE_END);
    		DWORD dwBytesWritten = 0;
    		WriteFile(mFileHandle, logBuffer.c_str(), logBuffer.length(), &dwBytesWritten, NULL);
    		FlushFileBuffers(mFileHandle);
    	}
    	if (LOG::getInstance()->getLogTarget() & LOG_TARGET_CONSOLE)
    	{
    		printf("%s", logBuffer.c_str());
    	}
    
    	// 清除buffer
    	logBuffer.clear();
    }
    • SetFilePointer:将文件指针移动到文件指定的位置
    • FlushFileBuffers:把写文件缓冲区的数据强制写入磁盘

    为了使用方便,可以定义一些宏来简化函数的使用,本文不再赘述。

    3.测试

    Jungle将上述设计实现的日志系统应用到了之前写的一些小程序里,比如在之前的“欲戴王冠,必承其重”——深度解析职责链模式的代码。如何添加呢?就是将两个文件(头文件和源文件)加入工程,包含头文件,再在需要打log的地方加上Jungle在日志类里定义的宏即可。下列是示例log:

    因为程序比较简单,代码量很小,所以只有一个线程(log中TID都是一样的)。但上述测试结果验证了Jungle设计的日志系统是可行的。

    4.多线程环境

    4.1.多线程环境测试

    接下来Jungle设计一个简单的多线程环境,测试一下上述日志系统,测试代码如下:

    #define THREAD_NUM 5
    // 全局资源变量
    int g_num = 0;
    
    unsigned int __stdcall func(void *pPM)
    {
    	LOG_INFO("enter");
    	Sleep(50);
    	g_num++;
    	LOG_INFO("g_num = %d", g_num);
    	LOG_INFO("exit");
    	return 0;
    }
    
    int main()
    {
    	LOG *logger = LOG::getInstance();
    	HANDLE  handle[THREAD_NUM];
    
    	//线程编号
    	int threadNum = 0;
    	while (threadNum < THREAD_NUM)
    	{
    		handle[threadNum] = (HANDLE)_beginthreadex(NULL, 0, func, NULL, 0, NULL);
    		//等子线程接收到参数时主线程可能改变了这个i的值
    		threadNum++;
    	}
    	//保证子线程已全部运行结束
    	WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
    	return 0;
    }

    上述代码中,Jungle一共开启了5个线程,理论上打印的日志文件里,TID应该出现5个不同的数值。每个线程里打印全局变量(即全局共享资源)的值。下面是输出的日志,一共运行了两次(第5、6行隔开):

    问题来啦!

    首先,在第一次运行输出的日志里,出现了乱码!(第1行和第4行),而且看起来该输出log的地方没有完全输出(真的吗?)

    其次,在第二次运行输出的日志里,一行log里好像打印了两次日志(第8行)!

    问题出在哪里呢?

    为什么会出现乱码?仔细看第8行log,其实打印的都是同一个时刻、同一个位置,都是在调用writeLog函数(宏LOG_INFO即是调用writeLog函数)时出现的问题,也就是说在这个时刻,两个线程都跑到函数writeLog里写log,导致logBuffer缓冲区里存放了两次信息。只不过第8行运气较好,每次的编码都保存完整。而第1行和第4行就没这么走运了!(logBuffer里已经完全乱了!)所以根本问题是,多个线程在同一个时刻访问了同一个资源!所以针对多线程环境,我们需要做到共享资源的互斥

    4.2.线程安全的日志系统

    在单例模式的设计实现里已经提到了线程安全,Jungle用互斥锁达到了互斥的目的。本文也可以使用互斥锁(并且在日志对象实例的单例模式中已经使用),但在这里Jungle想用另一种方法:临界区。

    在Log类成员里声明一个CRITICAL_SECTION对象criticalSection,初始化时:

    InitializeCriticalSection(&criticalSection);

    当然,最好在释放资源时加上下述代码:

    DeleteCriticalSection(&criticalSection);

    而在进入writeLog时和离开writeLog时加上下述代码:

    int LOG::writeLog(...)
    {
    	int ret = 0;
    
    	EnterCriticalSection(&criticalSection);
        
            // do something
    
    	LeaveCriticalSection(&criticalSection);
    
    	return 0;
    }
    

    需要提及的是,最好是在LeaveCriticalSection之后再DeleteCriticalSection。

    接下来再在多线程环境里测试,Jungle测试了几次,但为了缩短篇幅,只展示一次的结果:

    可以看到,日志完整记录了每个线程的运行过程(线程号TID不同)。

    5.注意事项

    尽管上述已经基本实现了日志系统,但仍有很大的改进空间,在调试代码和查阅资料的过程中,Jungle发现需要注意以下几个问题:

    1. 字符编码问题:宽字符、ANSI编码等多种不同编码的兼容;
    2. Visio Studio版本的差异:Jungle本想将日志系统应用到之前设计的一个机器人仿真控制器里,但遗憾的是编译不通过,因为那个代码是用Visio Studio 2008写的,而Mutex是C++2011标准的内容,需要用支持该新标准的编译器,比如VS2012及以上版本。(当然了,可以用临界区等其他方法实现互斥,这里Jungle只是提出这个需要注意的问题);
    3. 关于宏_CRT_SECURE_NO_WARNINGS:是的,需要在预处理器里加上这个宏或者代码里显示声明这个宏,否则编译不通过,如下图。原因是代码中使用的wcscat等函数不安全,可能会造成内存泄露等。解决方法除了前述提到的声明宏以外,还可以使用更安全的函数。

     最后,推荐两篇不错的关于日志系统的文章:

    欢迎评论区与Jungle交流,欢迎关注Jungle的公众号!


    欢迎关注知乎专栏:Jungle是一个用Qt的工业Robot

    欢迎关注Jungle的微信公众号:Jungle笔记

     

    展开全文
  • C++的异步日志, 其特点是效率高(实测每秒支持125+万日志写入)、易拓展,尤其适用于频繁写日志的场景
  • 此源码从一个开源项目nox中提取。其中有一份使用说明,包含了接口...此模块的接口使用方便,通过调用可以轻松管理应用中不同模块发出的日志消息、向socket中发送日志消息的功能。最后,要强调一点,这个模块依赖boost库
  • C++实现日志系统

    2018-07-05 11:06:24
    C++实现的日志系统,支持变长参数,支持文件大小控制。
  • C++实现的日志系统·

    2020-11-18 22:24:08
    我从网上找到的多个C++日志demo,支持跨平台和多线程安全。但是我不是很会用,都觉得用起来不方便,大家用的什么好用日志系统,麻烦留个言。这个也感谢各个demo的作者。
  • c/c++Log日志

    2017-05-18 17:28:06
    简单常用的c++日志
  • 主要介绍了linux系统c++日志文件功能,简化了glog,只保留了写日志文件的功能,只是改写了linux版本,需要的朋友可以参考下
  • 使用C++实现一个简单的日志系统

    千次阅读 2019-12-12 21:48:44
    本文讲述使用C++在windows平台实现一个简单的日志系统的方法。该日志系统的特点如下: 1.支持类似c语言printf风格的输出方式,支持不定参数。 2.支持将日志输出到屏幕和文件中。 3.支持打印系统时间。 4.线程...

    一、概述

        本文讲述使用C++在windows平台实现一个简单的日志系统的方法。该日志系统的特点如下:

    1.支持类似c语言printf风格的输出方式,支持不定参数。

    2.支持将日志输出到屏幕和文件中。

    3.支持打印系统时间。

    4.线程安全,各个线程都能同时写出日志。

     

    二、实现方式

     

    实现代码如下:

    AfMutex.h

    #ifndef _OSAPI_MUTEX_H
    #define _OSAPI_MUTEX_H
    
    struct AfMutex_Priv;
    
    //互斥锁相关的类
    class AfMutex
    {
    public:
    	AfMutex();
    	~AfMutex();
    
    	int Lock();
    	int TryLock();
    	void Unlock();
    
    private:
    	int Init(); 
    
    private:
    	AfMutex_Priv *m_Priv;
    
    };

     

    AfMutex_Win32.cpp

    #include "AfMutex.h"
    
    #ifdef _WIN32
    #include <windows.h>
    
    struct AfMutex_Priv
    {
    	HANDLE hMutex;
    };
    
    AfMutex::AfMutex()
    :m_Priv(NULL)
    {
    	Init();
    }
    
    AfMutex::~AfMutex()
    {
    	if(m_Priv) 
    	{
    		CloseHandle(m_Priv->hMutex);
    		delete m_Priv;
    	}
    }
    
    int AfMutex::Init()
    {
    	m_Priv = new AfMutex_Priv;
    	m_Priv->hMutex = CreateMutex(NULL, true, NULL);
    	if(m_Priv->hMutex == NULL)
    	{
    		delete m_Priv;
    		m_Priv = NULL;
    		return -1;
    	}
    
    	ReleaseMutex(m_Priv->hMutex);
    	return 0;
    }
    
    int AfMutex::Lock()
    {
    	if(!m_Priv) return -1;
    
    	WaitForSingleObject(m_Priv->hMutex, INFINITE);
    	return 0;
    }
    
    int AfMutex::TryLock()
    {
    	if(!m_Priv) return -1;
    
    	DWORD  ret = WaitForSingleObject(m_Priv->hMutex, 1);	
    	if( ret == WAIT_OBJECT_0)
    	{
    		return 0; // success
    	}
    	if( ret == WAIT_TIMEOUT)
    	{
    		return -1; // timeout
    	}
    	return -1;
    }
    
    void AfMutex::Unlock()
    {
    	if(!m_Priv) return;
    	ReleaseMutex(m_Priv->hMutex);
    }
    
    #endif

     

    Clog.h

    #ifndef CLOG_H
    #define CLOG_H
    
    #include <stdio.h>
    #include <ctime>
    #include <stdarg.h>
    #include "AfMutex.h"
    
    //日志相关的类
    class CLog
    {
    public:
    	CLog(void)
    	{
    		;
    	}
    public:
    	static CLog *i()                    //单例模式
    	{
    		static CLog c_log;
    		return &c_log;
    	}
    	void InitLog(const char *log_dir)   //打开log_dir目录下的日志文件,如果该文件不存在,则创建它
    	{
    		char filePath[100] = { 0 };
    		time_t now = time(0);
    		tm *ltm = localtime(&now);
    		sprintf(filePath, "%s/%04d-%02d-%02d_%02d%02d%02d.log", log_dir, 1900 + (ltm->tm_year), 1 + (ltm->tm_mon), ltm->tm_mday, ltm->tm_hour, ltm->tm_min, ltm->tm_sec);
    
    		m_fp = fopen(filePath, "a");     //以附加的方式打开只写文件filePath。使用fopen打开的文件是共享的,即使不关闭文件指针,其它文件也可以访问该文件
    		if (NULL == m_fp)
    		{
    			printf("can not open log file: %s/n", filePath);
    		}
    	}
    	void WriteLog(const char *msg ...)   //将信息msg写入到文件指针为m_fp的日志文件中,并打印到控制台上(支持ANSI,格式化输出)
    	{
    		m_mutex.Lock();
    
    		char buf[1024] = { 0 };
    		char t_buf[50] = { 0 };
    		time_t now = time(0);
    		tm *ltm = localtime(&now);
    
    		sprintf(t_buf, "<%d年%d月%d日%d时%d分%d秒>:", 1900 + (ltm->tm_year), 1 + (ltm->tm_mon), ltm->tm_mday, ltm->tm_hour, ltm->tm_min, ltm->tm_sec, msg);
    		printf("%s", t_buf);
    		fprintf(m_fp, "%s", t_buf);
    
    		va_list vp;
    		va_start(vp, msg);
    		vfprintf(stdout, msg, vp);
    		vfprintf(m_fp, msg, vp);
    		va_end(vp);
    		fflush(m_fp);
    		
    		m_mutex.Unlock();
    	}
    private:
    	FILE *m_fp;               //日志文件的文件指针
    	AfMutex m_mutex;          //互斥锁
    };
    
    #endif

     

    main.cpp

    #include "Clog.h"
    #include <stdio.h>
    
    using namespace std;
    
    
    int main()
    {
    	CLog::i()->InitLog("log");
    	char *buf = "xiaoming";
    	CLog::i()->WriteLog("name:%s\n", buf);
    	return 0;
    }

     

    三、运行效果

     

    我们在vs(比如vs2015)中创建一个控制台项目,添加上述代码,在项目目录下新建一个log目录,如下图所示:

     

    然后编译、运行,我们可以发现在控制台中已经可以将日志的信息打印出来了。

     

    在log目录下也根据启动程序的时间生成对应的日志文件了。

     

    四、总结

        本文演示的日志只是一个具有最基本功能的日志。实际在生产环境中使用日志还要考虑很多问题,比如日志的性能,日志是否支持程序故障异常退出时及时保存,是否支持准确的源码溯源,日志输出的级别;如果是在服务器中,还要考虑日志是否支持轮替(服务器不出故障是不重启的,半年一年的日志放到一个文件会导致文件过大)等等。所以在项目中,我们还是优先考虑使用现成的开源库(比如log4cpp、boost.log、spdlog、glog、zlog等),尤其是是公司项目,而不是自己实现日志,自己造的轮子可能不但不完善,还不好用。

     

    五、参考链接

    本文使用的互斥锁的代码部分参考了邵发老师的代码,链接如下:

    https://download.csdn.net/download/iamshaofa/4303875

     

     

    展开全文
  • C++控制台,通过读取系统日志记录获取系统开关机时间并打印到文件
  • C++日志系统log4cxx使用总结

    千次阅读 2018-09-06 13:44:09
     每个logger都被分配了一个日志级别 (log level),用来控制日志信息的输出。未被分配level的 logger将继承它最近的父logger的level。每条输出到logger的日志请求(logging request)也都有一个 level,如果该...

    本文主要从log4cxx级别、layout、格式化、命名规则、Filter几个方面介绍。

     

    一、log4cxx命名规则

            Logger由一个String类的名字识别,logger的名字是大小写敏感的,且名字之间具有继承的关系,子名有父名作为前缀,用点号.分隔。如:x.y是x.y.z的父亲。根logger (root logger)是所有logger的祖先, 它具有如下属性:1) 它总是存在的;2) 它不可以通过名字获得。通过调用public static Logger Logger.getRootLogger()获得root logger;通过调用public static Logger Logger.getLogger(String name)或者public static Logger Logger.getLogger(Class clazz)获得或者创建)一个named logger。后者相当于调用Logger.getLogger(clazz.getName())。在某对象中,用该对象所属的类为参数,调用Logger.getLogger(Class clazz)以获得logger被认为是目前 所知的最理智的命名logger的方法。

     

     

    二、log4cxx Log Level级别介绍

     

         每个logger都被分配了一个日志级别 (log level),用来控制日志信息的输出。未被分配level的 logger将继承它最近的父logger的level。每条输出到logger的日志请求(logging request)也都有一个 level,如果该request的level大于等于该logger的level,则该request将被处理(称为enabled);否则该 request将被忽略。故可得知:1、logger的level越低,表示该logger越详细 2、logging request的 level越高,表示该logging request越优先输出 3、如果没有设置日志记录器(Logger)的级别,那么它将 会继承最近的祖先的级别。因此,如果在包com.foo.bar中创建一个日志记录器(Logger)并且没有设置级 别,那它将会继承在包com.foo中创建的日志记录器(Logger)的级别。如果在com.foo中没有创建日志记录 器(Logger)的话,那么在com.foo.bar中创建的日志记录器(Logger)将继承root 日志记录器(Logger) 的级别,root日志记录器(Logger)经常被实例化而可用,它的级别为DEBUG。

    Level类中预定义了五个level,它们的大小关系如下:Level.ALL < Level.DEBUG < Level.INFO < Level.WARN < Level.ERROR < Level.FATAL < Level.OFF

     

    三、log4cxx(log4j) Log layout介绍

     

    org.apache.log4j.HTMLLayout(以HTML表格形式布局), 

    org.apache.log4j.PatternLayout(可以灵活地指定布局模式), 

    org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串), 

    org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)

     

     

    四、log4cxx Log 格式化信息介绍

     

    Log4J采用类似C语言中的printf函数的打印格式格式化日志信息,打印参数如下: 

    %m 输出代码中指定的消息 

    %p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL 

    %r 输出自应用启动到输出该log信息耗费的毫秒数

    %c 输出所属的类目,通常就是所在类的全名

    %t 输出产生该日志事件的线程名 

    %n 输出一个回车换行符,Windows平台为“rn”,Unix平台为“n”

    %d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyyy MMM dd

    HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921 %l 输出日志事件的发生位置,包括类目名

    、发生的线程,以及在代码中的行数。

     

    五、log4cxx Log appender种类介绍

     

    Log4cXX提供的appender种类:

    org.apache.log4j.ConsoleAppender 控制台

    org.apache.log4j.DailyRollingFileAppender 每天产生一个日志文件

    org.apache.log4j.FileAppender 文件org.apache.log4j.RollingFileAppender 文件大小达到指定尺寸的

    时候产生一个新的文件

     

    六、log4cxx Log Filter介绍

     

    包括选择过滤器和设置过滤条件,可选择的过滤器包括:LogLevelMatchFilter、LogLevelRangeFilter、和StringMatchFilter:

    1、对LogLevelMatchFilter来说,过滤条件包括LogLevelToMatch和AcceptOnMatch(true|false), 只有 当log信息的LogLevel值与LogLevelToMatch相同,且AcceptOnMatch为true时才会匹配。

    2、对LogLevelRangeFilter来说,过滤条件包括LogLevelMin、LogLevelMax和AcceptOnMatch,只有当log信 息的LogLevel在LogLevelMin、LogLevelMax之间同时AcceptOnMatch为true时才会匹配。

    3、对StringMatchFilter来说,过滤条件包括StringToMatch和AcceptOnMatch,只有当log信息的LogLevel 值与StringToMatch对应的LogLevel值与相同, 且AcceptOnMatch为true时会匹配。

     

    七、log4cxx additivity属性介绍

     

    它是 子Logger 是否继承 父Logger 的 输出源(appender) 的标志位。具体说,默认情况下子Logger会继承父Logger的appender,也就是说子Logger会在父Logger的appender里输 出。若是additivity设为false,则子Logger只会在自己的appender里输出,而不会在父Logger的appender里输 出。

     

     

    八、log4cxx中XML文件范例

     请参考:http://blog.sina.com.cn/s/blog_a459dcf501013vh1.html

     

     

    九、log4cxx编译

      请参考:http://blog.sina.com.cn/s/blog_a459dcf501013tbn.html

     http://blog.sina.com.cn/s/blog_a459dcf501013mr1.html

    展开全文
  • C++实现log日志系统

    万次阅读 2018-04-19 14:33:10
    1.log日志的作用在软件开发周期中,不管是前台还是后台,系统一般会采用一个持久化的日志系统来记录运行情况。在代码中嵌入log代码信息,主要记录下列信息:(1)记录系统运行异常信息。(2)记录系统运行状态信息。...
  • 总结_C++日志系统log4cxx使用

    万次阅读 2018-03-29 17:48:44
    C++日志系统log4cxx使用总结参考连接:https://blog.csdn.net/crazyhacking/article/details/9497135http://blog.sina.com.cn/s/blog_a459dcf501013mr1.html一、log4cxx组件介绍log4cxx有三个主要组件:loggers(记录...
  • c++日志产生类

    2013-05-16 22:22:18
    一个很老的c++日志生产类,使用说明: 1.将SAStatusLog.cpp和SAStatusLog.h文件拷贝到自己的工程中 2.包含头文件,定义一个CSAStatusLog的全局变量,像TheApp一样,定义方法参照TheApp。这个日志类中包含了线程同步...
  • C++通用日志系统Logger

    2022-03-25 10:32:32
    日志系统是成熟软件中的一个重要组成部分,其记录软件的使用和运行行为,方便事后进行故障分析、数据统计等。 本日志模块的主旨是提供一个基于Qt框架,简单易用、可拓展性好、支持多种输出目标和格式的底层日志库,...
  • 自己写的c++实现的单例模式日志类。支持多线程、日志等级、可输出每条日志的输出时间(精确到毫秒级),日志级别,支持可变长参数、标准格式化输出。 代码中包含VS2010示例程序,使用方便。
  • C++ 查询系统日志

    千次阅读 2019-08-25 18:25:06
    坑! 有一个大坑,浪费我整整8个小时。...使用 C++ 操作命令行,并接收命令行返回信息,通过 Dos 命令获取 Windows 系统日志。 Wevtutil 命令介绍 文档上介绍的很全,我就不一一列举了,这里只说下我用到的...
  • c++实现的日志打印和记录系统
  • C++ 多线程日志记录

    2018-09-07 12:42:20
    使用该类可实现C++ 多线程日志的记录,创建了日志记录线程,使用日志等级、队列、信号量、临界区等方法实现记录,可在较高的实时性系统上完成日志记录。
  • c++ 日志管理系统

    2020-02-26 11:21:38
    开源的c++日志管理不太好,有的占用空间太大,有的存在内存泄漏. 自己写的 占用空间少。 功能: 1.支持各种格式打印 2.日志分等级 3.设置每个日志的文件大小自动覆盖 4.根据时间(天),自动分割 ...
  • C++高效日志类(库)

    热门讨论 2014-07-03 18:40:20
    前段时间在做毕业设计需要有一个功能模块是写日志,为了配合高性能服务器不拖延服务器的效率所以必须要写出一个十分高效确不太占用系统资源的日志类。 经过参考前辈们写的资料加上自己不断地研究改进,现在完成了一...
  • yum install log4cxx.x86_64 # 库文件 yum install log4cxx-devel.x86_64 # 头文件

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 113,574
精华内容 45,429
关键字:

c++日志系统

c++ 订阅