前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【C++】基础:多种日志输出方式(终端、rdbuf、ros、类库)

【C++】基础:多种日志输出方式(终端、rdbuf、ros、类库)

作者头像
DevFrank
发布2024-07-24 15:08:13
1390
发布2024-07-24 15:08:13
举报
文章被收录于专栏:C++开发学习交流

1. 终端输出日志

在Linux中,可以用tee命令来将终端信息自动保存到txt文件中:

代码语言:javascript
复制
ping baidu.com | tee log.txt

在Windows中,可以用重定向(>)将控制台信息输出到日志,如:

代码语言:javascript
复制
ping baidu.com > log.txt

但重定向后,控制台就不会显示信息了,对于程序调试来说并不友好,因此,可以安装一个UnxUtils,解压后,将/usr/local/wbin添加到环境变量path中,就可以使用如tee一类的命令了。

在这里插入图片描述
在这里插入图片描述

2. rdbuf输出日志

c++中可以使用rdbuf()来将文件信息读入到终端,或将终端信息输出到文件。

一个例子如下:

代码语言:javascript
复制
#include <iostream>
#include <fstream>

void hello();

int main()
{
	static std::ofstream g_log("out.log");  //指定输出到out.log文件
	std::cout.rdbuf(g_log.rdbuf());  //rdbuf()将终端内容输出到文件

	hello();  //执行对应函数后,函数中cout的内容会自动输出到日志文件
}

void hello()
{
	std::cout << "xxx" << std::endl;
}
在这里插入图片描述
在这里插入图片描述

3. ROS日志输出

ros的日志级别有Debug、Info、Warn、Error、Fatal。需要包含头文件:#include <ros/console.h>

ROS_INFO是类似C语言的printf;ROS_INFO_STREAM是类似C++的cout。

在ros程序运行时,默认是不显示debug信息的。如果要查看debug消息,可在代码中设置:

代码语言:javascript
复制
#include <ros/console.h>
 
if( ros::console::set_logger_level(ROSCONSOLE_DEFAULT_NAME, ros::console::levels::Debug) ) {
   ros::console::notifyLoggerLevelsChanged();
}

参考:https://blog.csdn.net/yaked/article/details/123318657

4. 简单的日志类

参考:https://blog.csdn.net/jolin678/article/details/121945066

主要用于Windows VS下输出日志,作者的这个类可以输出到带日期的文件,且可打印代码所在文件路径、函数名、行号。(但有点小错误,下面的是修正后的)

代码如下: mylog.h

代码语言:javascript
复制
#ifndef LogWriter_H
#define LogWriter_H
 
#include <stdio.h>
 
#define LOG_BUFFER_SIZE 1024
#define VS_DEBUG_INFO_SIZE 100000
 
#define MYLOG(str) MyLog::GetInstance().WriteLog((str),__FILE__, __FUNCTION__, __LINE__)
 
#ifdef _WINDOWS
#define LOG_DIR ("./")
void PrintVsDebugInfo(const char* strPrint);
#define VSLOG(str) PrintVsDebugInfo(str)
#else
#define LOG_DIR ("./")
#define VSLOG(str)
#endif
 
 
class MyLog
{
public:
	static MyLog& GetInstance();
	MyLog(const MyLog&) = delete;
	MyLog(MyLog&&) = delete;
	MyLog& operator=(const MyLog&) = delete;
	MyLog& operator=(MyLog&&) = delete;
 
	bool WriteLog(const char* bufLog, const char* bufFile, const char* bufFunc, int line);
 
private:
	MyLog();
	~MyLog(void);
 
	void GetDateTimeNoMs(char* bufTime, const char *bufFormat);
	void GetDateTime(char* bufTime);
	void GetDate(char* bufDate);
 
private:
	FILE* m_fp;
	char m_bufFilePath[LOG_BUFFER_SIZE];
};
 
#endif

mylog.cpp

代码语言:javascript
复制
//#include "stdafx.h"
#include "mylog.h"
#include <time.h>
#include <string.h>
 
