背景
项目需求:
- 要打印日志到特定模块、特定文件,并打印相应时间戳到文件中。
- 日志开关,可以通过开关关闭、打开。
- 代码可以通过开关关闭、打开。(代码直接不进行编译)
需求分析:
- 日志打印涉及可变参数,涉及log 等级,打印到模块-文件可以通过fstream实现。
- 日志开关,通过宏开关实现。
- 代码编译开关,也可以通过宏实现。
代码实现
log_test.h
#ifndef LOG_TEST_H_
#define LOG_TEST_H_
#include <string>
#include <fstream>
namespace my_log_test {
//log switch
#define MY_LOG_DEBUG 1
//log write to output console
#define MY_LOG_OUTPUT_CONSOLE 1
//log buffer size
#define MY_LOG_BUFFER_SIZE 512
constexpr const char* g_testDirPath { "./testDir/" };
typedef enum {
LOG_FATAL = 0,
LOG_ERROR = 1,
LOG_WARN = 2,
LOG_INFO = 3,
LOG_UNKNOW
} MY_LOG_LEVEL;
class MyLogTest {
public:
MyLogTest(std::string fileName, std::string moduleName = "");
~MyLogTest();
static void LogPrint(MY_LOG_LEVEL level, const char* pcFunc, const int& line, const char* fmt, ...);
private:
std::ofstream writeOf;
bool fsOpenFlag {false};
}; //end of class MyLogTest
//日志打印函数,可变参数。可通过宏开关,整个代码不编译
#ifdef MY_LOG_DEBUG
#ifndef MY_LOGERR
#define MY_LOGERR(fmt, args...) MyLogTest::LogPrint(LOG_ERROR, __FUNCTION__, __LINE__, fmt, ## args)
#endif
#else
#define MY_LOGERR(fmt, args...) ((void)0)
#endif
//日志打印函数,可变参数。可通过宏开关,整个代码不编译
#ifdef MY_LOG_DEBUG
#ifndef MY_LOGWAR
#define MY_LOGWAR(fmt, args...) MyLogTest::LogPrint(LOG_WARN, __FUNCTION__, __LINE__, fmt, ## args)
#endif
#else
#define MY_LOGWAR(fmt, args...) ((void)0)
#endif
//类使用宏,可通过宏开关,整个代码不编译
#ifdef MY_LOG_DEBUG
#ifndef DefineMyLogTest
#define DefineMyLogTest(fileName, moduleName) \
MyLogTest myLogTest(fileName, moduleName)
#endif
#else
#define DefineMyLogTest(fileName, moduleName) ((void)0)
#endif
}//end of namespace my_log_test
#endif //LOG_TEST_H_
log_test.cpp
#include <iostream>
#include <fstream>
//获取系统时间,可变参数
#include <stdlib.h>
#include <time.h>
#include <stdarg.h>
#include "log_test.h"
namespace my_log_test {
void * BaseMyLogTest { nullptr };
//获取当前系统时间
void gettime(std::string *date_time)
{
time_t rawtime;
struct tm *ptminfo;
char buf[32];
time(&rawtime);
ptminfo = localtime(&rawtime);
strftime(buf, 32, "%Y-%m-%d %H:%M:%S", ptminfo);
*date_time = buf;
//std::cout<<"date-time: " <<buf<<std::endl;
}
MyLogTest::MyLogTest(std::string fileName, std::string moduleName)
{
if( fileName.empty()) {
std::string file {__FILE__};
if(file.find(".cpp")) {
fileName = file.substr(0, file.size()-5);
}
}
if(!moduleName.empty()) {
fileName = moduleName + "-" + fileName;
}
BaseMyLogTest = (void*)this;
fileName = g_testDirPath + fileName;
writeOf.open(fileName, std::ios::out);
if(writeOf.fail()) {
std::cout<<__FUNCTION__<<" open file failed: "<< fileName <<std::endl;
fsOpenFlag = false;
}else {
fsOpenFlag = true;
}
if(fsOpenFlag) {
std::string date_time {""};
gettime(&date_time);
std::cout<<__FUNCTION__<<" "<<date_time << std::endl;
writeOf <<"My log test Begin date-time: "<<date_time<<std::endl;
}
}
MyLogTest::~MyLogTest()
{
if(fsOpenFlag) {
writeOf.close();
}
}
void MyLogTest::LogPrint(MY_LOG_LEVEL level, const char*pcFunc, const int& line, const char* fmt, ...)
{
char buffer[MY_LOG_BUFFER_SIZE];
int n = sprintf(buffer, "[%s]-[%d]", pcFunc, line);
va_list vap;
va_start(vap, fmt);
vsnprintf(buffer + n, MY_LOG_BUFFER_SIZE-n, fmt, vap);
va_end(vap);
std::string logLevelStr {""};
switch(level) {
case LOG_ERROR:
logLevelStr = "Error";
break;
case LOG_WARN:
logLevelStr = "Warn";
break;
default:
logLevelStr = "Error";
break;
}
std::string dt {""};
gettime(&dt);
#ifdef MY_LOG_OUTPUT_CONSOLE
std::cout<<dt <<" [" <<logLevelStr << "]: " <<buffer << std::endl;
#else
MyLogTest *myLogTest = (MyLogTest*)BaseMyLogTest ;
myLogTest->writeOf <<dt <<" [" <<logLevelStr << "]: " <<buffer << std::endl;
#endif
}
}//end of namespace my_log_test
main.cpp
#include <iostream>
#include "log_test.h"
using namespace my_log_test;
int main(int argc, char* argv[])
{
std::cout<< __FUNCTION__ <<" Begin of the program!"<< std::endl;
DefineMyLogTest("test_file", "test_module");
std::string strErr {"string error!"};
std::string strWar {"string waring!"};
MY_LOGERR("my log test error: %s", strErr.c_str());
MY_LOGWAR("my log test warn: %s", strWar.c_str();
std::cout<< __FUNCTION__ <<" End of the program!"<< std::endl;
return 0;
}
代码测试
当前代码在 ubuntu 系统上运行,通过下面命令进行编译。
g++ -std=c++11 log_test.cpp main.cpp -o test
在当前目录创建 ”testDir“ 文件夹,执行 ./test
即会在终端打印部分日志,并在文件 ./testDir/test_module_test_file 中写入日志,及时间戳。