在Linux中,可以用tee
命令来将终端信息自动保存到txt文件中:
ping baidu.com | tee log.txt
在Windows中,可以用重定向(>)
将控制台信息输出到日志,如:
ping baidu.com > log.txt
但重定向后,控制台就不会显示信息了,对于程序调试来说并不友好,因此,可以安装一个UnxUtils,解压后,将/usr/local/wbin
添加到环境变量path
中,就可以使用如tee
一类的命令了。
c++中可以使用rdbuf()
来将文件信息读入到终端,或将终端信息输出到文件。
一个例子如下:
#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;
}
ros的日志级别有Debug、Info、Warn、Error、Fatal
。需要包含头文件:#include <ros/console.h>
ROS_INFO
是类似C语言的printf;ROS_INFO_STREAM
是类似C++的cout。
在ros程序运行时,默认是不显示debug信息的。如果要查看debug消息,可在代码中设置:
#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
参考:https://blog.csdn.net/jolin678/article/details/121945066
主要用于Windows VS下输出日志,作者的这个类可以输出到带日期的文件,且可打印代码所在文件路径、函数名、行号。(但有点小错误,下面的是修正后的)
代码如下: mylog.h
#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
//#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
#include "MyLog.h"
int main()
{
MYLOG("test log 1");
MYLOG("test log 2");
MYLOG("test log 3");
VSLOG("vs debug"); //输出窗口
return 0;
}
实现效果如下:
log4cpp库可以管理c++程序的日志。
常见的日志等级:
地址:https://sourceforge.net/projects/log4cpp/files/
用VS2017打开msvc10中的sln。
若出现:无法打开输入文件“Debug\NTEventLogCategories.res”
解决方法:在log4cpp项目工程中找到NTEventLogCategories.mc
,右键选择属性,在弹出窗口中找到“配置属性–>自定义生成工具–>常规–>命令行
”中修改编译命令,设置为如下命令:
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++->预处理器–>预处理器定义
"中增加一条预处理定义:
HAVE_SNPRINTF
选择Release x64编译完成后,会出现dll和lib两个文件。
使用时,只需在头文件引用include,库文件引用生成的库文件即可。
示例程序1——输出时间戳:
#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——输出日期时间:
#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;
}
更多使用:
/*
log4cpp::FileAppender // 输出到文件
log4cpp::RollingFileAppender // 输出到回卷文件,即当文件到达某个大小后回卷
log4cpp::OstreamAppender // 输出到一个ostream类
log4cpp::StringQueueAppender // 内存队列
log4cpp::Win32DebugAppender // 发送到缺省系统调试器
log4cpp::NTEventLogAppender // 发送到win 事件日志
*/
以上。