linux c/c++日志输出
介绍一个在linux下c++开发的一个日志输出的小功能模块,在下面的几个文件当中,代码的设计思路值得我们好好的借鉴
1 /* 2 * ILogger.h 3 * 4 * Created on: 2016��1��7�� 5 * Author: Administrator 6 */ 7 8 #ifndef FRAME_LOG_LOGGER_H_ 9 #define FRAME_LOG_LOGGER_H_ 10 #include <iostream> 11 #include "Mutex.h" 12 #include<fstream> 13 #include<string> 14 15 using namespace std; 16 17 const int MaxFilePathLen = 1024; 18 const char PathSplitChar = '/'; 19 namespace FrameWork { 20 enum LogLevel{ 21 /// <summary> 22 /// 调试 23 /// </summary> 24 DEBUG = -1, 25 /// <summary> 26 /// 普通日志 27 /// </summary> 28 INFO = 0, 29 /// <summary> 30 /// 警告 31 /// </summary> 32 WARN, 33 /// <summary> 34 /// 错误 35 /// </summary> 36 ERROR, 37 /// <summary> 38 /// 崩溃 39 /// </summary> 40 FATAL, 41 /// <summary> 42 /// 超出错误级别 43 /// </summary> 44 OFF 45 }; 46 47 48 class ILogger { 49 public: 50 // 51 virtual ~ILogger() { 52 } 53 54 //利用宏定义,来简化代码输出,值得推荐 55 #define ABSTRACT_LOG_FUNC(name) \ 56 virtual void name(string msg)=0; \ 57 virtual void name(const char* fmt,...)=0; 58 59 ABSTRACT_LOG_FUNC(Debug) 60 ABSTRACT_LOG_FUNC(Info) 61 ABSTRACT_LOG_FUNC(Warn) 62 ABSTRACT_LOG_FUNC(Error) 63 ABSTRACT_LOG_FUNC(Fatal) 64 65 #undef ABSTRACT_LOG_FUNC 66 #define ABSTRACT_LOG_FUNC_X(name) \ 67 virtual void name(LogLevel lv,string msg)=0; \ 68 virtual void name(LogLevel lv,const char* fmt,...)=0;\ 69 virtual void name(const char* file,int line,LogLevel lv,string msg)=0;\ 70 virtual void name(const char* file,int line,LogLevel lv,const char* fmt,...)=0; 71 ABSTRACT_LOG_FUNC_X(Log) 72 73 #undef LOG_FUNC_X 74 }; 75 76 class Logger: public ILogger { 77 std::string logPath; 78 std::string logPrefix; 79 std::fstream logFile; 80 LogLevel level; 81 Mutex mutex; 82 83 Logger(LogLevel level, char * folder, char * prefix); 84 85 public: 86 static Logger& GetInstance(); 87 static Logger* GetInstancePtr(); 88 virtual ~Logger(); 89 inline fstream & stream() { 90 return logFile; 91 } 92 93 #define DECLARE_LOG_FUNC(name) \ 94 virtual void name(string msg); \ 95 virtual void name(const char* fmt,...); 96 97 #define DECLARE_LOG_FUNC_X(name) \ 98 virtual void name(LogLevel lv,string msg); \ 99 virtual void name(LogLevel lv,const char* fmt,...);\ 100 virtual void name(const char* file,int line,LogLevel lv,string msg);\ 101 virtual void name(const char* file,int line,LogLevel lv,const char* fmt,...); 102 103 DECLARE_LOG_FUNC(Debug) 104 DECLARE_LOG_FUNC(Info) 105 DECLARE_LOG_FUNC(Warn) 106 DECLARE_LOG_FUNC(Error) 107 DECLARE_LOG_FUNC(Fatal) 108 109 DECLARE_LOG_FUNC_X(Log) 110 111 #undef DECLARE_LOG_FUNC_X 112 #undef DECLARE_LOG_FUNC 113 114 }; 115 116 class LogMessage { 117 Logger* logger; 118 static Mutex mutex; 119 public: 120 LogMessage(const char* file, int line, LogLevel lv); 121 LogMessage(LogLevel lv); 122 ostream& stream() { 123 return logger->stream(); 124 } 125 virtual ~LogMessage(); 126 }; 127 128 void InitLogging(const char* filename, LogLevel minlevel, 129 const char* destFolder); 130 void CloseLogging(); 131 132 #define LOG(level) LogMessage(__FILE__, __LINE__,level).stream() 133 134 #define LOGFILE(level) LogMessage(level).stream() 135 136 #define LOG_IF(severity, condition) \ 137 !(condition) ? (void) 0 : LOG(severity) 138 #define LOG_ASSERT(condition) \ 139 LOG_IF(FATAL, !(condition)) << "Assert failed: " #condition 140 #define CHECK(condition) \ 141 LOG_IF(FATAL, !(condition)) \ 142 << "Check failed: " #condition " " 143 144 } /* namespace FrameWork */ 145 146 #endif /* FRAME_LOG_LOGGER_H_ */
Logger的实现,在文件内定义了输助函数的使用
1 /* 2 * Logger.cpp 3 * 4 * Author: liu xw 5 */ 6 7 #include "Logger.h" 8 #include<cstring> 9 #include<time.h> 10 #include<cstdarg> 11 #include<cstdlib> 12 #include<assert.h> 13 #include "FileHelper.h" 14 #include "Mutex.h" 15 namespace FrameWork { 16 Mutex LogMessage::mutex; 17 static char _defaltFolder[]="/var/tmp/"; 18 static char _appName[MaxFilePathLen]; 19 static char _appFolder[MaxFilePathLen]; 20 static char _destFolder[MaxFilePathLen]; 21 static char _destPrefix[MaxFilePathLen]; 22 static LogLevel _destLevel; 23 static char _levelInfos[][16]={ 24 "Debug","Info","Warn","Error","Fatal" 25 }; 26 const int BUFFER_SIZE = 8196; 27 static char _gBuffer[BUFFER_SIZE]; 28 void combine_folder(char** destpath, char* basefolder,char* relativefolder) 29 { 30 int lenb = strlen(basefolder); 31 int lenr = strlen(relativefolder); 32 char* pret = (char*)malloc((lenb+lenr+1)*sizeof(char)); 33 int pos=lenb-1; 34 memset(pret,0,lenb+lenr+1); 35 while(pos>0 && ( basefolder[pos]!='/')) 36 pos--; 37 strncpy(*destpath,basefolder,pos+1); 38 if(relativefolder[0] == '\\' || relativefolder[0] == '/'){ 39 strncpy(*destpath+pos+1,relativefolder+1,lenr-1); 40 }else{ 41 strncpy(*destpath+pos+1,relativefolder,lenr); 42 } 43 } 44 45 static void InitPaths(const char* filename,const char* destFolder) 46 { 47 memset(_appName,0,MaxFilePathLen); 48 memset(_appFolder,0,MaxFilePathLen); 49 memset(_destFolder,0,MaxFilePathLen); 50 memset(_destPrefix,0,MaxFilePathLen); 51 52 strcpy(_appName,filename); 53 int len = strlen(filename),lend; 54 int pos = len-1,posd,start; 55 while(pos >0 && filename[pos] != PathSplitChar) 56 pos--; 57 strncpy(_appFolder,filename,pos+1); 58 lend = strlen(destFolder); 59 posd = lend-1; 60 if(destFolder[lend-1] != PathSplitChar) { 61 //has prefix 62 while(posd >0 && destFolder[posd] != PathSplitChar) 63 posd--; 64 } 65 if(destFolder[0] == '.' && destFolder[1] == PathSplitChar){ 66 strncpy(_destFolder,filename,pos+1); 67 start = 2; 68 } else{ 69 pos = 8; 70 strcpy(_destFolder,_defaltFolder); 71 if(destFolder[0] != PathSplitChar){ 72 start = 0; 73 }else{ 74 start = 1; 75 } 76 } 77 strncpy(_destFolder+pos+1,destFolder+start,posd-start+1); 78 strncpy(_destPrefix,filename,pos+1); 79 strncpy(_destPrefix+pos+1,destFolder+start,lend-start); 80 } 81 82 void InitLogging(const char* filename,LogLevel minlevel,const char* destFolder) 83 { 84 InitPaths(filename,destFolder); 85 _destLevel = minlevel; 86 } 87 88 89 90 static string GetLocalDate(void) 91 { 92 time_t t = time(0); 93 tm *ld; 94 char tmp[64] = ""; 95 ld=localtime(&t); 96 strftime(tmp,sizeof(tmp),"%Y-%m-%d",ld); 97 return string(tmp); 98 } 99 static string GetCurTime(void) 100 { 101 time_t t = time(0); 102 tm *ld; 103 char tmp[64] = ""; 104 ld=localtime(&t); 105 strftime(tmp,sizeof(tmp),"%Y-%m-%d %H:%M:%S",ld); 106 return string(tmp); 107 } 108 109 Logger::Logger(LogLevel level,char * folder,char * prefix) 110 :level(level) 111 { 112 std::string path; 113 path.append(prefix); 114 path.append(GetLocalDate()); 115 path.append(".log"); 116 FileHelper::CreateDir(folder); 117 logPrefix.append(prefix); 118 logPath = path; 119 logFile.open(path.c_str(),ios::app|ios::out); 120 logFile<<"Log file created at:"<<GetCurTime()<<endl; 121 } 122 123 Logger::~Logger() { 124 logFile.close(); 125 } 126 127 #define IMPLEMENT_LOG_FUNC1(cname,fname,lv) \ 128 void cname::fname(string msg) {\ 129 if(level <= lv){\ 130 WriterMutexLock lock(&mutex);\ 131 logFile<<"["<<GetCurTime().c_str()<<"][" #lv "]"<<msg.c_str()<<endl;\ 132 logFile.flush();\ 133 }\ 134 } 135 136 #define PRINT_ARGS_TO_BUFFER(fmt,buf) \ 137 {\ 138 memset(buf,0,sizeof(buf));\ 139 va_list argp;\ 140 va_start(argp,fmt);\ 141 vsprintf(buf,fmt,argp);\ 142 va_end(argp);\ 143 } 144 145 #define IMPLEMENT_LOG_FUNC2(cname,fname,lv) \ 146 void cname::fname(const char* format,...) {\ 147 if(level <= lv){\ 148 WriterMutexLock lock(&mutex);\ 149 PRINT_ARGS_TO_BUFFER(format,_gBuffer)\ 150 logFile<<"["<<GetCurTime().c_str()<<"][" #lv "]"<<_gBuffer<<endl;\ 151 logFile.flush();\ 152 }\ 153 } 154 155 156 #define IMPLEMENT_LOG_FUNC(cname,fname,lv) \ 157 IMPLEMENT_LOG_FUNC1(cname,fname,lv)\ 158 IMPLEMENT_LOG_FUNC2(cname,fname,lv) 159 160 IMPLEMENT_LOG_FUNC(Logger,Debug,DEBUG) 161 IMPLEMENT_LOG_FUNC(Logger,Info,INFO) 162 IMPLEMENT_LOG_FUNC(Logger,Warn,WARN) 163 IMPLEMENT_LOG_FUNC(Logger,Error,ERROR) 164 IMPLEMENT_LOG_FUNC(Logger,Fatal,FATAL) 165 166 Logger& Logger::GetInstance() { 167 static Logger _logger(_destLevel,_destFolder,_destPrefix); 168 return _logger; 169 } 170 171 void Logger::Log(LogLevel lv, string msg) { 172 if(level <= lv){ 173 WriterMutexLock lock(&mutex); 174 logFile<<"["<<GetCurTime().c_str()<<"]["<<_levelInfos[lv+1]<<"]"<<msg.c_str()<<endl; 175 logFile.flush(); 176 } 177 } 178 179 void Logger::Log(LogLevel lv, const char* format,...) { 180 if(level <= lv){ 181 WriterMutexLock lock(&mutex); 182 PRINT_ARGS_TO_BUFFER(format,_gBuffer) 183 logFile<<"["<<GetCurTime().c_str()<<"]["<<_levelInfos[lv+1]<<"]"<<_gBuffer<<endl; 184 logFile.flush(); 185 } 186 } 187 188 void Logger::Log(const char* file, int line, LogLevel lv, string msg) { 189 if(level <= lv){ 190 WriterMutexLock lock(&mutex); 191 logFile<<"["<<GetCurTime().c_str()<<"]["<<_levelInfos[lv+1]<<"]["<<file<<"]["<<line<<"]"<<msg.c_str(); 192 logFile.flush(); 193 } 194 } 195 196 Logger* Logger::GetInstancePtr() { 197 return &GetInstance(); 198 } 199 200 void Logger::Log(const char* file, int line, LogLevel lv, const char* format,...) { 201 if(level <= lv){ 202 WriterMutexLock lock(&mutex); 203 PRINT_ARGS_TO_BUFFER(format,_gBuffer) 204 logFile<<"["<<GetCurTime().c_str()<<"]["<<_levelInfos[lv+1]<<"]["<<file<<"]["<<line<<"]"<<_gBuffer; 205 logFile.flush(); 206 } 207 } 208 209 LogMessage::LogMessage(const char* file, int line, LogLevel lv) { 210 logger = Logger::GetInstancePtr(); 211 mutex.Lock(); 212 logger->Log(file,line,lv,""); 213 } 214 215 LogMessage::LogMessage(LogLevel lv) { 216 logger = Logger::GetInstancePtr(); 217 mutex.Lock(); 218 logger->Log(lv,""); 219 } 220 221 LogMessage::~LogMessage() { 222 logger->stream()<<endl; 223 logger->stream().flush(); 224 mutex.Unlock(); 225 } 226 227 } /* namespace FrameWork */
读写文件类:FileHelper
1 /* 2 * FileHelper.h 3 * 4 * Created on: 2013年9月10日 5 * Author: Administrator 6 */ 7 8 #ifndef FILEHELPER_H_ 9 #define FILEHELPER_H_ 10 #include <string> 11 #include <vector> 12 #include <fstream> 13 #include <stdio.h> 14 #ifdef _WIN32 15 #include <direct.h> 16 #include <io.h> 17 #else 18 #include <stdarg.h> 19 #include <sys/stat.h> 20 #endif 21 22 namespace FrameWork { 23 #ifdef _WIN32 24 #define ACCESS _access 25 #define MKDIR(a) _mkdir((a)) 26 #else 27 #define ACCESS access 28 #define MKDIR(a) mkdir((a),0755) 29 #endif 30 31 class FileHelper { 32 public: 33 static bool save(const std::string filename, std::string& content) { 34 FILE *file = fopen(filename.c_str(), "wb"); 35 36 if (file == NULL) 37 return false; 38 fwrite(content.c_str(), sizeof(char), content.size(), file); 39 fclose(file); 40 return true; 41 } 42 43 // used to open binary file 44 static bool open(const std::string filename, std::string& content) { 45 FILE *file = fopen(filename.c_str(), "rb"); 46 47 if (file == NULL) 48 return false; 49 50 fseek(file, 0, SEEK_END); 51 int len = ftell(file); 52 rewind(file); 53 content.clear(); 54 char *buffer = new char[len]; 55 fread(buffer, sizeof(char), len, file); 56 content.assign(buffer, len); 57 delete[] buffer; 58 59 //int nRead; 60 //content.clear(); 61 //char buffer[80]; 62 //while(!feof(file)){ 63 // nRead = fread(buffer,sizeof(char),sizeof(buffer),file); 64 // if(nRead > 0){ 65 // content.append(buffer); 66 // } 67 //} 68 fclose(file); 69 return true; 70 } 71 72 // used to open text file 73 static bool open(const std::string file_name, 74 std::vector<std::string>& lines) { 75 std::ifstream file(file_name.c_str(), std::ios::in); 76 if (!file) { 77 return false; 78 } 79 80 lines.clear(); 81 char buffer[BUFFER_SIZE]; 82 83 while (file.getline(buffer, BUFFER_SIZE, '\n')) { 84 lines.push_back(buffer); 85 } 86 87 return true; 88 } 89 static bool CreateDir(const char *pszDir) { 90 size_t i = 0; 91 size_t iRet; 92 size_t iLen = strlen(pszDir); 93 char* buf = new char[iLen + 1]; 94 strncpy(buf, pszDir, iLen + 1); 95 for (i = 0; i < iLen; i++) { 96 if (pszDir[i] == '\\' || pszDir[i] == '/') { 97 buf[i] = '\0'; 98 //如果不存在,创建 99 iRet = ACCESS(buf, 0); 100 if (iRet != 0) { 101 iRet = MKDIR(buf); 102 if (iRet != 0) { 103 delete[] buf; 104 return false; 105 } 106 } 107 //支持linux,将所有\换成/ 108 buf[i] = '/'; 109 } 110 } 111 delete[] buf; 112 return true; 113 } 114 115 private: 116 117 enum { 118 BUFFER_SIZE = 3000 119 }; 120 121 }; 122 123 } /* namespace FrameWork */ 124 #endif /* FILEHELPER_H_ */
为线程安全的使用增加互斥量
1 /* 2 * Mutex.h 3 * 4 * Created on: 2016年1月7日 5 * Author: Administrator 6 */ 7 8 #ifndef FRAME_LOG_MUTEX_H_ 9 #define FRAME_LOG_MUTEX_H_ 10 11 #include <pthread.h> 12 #include <stdlib.h> 13 namespace FrameWork { 14 typedef pthread_mutex_t MutexType; 15 16 class Mutex { 17 public: 18 // Create a Mutex that is not held by anybody. This constructor is 19 // typically used for Mutexes allocated on the heap or the stack. 20 // See below for a recommendation for constructing global Mutex 21 // objects. 22 inline Mutex(); 23 24 // Destructor 25 inline ~Mutex(); 26 27 inline void Lock(); // Block if needed until free then acquire exclusively 28 inline void Unlock(); // Release a lock acquired via Lock() 29 inline bool TryLock(); // If free, Lock() and return true, else return false 30 // Note that on systems that don't support read-write locks, these may 31 // be implemented as synonyms to Lock() and Unlock(). So you can use 32 // these for efficiency, but don't use them anyplace where being able 33 // to do shared reads is necessary to avoid deadlock. 34 inline void ReaderLock(); // Block until free or shared then acquire a share 35 inline void ReaderUnlock(); // Release a read share of this Mutex 36 inline void WriterLock() { Lock(); } // Acquire an exclusive lock 37 inline void WriterUnlock() { Unlock(); } // Release a lock from WriterLock() 38 39 // TODO(hamaji): Do nothing, implement correctly. 40 inline void AssertHeld() {} 41 private: 42 MutexType mutex_; 43 // We want to make sure that the compiler sets is_safe_ to true only 44 // when we tell it to, and never makes assumptions is_safe_ is 45 // always true. volatile is the most reliable way to do that. 46 volatile bool is_safe_; 47 48 inline void SetIsSafe() { is_safe_ = true; } 49 50 // Catch the error of writing Mutex when intending MutexLock. 51 Mutex(Mutex* /*ignored*/) {} 52 // Disallow "evil" constructors 53 Mutex(const Mutex&); 54 void operator=(const Mutex&); 55 }; 56 #define SAFE_PTHREAD(fncall) do { /* run fncall if is_safe_ is true */ \ 57 if (is_safe_ && fncall(&mutex_) != 0) abort(); \ 58 } while (0) 59 60 Mutex::Mutex() { 61 SetIsSafe(); 62 if (is_safe_ && pthread_mutex_init(&mutex_, NULL) != 0) abort(); 63 } 64 Mutex::~Mutex() { SAFE_PTHREAD(pthread_mutex_destroy); } 65 void Mutex::Lock() { SAFE_PTHREAD(pthread_mutex_lock); } 66 void Mutex::Unlock() { SAFE_PTHREAD(pthread_mutex_unlock); } 67 bool Mutex::TryLock() { return is_safe_ ? 68 pthread_mutex_trylock(&mutex_) == 0 : true; } 69 void Mutex::ReaderLock() { Lock(); } 70 void Mutex::ReaderUnlock() { Unlock(); } 71 class MutexLock { 72 public: 73 explicit MutexLock(Mutex *mu) : mu_(mu) { mu_->Lock(); } 74 ~MutexLock() { mu_->Unlock(); } 75 private: 76 Mutex * const mu_; 77 // Disallow "evil" constructors 78 MutexLock(const MutexLock&); 79 void operator=(const MutexLock&); 80 }; 81 82 // ReaderMutexLock and WriterMutexLock do the same, for rwlocks 83 class ReaderMutexLock { 84 public: 85 explicit ReaderMutexLock(Mutex *mu) : mu_(mu) { mu_->ReaderLock(); } 86 ~ReaderMutexLock() { mu_->ReaderUnlock(); } 87 private: 88 Mutex * const mu_; 89 // Disallow "evil" constructors 90 ReaderMutexLock(const ReaderMutexLock&); 91 void operator=(const ReaderMutexLock&); 92 }; 93 94 class WriterMutexLock { 95 public: 96 explicit WriterMutexLock(Mutex *mu) : mu_(mu) { mu_->WriterLock(); } 97 ~WriterMutexLock() { mu_->WriterUnlock(); } 98 private: 99 Mutex * const mu_; 100 // Disallow "evil" constructors 101 WriterMutexLock(const WriterMutexLock&); 102 void operator=(const WriterMutexLock&); 103 }; 104 } /* namespace FrameWork */ 105 106 #endif /* FRAME_LOG_MUTEX_H_ */
主函数:
1 /* 2 * TestLogMain.cpp 3 * 4 * Created on: 2016 5 * Author: Administrator 6 */ 7 8 #include <iostream> 9 #include "Logger.h" 10 using namespace std; 11 using namespace FrameWork; 12 int Main(int argc, char* argv[]) { 13 InitLogging(argv[0], INFO, "./log/test"); 14 cout << "!!!Hello World!!!" << endl; // prints !!!Hello World!!! 15 LOG(INFO) << "info test"; 16 LOG(WARN) << "WARN TEST %d" << 20; 17 LOG(ERROR) << "Error test %d %s" << 20 << "nihao"; 18 19 Logger::GetInstance().Error("error test common"); 20 Logger::GetInstance().Fatal("fatal test common %d ", 100); 21 Logger::GetInstance().Info("info test normal %d %s ", 50, "zhongguoren"); 22 return 0; 23 }
该代码并来源于他人分享,但没有记住地址,十分抱歉。但是里面的代码风格与设计思路是应该好好学习的。
感谢大神们的分享!