#define WIN32
#include <time.h>
#ifdef WIN32
#include <windows.h>
#include <atlconv.h>
#else
#include <sys/time.h>
#endif
 
 
#pragma warning(disable:4996)
//#define LOG_CODE_DETAIL
 
 
/*char strBuffer[4096] = { 0 };
va_list vlArgs;
va_start(vlArgs, strPrint);
_vsnprintf_s(strBuffer, sizeof(strBuffer) - 1, strPrint, vlArgs);
va_end(vlArgs);*/
 
#ifdef WIN32
void PrintVsDebugInfo(const char* strPrint)
{
	char bufLogData[VS_DEBUG_INFO_SIZE] = { 0 };
	snprintf(bufLogData, VS_DEBUG_INFO_SIZE - 1, "VS_DEBUG:\t%s\n", strPrint);
#ifdef UNICODE
	USES_CONVERSION;
	OutputDebugString(A2W(bufLogData));
#else
	OutputDebugString(bufLogData);
#endif
}
#endif 
 
MyLog::MyLog()
{
#ifdef APP_RELEASE
	return;
#endif
	char bufFileName[100] = { 0 };
	char bufDate[100] = { 0 };
	//GetDate(bufDate);
	GetDateTimeNoMs(bufDate, "%04d-%02d-%02d %02d%02d%02d");//以时间点来命名,使得每次程序运行都生成新的日志文件
	snprintf(bufFileName, 100, "%s.log", bufDate);
	memset(m_bufFilePath, 0, LOG_BUFFER_SIZE);
	strncat(m_bufFilePath, LOG_DIR, 200);
	strncat(m_bufFilePath, bufFileName, 200);
	printf(m_bufFilePath);
	m_fp = fopen(m_bufFilePath, "a+");
}
 
MyLog::~MyLog(void)
{
	if (m_fp)
	{
		fclose(m_fp);
		m_fp = NULL;
	}
}
 
MyLog& MyLog::GetInstance()
{
	static MyLog objLog;
	return objLog;
}
 
bool MyLog::WriteLog(const char* bufLog, const char* bufFile, const char* bufFunc, int line)
{
#ifdef APP_RELEASE
	return;
#endif
 
	if (m_fp == NULL)
		return false;
 
	char bufTime[100] = { 0 };
	GetDateTime(bufTime);
 
	char bufLogData[LOG_BUFFER_SIZE] = { 0 };
#ifdef LOG_CODE_DETAIL
	snprintf(bufLogData, LOG_BUFFER_SIZE, "%s    %s\\%s\\Line_%d    %s\n", bufTime, bufFile, bufFunc, line, bufLog);
	fwrite(bufLogData, strlen(bufLogData), 1, m_fp);
#else
	snprintf(bufLogData, LOG_BUFFER_SIZE, "%s    %s\n", bufTime, bufLog);
	fwrite(bufLogData, strlen(bufLogData), 1, m_fp);
#endif
 
	fflush(m_fp);
	return true;
}
 
void MyLog::GetDateTime(char* bufTime)
{
#ifdef WIN32
	SYSTEMTIME sysTm;
	GetLocalTime(&sysTm);
	snprintf(bufTime, LOG_BUFFER_SIZE, "%04d-%02d-%02d %02d:%02d:%02d:%03ld", sysTm.wYear, sysTm.wMonth, sysTm.wDay
		, sysTm.wHour, sysTm.wMinute, sysTm.wSecond, sysTm.wMilliseconds);
#else
	struct timeval    tv;
	struct timezone tz;
	struct tm* curTime;
	gettimeofday(&tv, &tz);
	curTime = localtime(&tv.tv_sec);
	snprintf(bufTime, LOG_BUFFER_SIZE, "%04d-%02d-%02d %02d:%02d:%02d:%03ld", curTime->tm_year + 1900
		, curTime->tm_mon + 1, curTime->tm_mday, curTime->tm_hour, curTime->tm_min, curTime->tm_sec, tv.tv_usec / 1000);
#endif
}
 
