linux系统中c++写日志文件功能分享
简化了glog,只保留了写日志文件的功能,只是改写了linux版本,win版本未改写,可以用
LOG(INFO)<< 输出日志
也可用LOG_IF(INFO,condition)<<输出日志
也可直接调用日志类Logger::GetInstance().Error 等方式写日志
初始化时调用 InitLogging(argv[0],INFO,"./log/test");
第一个参数是路径,第二个参数是最低日志级别,第三个参数表示日志文件的前缀和文件夹
FileHelper.h
#ifndef FILEHELPER_H_
#define FILEHELPER_H_
#include <string>
#include <vector>
#include <fstream>
#include <stdio.h>
#ifdef _WIN32
#include <direct.h>
#include <io.h>
#else
#include <stdarg.h>
#include <sys/stat.h>
#endif
namespace FrameWork {
#ifdef _WIN32
#define ACCESS _access
#define MKDIR(a) _mkdir((a))
#else
#define ACCESS access
#define MKDIR(a) mkdir((a),0755)
#endif
class FileHelper {
public:
static bool save(const std::string filename, std::string& content)
{
FILE *file = fopen(filename.c_str(), "wb");
if (file == NULL)
return false;
fwrite(content.c_str(),sizeof(char),content.size(),file);
fclose(file);
return true;
}
// used to open binary file
static bool open(const std::string filename, std::string& content)
{
FILE *file = fopen(filename.c_str(), "rb");
if (file == NULL)
return false;
fseek(file, 0, SEEK_END);
int len = ftell(file);
rewind(file);
content.clear();
char *buffer = new char[len];
fread(buffer, sizeof(char), len, file);
content.assign(buffer, len);
delete []buffer;
//int nRead;
//content.clear();
//char buffer[80];
//while(!feof(file)){
// nRead = fread(buffer,sizeof(char),sizeof(buffer),file);
// if(nRead > 0){
// content.append(buffer);
// }
//}
fclose(file);
return true;
}
// used to open text file
static bool open(const std::string file_name, std::vector<std::string>& lines)
{
std::ifstream file(file_name.c_str(), std::ios::in);
if (!file)
{
return false;
}
lines.clear();
char buffer[BUFFER_SIZE];
while (file.getline(buffer, BUFFER_SIZE, '\n'))
{
lines.push_back(buffer);
}
return true;
}
static bool CreateDir(const char *pszDir)
{
size_t i = 0;
size_t iRet;
size_t iLen = strlen(pszDir);
char* buf=new char[iLen+1];
strncpy(buf,pszDir,iLen+1);
for (i = 0;i < iLen;i ++) {
if (pszDir[i] == '\\' || pszDir[i] == '/') {
buf[i] = '\0';
//如果不存在,创建
iRet = ACCESS(buf,0);
if (iRet != 0) {
iRet = MKDIR(buf);
if (iRet != 0) {
delete[] buf;
return false;
}
}
//支持linux,将所有\换成/
buf[i] = '/';
}
}
delete[] buf;
return true;
}
private:
enum { BUFFER_SIZE = 3000 };
};
} /* namespace FrameWork */
#endif /* FILEHELPER_H_ */
[/code]
Logger.cpp
#include "Logger.h"
#include<cstring>
#include<time.h>
#include<cstdarg>
#include<cstdlib>
#include<assert.h>
#include "FileHelper.h"
#include "Mutex.h"
namespace FrameWork {
Mutex LogMessage::mutex;
static char _defaltFolder[]="/var/tmp/";
static char _appName[MaxFilePathLen];
static char _appFolder[MaxFilePathLen];
static char _destFolder[MaxFilePathLen];
static char _destPrefix[MaxFilePathLen];
static LogLevel _destLevel;
static char _levelInfos[][16]={
"Debug","Info","Warn","Error","Fatal"
};
const int BUFFER_SIZE = 8196;
static char _gBuffer[BUFFER_SIZE];
void combine_folder(char** destpath, char* basefolder,char* relativefolder)
{
int lenb = strlen(basefolder);
int lenr = strlen(relativefolder);
char* pret = (char*)malloc((lenb+lenr+1)*sizeof(char));
int pos=lenb-1;
memset(pret,0,lenb+lenr+1);
while(pos>0 && ( basefolder[pos]!='/'))
pos--;
strncpy(*destpath,basefolder,pos+1);
if(relativefolder[0] == '\\' || relativefolder[0] == '/'){
strncpy(*destpath+pos+1,relativefolder+1,lenr-1);
}else{
strncpy(*destpath+pos+1,relativefolder,lenr);
}
}
static void InitPaths(const char* filename,const char* destFolder)
{
memset(_appName,0,MaxFilePathLen);
memset(_appFolder,0,MaxFilePathLen);
memset(_destFolder,0,MaxFilePathLen);
memset(_destPrefix,0,MaxFilePathLen);
strcpy(_appName,filename);
int len = strlen(filename),lend;
int pos = len-1,posd,start;
while(pos >0 && filename[pos] != PathSplitChar)
pos--;
strncpy(_appFolder,filename,pos+1);
lend = strlen(destFolder);
posd = lend-1;
if(destFolder[lend-1] != PathSplitChar) {
//has prefix
while(posd >0 && destFolder[posd] != PathSplitChar)
posd--;
}
if(destFolder[0] == '.' && destFolder[1] == PathSplitChar){
strncpy(_destFolder,filename,pos+1);
start = 2;
} else{
pos = 8;
strcpy(_destFolder,_defaltFolder);
if(destFolder[0] != PathSplitChar){
start = 0;
}else{
start = 1;
}
}
strncpy(_destFolder+pos+1,destFolder+start,posd-start+1);
strncpy(_destPrefix,filename,pos+1);
strncpy(_destPrefix+pos+1,destFolder+start,lend-start);
}
void InitLogging(const char* filename,LogLevel minlevel,const char* destFolder)
{
InitPaths(filename,destFolder);
_destLevel = minlevel;
}
static string GetLocalDate(void)
{
time_t t = time(0);
tm *ld;
char tmp[64] = "";
ld=localtime(&t);
strftime(tmp,sizeof(tmp),"%Y-%m-%d",ld);
return string(tmp);
}
static string GetCurTime(void)
{
time_t t = time(0);
tm *ld;
char tmp[64] = "";
ld=localtime(&t);
strftime(tmp,sizeof(tmp),"%Y-%m-%d %H:%M:%S",ld);
return string(tmp);
}
Logger::Logger(LogLevel level,char * folder,char * prefix)
:level(level)
{
std::string path;
path.append(prefix);
path.append(GetLocalDate());
path.append(".log");
FileHelper::CreateDir(folder);
logPrefix.append(prefix);
logPath = path;
logFile.open(path.c_str(),ios::app|ios::out);
logFile<<"Log file created at:"<<GetCurTime()<<endl;
}
Logger::~Logger() {
logFile.close();
}
#define IMPLEMENT_LOG_FUNC1(cname,fname,lv) \
void cname::fname(string msg) {\
if(level <= lv){\
WriterMutexLock lock(&mutex);\
logFile<<"["<<GetCurTime().c_str()<<"][" #lv "]"<<msg.c_str()<<endl;\
logFile.flush();\
}\
}
#define PRINT_ARGS_TO_BUFFER(fmt,buf) \
{\
memset(buf,0,sizeof(buf));\
va_list argp;\
va_start(argp,fmt);\
vsprintf(buf,fmt,argp);\
va_end(argp);\
}
#define IMPLEMENT_LOG_FUNC2(cname,fname,lv) \
void cname::fname(const char* format,...) {\
if(level <= lv){\
WriterMutexLock lock(&mutex);\
PRINT_ARGS_TO_BUFFER(format,_gBuffer)\
logFile<<"["<<GetCurTime().c_str()<<"][" #lv "]"<<_gBuffer<<endl;\
logFile.flush();\
}\
}
#define IMPLEMENT_LOG_FUNC(cname,fname,lv) \
IMPLEMENT_LOG_FUNC1(cname,fname,lv)\
IMPLEMENT_LOG_FUNC2(cname,fname,lv)
IMPLEMENT_LOG_FUNC(Logger,Debug,DEBUG)
IMPLEMENT_LOG_FUNC(Logger,Info,INFO)
IMPLEMENT_LOG_FUNC(Logger,Warn,WARN)
IMPLEMENT_LOG_FUNC(Logger,Error,ERROR)
IMPLEMENT_LOG_FUNC(Logger,Fatal,FATAL)
Logger& Logger::GetInstance() {
static Logger _logger(_destLevel,_destFolder,_destPrefix);
return _logger;
}
void Logger::Log(LogLevel lv, string msg) {
if(level <= lv){
WriterMutexLock lock(&mutex);
logFile<<"["<<GetCurTime().c_str()<<"]["<<_levelInfos[lv+1]<<"]"<<msg.c_str()<<endl;
logFile.flush();
}
}
void Logger::Log(LogLevel lv, const char* format,...) {
if(level <= lv){
WriterMutexLock lock(&mutex);
PRINT_ARGS_TO_BUFFER(format,_gBuffer)
logFile<<"["<<GetCurTime().c_str()<<"]["<<_levelInfos[lv+1]<<"]"<<_gBuffer<<endl;
logFile.flush();
}
}
void Logger::Log(const char* file, int line, LogLevel lv, string msg) {
if(level <= lv){
WriterMutexLock lock(&mutex);
logFile<<"["<<GetCurTime().c_str()<<"]["<<_levelInfos[lv+1]<<"]["<<file<<"]["<<line<<"]"<<msg.c_str();
logFile.flush();
}
}
Logger* Logger::GetInstancePtr() {
return &GetInstance();
}
void Logger::Log(const char* file, int line, LogLevel lv, const char* format,...) {
if(level <= lv){
WriterMutexLock lock(&mutex);
PRINT_ARGS_TO_BUFFER(format,_gBuffer)
logFile<<"["<<GetCurTime().c_str()<<"]["<<_levelInfos[lv+1]<<"]["<<file<<"]["<<line<<"]"<<_gBuffer;
logFile.flush();
}
}
LogMessage::LogMessage(const char* file, int line, LogLevel lv) {
logger = Logger::GetInstancePtr();
mutex.Lock();
logger->Log(file,line,lv,"");
}
LogMessage::~LogMessage() {
logger->stream()<<endl;
logger->stream().flush();
mutex.Unlock();
}
} /* namespace FrameWork */
#ifndef LOGGER_H_
#define LOGGER_H_
#include "ILogger.h"
#include "Mutex.h"
#include<fstream>
#include<string>
const int MaxFilePathLen = 1024;
const char PathSplitChar = '/';
namespace FrameWork {
enum LogLevel{
/// <summary>
/// 调试
/// </summary>
DEBUG = -1,
/// <summary>
/// 普通日志
/// </summary>
INFO = 0,
/// <summary>
/// 警告
/// </summary>
WARN,
/// <summary>
/// 错误
/// </summary>
ERROR,
/// <summary>
/// 崩溃
/// </summary>
FATAL,
/// <summary>
/// 超出错误级别
/// </summary>
OFF
};
class ILogger {
public:
//
virtual ~ILogger(){}
#define ABSTRACT_LOG_FUNC(name) \
virtual void name(string msg)=0; \
virtual void name(const char* fmt,...)=0;
ABSTRACT_LOG_FUNC(Debug)
ABSTRACT_LOG_FUNC(Info)
ABSTRACT_LOG_FUNC(Warn)
ABSTRACT_LOG_FUNC(Error)
ABSTRACT_LOG_FUNC(Fatal)
#undef ABSTRACT_LOG_FUNC
#define ABSTRACT_LOG_FUNC_X(name) \
virtual void name(LogLevel lv,string msg)=0; \
virtual void name(LogLevel lv,const char* fmt,...)=0;\
virtual void name(const char* file,int line,LogLevel lv,string msg)=0;\
virtual void name(const char* file,int line,LogLevel lv,const char* fmt,...)=0;
ABSTRACT_LOG_FUNC_X(Log)
#undef LOG_FUNC_X
};
class Logger: public ILogger {
std::string logPath;
std::string logPrefix;
std::fstream logFile;
LogLevel level;
Mutex mutex;
Logger(LogLevel level,char * folder,char * prefix);
public:
static Logger& GetInstance();
static Logger* GetInstancePtr();
virtual ~Logger();
inline fstream & stream(){return logFile;}
#define DECLARE_LOG_FUNC(name) \
virtual void name(string msg); \
virtual void name(const char* fmt,...);
#define DECLARE_LOG_FUNC_X(name) \
virtual void name(LogLevel lv,string msg); \
virtual void name(LogLevel lv,const char* fmt,...);\
virtual void name(const char* file,int line,LogLevel lv,string msg);\
virtual void name(const char* file,int line,LogLevel lv,const char* fmt,...);
DECLARE_LOG_FUNC(Debug)
DECLARE_LOG_FUNC(Info)
DECLARE_LOG_FUNC(Warn)
DECLARE_LOG_FUNC(Error)
DECLARE_LOG_FUNC(Fatal)
DECLARE_LOG_FUNC_X(Log)
#undef DECLARE_LOG_FUNC_X
#undef DECLARE_LOG_FUNC
};
class LogMessage {
Logger* logger;
static Mutex mutex;
public:
LogMessage(const char* file, int line,LogLevel lv);
ostream& stream(){return logger->stream();}
virtual ~LogMessage();
};
void InitLogging(const char* filename,LogLevel minlevel,const char* destFolder);
void CloseLogging();
#define LOG(level) LogMessage(__FILE__, __LINE__,level).stream()
#define LOG_IF(severity, condition) \
!(condition) ? (void) 0 : LOG(severity)
#define LOG_ASSERT(condition) \
LOG_IF(FATAL, !(condition)) << "Assert failed: " #condition
#define CHECK(condition) \
LOG_IF(FATAL, !(condition)) \
<< "Check failed: " #condition " "
} /* namespace FrameWork */
#endif /* LOGGER_H_ */
main.cpp
#include <iostream>
#include "Logger.h"
using namespace std;
using namespace FrameWork;
int main(int argc,char* argv[]) {
InitLogging(argv[0],INFO,"./log/test");
cout << "!!!Hello World!!!" << endl; // prints !!!Hello World!!!
LOG(INFO)<<"info test";
LOG(WARN)<<"WARN TEST %d"<<20;
LOG(ERROR)<<"Error test %d %s"<<20<<"nihao";
Logger::GetInstance().Error("error test common");
Logger::GetInstance().Fatal("fatal test common %d ",100);
Logger::GetInstance().Info("info test normal %d %s ",50,"zhongguoren");
return 0;
}
Mutex.h
#ifndef MUTEX_H_
#define MUTEX_H_
#include <pthread.h>
#include <stdlib.h>
namespace FrameWork {
typedef pthread_mutex_t MutexType;
class Mutex {
public:
// Create a Mutex that is not held by anybody. This constructor is
// typically used for Mutexes allocated on the heap or the stack.
// See below for a recommendation for constructing global Mutex
// objects.
inline Mutex();
// Destructor
inline ~Mutex();
inline void Lock(); // Block if needed until free then acquire exclusively
inline void Unlock(); // Release a lock acquired via Lock()
inline bool TryLock(); // If free, Lock() and return true, else return false
// Note that on systems that don't support read-write locks, these may
// be implemented as synonyms to Lock() and Unlock(). So you can use
// these for efficiency, but don't use them anyplace where being able
// to do shared reads is necessary to avoid deadlock.
inline void ReaderLock(); // Block until free or shared then acquire a share
inline void ReaderUnlock(); // Release a read share of this Mutex
inline void WriterLock() { Lock(); } // Acquire an exclusive lock
inline void WriterUnlock() { Unlock(); } // Release a lock from WriterLock()
// TODO(hamaji): Do nothing, implement correctly.
inline void AssertHeld() {}
private:
MutexType mutex_;
// We want to make sure that the compiler sets is_safe_ to true only
// when we tell it to, and never makes assumptions is_safe_ is
// always true. volatile is the most reliable way to do that.
volatile bool is_safe_;
inline void SetIsSafe() { is_safe_ = true; }
// Catch the error of writing Mutex when intending MutexLock.
Mutex(Mutex* /*ignored*/) {}
// Disallow "evil" constructors
Mutex(const Mutex&);
void operator=(const Mutex&);
};
#define SAFE_PTHREAD(fncall) do { /* run fncall if is_safe_ is true */ \
if (is_safe_ && fncall(&mutex_) != 0) abort(); \
} while (0)
Mutex::Mutex() {
SetIsSafe();
if (is_safe_ && pthread_mutex_init(&mutex_, NULL) != 0) abort();
}
Mutex::~Mutex() { SAFE_PTHREAD(pthread_mutex_destroy); }
void Mutex::Lock() { SAFE_PTHREAD(pthread_mutex_lock); }
void Mutex::Unlock() { SAFE_PTHREAD(pthread_mutex_unlock); }
bool Mutex::TryLock() { return is_safe_ ?
pthread_mutex_trylock(&mutex_) == 0 : true; }
void Mutex::ReaderLock() { Lock(); }
void Mutex::ReaderUnlock() { Unlock(); }
class MutexLock {
public:
explicit MutexLock(Mutex *mu) : mu_(mu) { mu_->Lock(); }
~MutexLock() { mu_->Unlock(); }
private:
Mutex * const mu_;
// Disallow "evil" constructors
MutexLock(const MutexLock&);
void operator=(const MutexLock&);
};
// ReaderMutexLock and WriterMutexLock do the same, for rwlocks
class ReaderMutexLock {
public:
explicit ReaderMutexLock(Mutex *mu) : mu_(mu) { mu_->ReaderLock(); }
~ReaderMutexLock() { mu_->ReaderUnlock(); }
private:
Mutex * const mu_;
// Disallow "evil" constructors
ReaderMutexLock(const ReaderMutexLock&);
void operator=(const ReaderMutexLock&);
};
class WriterMutexLock {
public:
explicit WriterMutexLock(Mutex *mu) : mu_(mu) { mu_->WriterLock(); }
~WriterMutexLock() { mu_->WriterUnlock(); }
private:
Mutex * const mu_;
// Disallow "evil" constructors
WriterMutexLock(const WriterMutexLock&);
void operator=(const WriterMutexLock&);
};
} /* namespace FrameWork */
#endif /* MUTEX_H_ */