void MyLog::GetDateTimeNoMs(char* bufTime, const char* bufFormat)
{
#ifdef WIN32
	SYSTEMTIME sysTm;
	GetLocalTime(&sysTm);
	snprintf(bufTime, LOG_BUFFER_SIZE, bufFormat, sysTm.wYear, sysTm.wMonth, sysTm.wDay
		, sysTm.wHour, sysTm.wMinute, sysTm.wSecond);
#else
	struct timeval    tv;
	struct timezone tz;
	struct tm* curTime;
	gettimeofday(&tv, &tz);
	curTime = localtime(&tv.tv_sec);
	snprintf(bufTime, LOG_BUFFER_SIZE, bufFormat, curTime->tm_year + 1900
		, curTime->tm_mon + 1, curTime->tm_mday, curTime->tm_hour, curTime->tm_min, curTime->tm_sec);
#endif
}
 
void MyLog::GetDate(char* bufDate)
{
#ifdef WIN32
	SYSTEMTIME sysTm;
	GetLocalTime(&sysTm);
	snprintf(bufDate, LOG_BUFFER_SIZE, "%04d-%02d-%02d", sysTm.wYear, sysTm.wMonth, sysTm.wDay);
#else
	struct timeval    tv;
	struct timezone tz;
	struct tm* curTime;
	gettimeofday(&tv, &tz);
	curTime = localtime(&tv.tv_sec);
	snprintf(bufDate, LOG_BUFFER_SIZE, "%04d-%02d-%02d", curTime->tm_year + 1900, curTime->tm_mon + 1, curTime->tm_mday);
#endif
}

main.cpp

代码语言:javascript
复制
#include "MyLog.h"
 

int main()
{
	MYLOG("test log 1");
	MYLOG("test log 2");
	MYLOG("test log 3");
	VSLOG("vs debug");	//输出窗口
	return 0;
}

实现效果如下:

在这里插入图片描述
在这里插入图片描述

5. log4cpp库管理日志

log4cpp库可以管理c++程序的日志。

常见的日志等级:

地址:https://sourceforge.net/projects/log4cpp/files/

用VS2017打开msvc10中的sln。

在这里插入图片描述
在这里插入图片描述

若出现:无法打开输入文件“Debug\NTEventLogCategories.res” 解决方法:在log4cpp项目工程中找到NTEventLogCategories.mc,右键选择属性,在弹出窗口中找到“配置属性–>自定义生成工具–>常规–>命令行”中修改编译命令,设置为如下命令:

代码语言:javascript
复制
if not exist $(OutDir) md $(OutDir)
mc.exe -h $(OutDir) -r $(OutDir) $(ProjectDir)..\%(Filename).mc
RC.exe -r -fo $(OutDir)%(Filename).res $(OutDir)%(Filename).rc
link.exe /MACHINE:IX86 -dll -noentry -out:$(OutDir)NTEventLogAppender.dll $(OutDir)%(Filename).res
在这里插入图片描述
在这里插入图片描述

若出现:int snprintf(char* const,const size_t,const char*const,…)已有主体的报警。 解决方法:由于log4cpp中对snprintf进行了重新实现,VS的c库对snprintf也有实现,windows中在链接时会报snprintf函数冲突,所以需要设置log4cpp的预编译项,选择使用VS中c库的实现,在log4cpp工程上右键属性,在”配置属性–>C/C++->预处理器–>预处理器定义"中增加一条预处理定义:

代码语言:javascript
复制
HAVE_SNPRINTF
在这里插入图片描述
在这里插入图片描述

选择Release x64编译完成后,会出现dll和lib两个文件。

在这里插入图片描述
在这里插入图片描述

使用时,只需在头文件引用include,库文件引用生成的库文件即可。

示例程序1——输出时间戳:

代码语言:javascript
复制
#include <iostream>
#include "log4cpp/Category.hh"
#include "log4cpp/FileAppender.hh"
#include "log4cpp/OstreamAppender.hh"
#include "log4cpp/BasicLayout.hh"

#pragma comment(lib, "log4cpp.lib")

int main(int argc, char *argv[])
{
	// 1实例化一个layout 对象
	log4cpp::Layout *layout = new log4cpp::BasicLayout();   // 有不同的layout
															// 2. 初始化一个appender 对象
	log4cpp::Appender *appender = new log4cpp::FileAppender("FileAppender",
		"./test_log4cpp1.log");
	log4cpp::Appender *osappender = new log4cpp::OstreamAppender("OstreamAppender",
		&std::cout);
	// 3. 把layout对象附着在appender对象上
	appender->setLayout(layout);
	// appender->addLayout 没有addLayout,一个layout格式样式对应一个appender

	// 4. 实例化一个category对象
	log4cpp::Category &warn_log =
		log4cpp::Category::getInstance("LogTest");   // 是一个单例工厂
													// 5. 设置additivity为false,替换已有的appender
	warn_log.setAdditivity(false);

	// 5. 把appender对象附到category上
	warn_log.setAppender(appender);
	warn_log.addAppender(osappender);

	// 6. 设置category的优先级,低于此优先级的日志不被记录
	warn_log.setPriority(log4cpp::Priority::INFO);
	// 记录一些日志
	warn_log.info("Program info which cannot be wirten, darren = %d", 100);
	warn_log.debug("This debug message will fail to write");
	warn_log.alert("Alert info");

	warn_log.info("----------hello---------");

	// 其他记录日志方式
	warn_log.log(log4cpp::Priority::WARN, "This will be a logged warning, darren = %d", 100);


	log4cpp::Priority::PriorityLevel priority;
	bool this_is_critical = true;
	if (this_is_critical)
		priority = log4cpp::Priority::CRIT;
	else
		priority = log4cpp::Priority::DEBUG;
	warn_log.log(priority, "Importance depends on context");

	warn_log.critStream() << "This will show up << as "
		<< 1 << " critical message";

	// clean up and flush all appenders
	log4cpp::Category::shutdown();
	return 0;
}

实现效果如下:

在这里插入图片描述
在这里插入图片描述

示例程序2——输出日期时间:

代码语言:javascript
复制
#include <iostream>
#include <log4cpp/Category.hh>
#include <log4cpp/OstreamAppender.hh>
#include <log4cpp/Priority.hh>
#include <log4cpp/PatternLayout.hh>
using namespace std;

int main(int argc, char* argv[])
{
   log4cpp::OstreamAppender* osAppender = new log4cpp::OstreamAppender("osAppender", &cout);
    
    log4cpp::PatternLayout* pLayout = new log4cpp::PatternLayout();
    pLayout->setConversionPattern("%d: %p %c %x: %m%n");
    osAppender->setLayout(pLayout);
        
    log4cpp::Category& root = log4cpp::Category::getRoot();
    log4cpp::Category& infoCategory = root.getInstance("infoCategory");
    infoCategory.addAppender(osAppender);
    infoCategory.setPriority(log4cpp::Priority::INFO);

    infoCategory.info("system is running");
    infoCategory.warn("system has a warning");
    infoCategory.error("system has a error, can't find a file");
    infoCategory.fatal("system has a fatal error,must be shutdown");
    infoCategory.info("system shutdown,you can find some information in system log");

    log4cpp::Category::shutdown();
    
    return 0;
}
在这里插入图片描述
在这里插入图片描述

更多使用:

代码语言:javascript
复制
/*
    log4cpp::FileAppender                       // 输出到文件
    log4cpp::RollingFileAppender                // 输出到回卷文件,即当文件到达某个大小后回卷
    log4cpp::OstreamAppender                    // 输出到一个ostream类
    log4cpp::StringQueueAppender                // 内存队列
    log4cpp::Win32DebugAppender                 // 发送到缺省系统调试器
    log4cpp::NTEventLogAppender                 // 发送到win 事件日志
*/

以上。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-01-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 终端输出日志
  • 2. rdbuf输出日志
  • 3. ROS日志输出
  • 4. 简单的日志类
  • 5. log4cpp库管理日志
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档