这个是用于Linux下的一个日志服务,日志的使用很简单,支持多线程输入到文件,并且对日志文件大小也有限制。
[2020-06-12T09:46:36.547+0100]-[test]-[error]-[filename:0]-[51086:51086]
时间 执行程序名 级别 第几个文件 线程号 子线程号
对外接口
#ifndef _SMART_LOG_SERVER_H__
#define _SMART_LOG_SERVER_H__
#include "Smart.h"
typedef std::stringstream SmartString;
#define TRACE(str, ToWhere, Level) { \
TextFormat _log_c_; \
_log_c_ << str; \
unsigned int _c_len_ = 0; \
const char* _c_ptr_ = _log_c_.GetContent(_c_len_); \
TraceApi::Write(ToWhere, Level, _c_ptr_, _c_len_); \
}
/
// interfaces provided
#define OUTPUT_GBJECTIVE OUTPUT_FILE
#define CLASSFUNC_TRACE(str) TRACE(str, OUTPUT_GBJECTIVE, TRACE_LEVEL_DEBUG)
#define STATE_TRACE(str) TRACE(str, OUTPUT_GBJECTIVE, TRACE_LEVEL_DEBUG)
#define INFO_TRACE(str) TRACE(str, OUTPUT_GBJECTIVE, TRACE_LEVEL_INFO)
#define WARNING_TRACE(str) TRACE(str, OUTPUT_GBJECTIVE, TRACE_LEVEL_WARNING)
#define ERROR_TRACE(str) TRACE(str, OUTPUT_GBJECTIVE, TRACE_LEVEL_ERROR)
#define ERROR_TRACE_THIS(str) ERROR_TRACE(str << " this=" << this)
#define WARNING_TRACE_THIS(str) WARNING_TRACE(str << " this=" << this)
#define INFO_TRACE_THIS(str) INFO_TRACE(str << " this=" << this)
#define STATE_TRACE_THIS(str) STATE_TRACE(str << " this=" << this)
//debug
#define ASSERTE(expr) \
do { \
if (!(expr)) { \
ERROR_TRACE(__FILE__ << ":" << __LINE__ << " Assert failed: " << #expr); \
} \
} while (0)
#define ASSERTE_RETURN(expr, rv) \
do { \
bool resexpr = (expr)?true:false; \
ASSERTE((resexpr)); \
if (!(resexpr)) { \
ERROR_TRACE(__FILE__ << ":" << __LINE__ << " Assert failed: " << #expr); \
return rv; \
} \
} while (0)
#define ASSERTE_RETURN_VOID(expr) \
do { \
bool resexpr = (expr)?true:false; \
ASSERTE((resexpr)); \
if (!(resexpr)) { \
ERROR_TRACE(__FILE__ << ":" << __LINE__ << " Assert failed: " << #expr); \
return; \
} \
} while (0)
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <time.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <ctype.h>
#include <assert.h>
#include <netinet/tcp.h>
#include <semaphore.h>
using namespace std;
#endif
实现,可以编译为库文件
#include "Smart.h"
void TraceApi::Destroy()
{
TraceImpl::DestroyInstance();
}
void TraceApi::Write(TRACE_OUTPUT_WHERE_TYPE ToWhere,
TRACE_LEVEL_TYPE Level,
const char* szTraceContent,
unsigned int uContentLen)
{
TraceImpl* ptr = TraceImpl::GetInstance();
if (!ptr)
{
return;
}
ptr->Write(ToWhere, Level, szTraceContent, uContentLen);
}
//impl
TraceImpl* TraceImpl::s_LogInstance = NULL;
MyMutex TraceImpl::s_Mutex;
int TraceImpl::s_HasDeleted = 0;
TraceImpl* TraceImpl::GetInstance()
{
if (s_LogInstance == NULL)
{
s_Mutex.Lock();
if (s_HasDeleted)
{
s_Mutex.UnLock();
return NULL;
}
if (s_LogInstance == NULL)
{
s_LogInstance = new TraceImpl();
if (s_LogInstance == NULL)
{
s_Mutex.UnLock();
return NULL;
}
}
s_Mutex.UnLock();
return s_LogInstance;
}
return s_LogInstance;
}
void TraceImpl::DestroyInstance()
{
s_Mutex.Lock();
s_HasDeleted = 1;
// CloseFile();
if (s_LogInstance != NULL)
{
delete s_LogInstance;
s_LogInstance = NULL;
}
s_Mutex.UnLock();
}
void TraceImpl::CloseFile()
{
if (m_File)
{
fclose(m_File);
m_File = NULL;
}
}
void TraceImpl::GetFileName(bool writealbe_file, std::string& fn)
{
std::stringstream ssfn;
ssfn << m_FilenamePrefix << '_' << "linux";
if (writealbe_file)
{
ssfn << "_" << "w" << ".log";
}
else
{
ssfn << "_" << m_uProcessID << "_" << m_uFileNoIndex << ".log";
}
fn = ssfn.str();
}
FILE* TraceImpl::OpenFile()
{
CheckFilesize();
if ((++m_uAccessCount & 127) == 0 && access(_writable_fname.c_str(), 0) == -1)
{
CloseFile();
m_uFileSize = 0;
m_uFlushIndex = 0;
}
if (m_File == NULL)
{
m_File = fopen(_writable_fname.c_str(), "a+");
}
return m_File;
}
void TraceImpl::CheckFilesize()
{
if (m_uFileSize > m_uMaxSizeInFile)
{
if (m_File)
{
fprintf(m_File, "=============================== end of this file ====================\n");
}
CloseFile();
// rename file
std::string new_fn;
GetFileName(false, new_fn);
int res = ::rename(_writable_fname.c_str(), new_fn.c_str());
if (res != 0)
{
::printf("media server trace module, rename failed");
}
m_uFileNoIndex++;
m_uFileNoIndex %= m_uMaxFileNums;
m_uFileSize = 0;
m_uFlushIndex = 0;
}
}
void TraceImpl::WriteToFile(const char* szAContent, unsigned int uConLen)
{
s_Mutex.Lock();
FILE* tmp_file = OpenFile();
if (tmp_file == NULL)
{
s_Mutex.UnLock();
return;
}
fprintf(tmp_file, "%s", szAContent);
if ((++m_uFlushIndex % m_uMaxFlush) == 0)
{
fflush(tmp_file);
}
m_uFileSize += uConLen;
CheckFilesize();
s_Mutex.UnLock();
}
static const char* _trace_level_str_arr[] = { "debug",
"info",
"warn",
"error" };
unsigned int TraceImpl::GetTracePrefix(int Level, char* szContentBuf, unsigned int uBufLen)
{
if (Level > (int)TRACE_LEVEL_ERROR)
{
Level = (int)TRACE_LEVEL_ERROR;
}
// streaming time, process ID and thread ID
int rets = 0;
unsigned int reslen = 0;
struct timeval tv;
struct timezone zn;
gettimeofday(&tv, &zn);
struct tm tm_now;
localtime_r(&tv.tv_sec, &tm_now);
int utcSec = tv.tv_sec % (24 * 60 * 60);
int nowSec = tm_now.tm_hour * 3600 + tm_now.tm_min * 60 + tm_now.tm_sec;
char tzBuf[8] = { 0 };
int diff = (nowSec - utcSec) / 3600;
//Adjust to the same day
if (diff <= -12)
{
diff += 24;
}
else if (diff > 12)
{
diff -= 24;
}
if (diff > 0)
{
snprintf(tzBuf, 6, "+%02d00", diff);
}
else if (diff < 0)
{
snprintf(tzBuf, 6, "-%02d00", abs(diff));
}
else
{
snprintf(tzBuf, 6, "+0000");
}
rets = snprintf(szContentBuf,
uBufLen,
"[%02d-%02d-%02dT%02d:%02d:%02d.%03d%s]-[%s]-[%s]-[%s:%d]-[%u:%u] ",
(int)(tm_now.tm_year + 1900),
(int)(tm_now.tm_mon + 1),
(int)tm_now.tm_mday,
(int)tm_now.tm_hour,
(int)tm_now.tm_min,
(int)tm_now.tm_sec,
(int)(tv.tv_usec / 1000),
tzBuf,
m_FilenamePrefix.c_str(),
_trace_level_str_arr[Level],
"filename",
m_uFileNoIndex,
m_uProcessID,
(unsigned int)gettid());
if (rets < 0)
{
reslen = uBufLen;
}
else
{
reslen = rets;
}
return reslen;
}
void TraceImpl::Write(TRACE_OUTPUT_WHERE_TYPE ToWhere,
TRACE_LEVEL_TYPE Level,
const char* szTraceContent,
unsigned int uContentLen)
{
char logcon[CONLEN_ONELINE];
unsigned int bufflen = CONLEN_ONELINE - 4;
char* bufflog = logcon;
if (bufflen > m_uMaxCharsOneLine)
{
bufflen = m_uMaxCharsOneLine;
}
unsigned int reslen = 0;
unsigned int rest = GetTracePrefix(Level, bufflog, bufflen);
if (rest < bufflen)
{
bufflen -= rest;
reslen += rest;
bufflog += rest;
if (uContentLen > bufflen)
{
uContentLen = bufflen;
}
if (uContentLen)
{
memcpy(bufflog, szTraceContent, uContentLen);
reslen += uContentLen;
bufflog += uContentLen;
}
// append '\n'
bufflog[0] = '\n';
reslen++;
// append '\0'
bufflog[1] = 0;
}
else if (rest == bufflen)
{
reslen += rest;
// append '\n'
bufflog[rest] = '\n';
reslen++;
// append '\0'
bufflog[rest + 1] = 0;
}
else
{
WriteToStdout("logwrite, outof memory\n");
return;
}
if (ToWhere == OUTPUT_STDOUT)
{
WriteToStdout(logcon);
}
else
{
WriteImpl(logcon, reslen, Level);
}
}
void TraceImpl::WriteImpl(const char* szAContent, unsigned int uConLen, TRACE_LEVEL_TYPE Level)
{
WriteToFile(szAContent, uConLen);
}
#ifndef _smart_h__
#define _smart_h__
#include "../Intelligent/SmartPtr.h"
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
#include <sstream>
#include <iostream>
#include <string>
#define FILE_NAME_PREFIX "mediaserver"
#define MAX_CHARS_IN_ONE_LINE 1024
#define FLUSH_FREQUENCY 1
#define FILE_NUMS_BY_REWRITE 2
#define MAX_TRACE_FILESIZE 50*1024*1024
// must use syscall.h
// __NR_gettid = 224
#include <sys/syscall.h>
#define gettid() syscall(__NR_gettid) //
/
// trace level for filter trace, trace can be filtered by level
enum TRACE_LEVEL_TYPE
{
TRACE_LEVEL_DEBUG,
TRACE_LEVEL_INFO,
TRACE_LEVEL_WARNING,
TRACE_LEVEL_ERROR
};
enum TRACE_OUTPUT_WHERE_TYPE
{
OUTPUT_STDOUT,
OUTPUT_FILE,
OUTPUT_SYSLOG
};
#define _MSNPRINTF snprintf
struct TextFormat
{
public:
TextFormat() : m_uIndex(0)
{}
~TextFormat()
{}
const char* GetContent(unsigned int& uContentLen) const
{
uContentLen = m_uIndex;
return m_aContent;
}
TextFormat& operator<<(unsigned int uVal)
{
return *this << (unsigned long)uVal;
}
TextFormat& operator<<(unsigned long ulVal)
{
if (m_uIndex >= CONT_BUFF_LEN)
{
return *this;
}
unsigned int buf_len = CONT_BUFF_LEN - m_uIndex;
int r_len = _MSNPRINTF(m_aContent + m_uIndex, buf_len, "%lu", ulVal);
if (r_len >= 0)
{
m_uIndex += r_len;
}
else
{
m_uIndex += buf_len;
}
m_aContent[m_uIndex] = 0;
return *this;
}
TextFormat& operator<<(unsigned long long ullVal)
{
if (m_uIndex >= CONT_BUFF_LEN)
{
return *this;
}
unsigned int buf_len = CONT_BUFF_LEN - m_uIndex;
int r_len = _MSNPRINTF(m_aContent + m_uIndex, buf_len, "%llu", ullVal);
if (r_len >= 0)
{
m_uIndex += r_len;
}
else
{
m_uIndex += buf_len;
}
m_aContent[m_uIndex] = 0;
return *this;
}
TextFormat& operator<<(int iVal)
{
return *this << (long)iVal;
}
TextFormat& operator<<(long lVal)
{
if (m_uIndex >= CONT_BUFF_LEN)
{
return *this;
}
unsigned int buf_len = CONT_BUFF_LEN - m_uIndex;
int r_len = _MSNPRINTF(m_aContent + m_uIndex, buf_len, "%ld", lVal);
if (r_len >= 0)
{
m_uIndex += r_len;
}
else
{
m_uIndex += buf_len;
}
m_aContent[m_uIndex] = 0;
return *this;
}
TextFormat& operator<<(long long llVal)
{
if (m_uIndex >= CONT_BUFF_LEN)
{
return *this;
}
unsigned int buf_len = CONT_BUFF_LEN - m_uIndex;
int r_len = _MSNPRINTF(m_aContent + m_uIndex, buf_len, "%lld", llVal);
if (r_len >= 0)
{
m_uIndex += r_len;
}
else
{
m_uIndex += buf_len;
}
m_aContent[m_uIndex] = 0;
return *this;
}
TextFormat& operator<<(const char* pszCont)
{
if (m_uIndex >= CONT_BUFF_LEN)
{
return *this;
}
if (pszCont == NULL)
{
*this << (void *)pszCont;
}
else
{
unsigned int buf_len = CONT_BUFF_LEN - m_uIndex;
int r_len = _MSNPRINTF(m_aContent + m_uIndex, buf_len, "%s", pszCont);
if (r_len >= 0)
{
if (r_len > (int)buf_len)
{
r_len = buf_len;
}
m_uIndex += r_len;
}
else
{
m_uIndex += buf_len;
}
m_aContent[m_uIndex] = 0;
}
return *this;
}
TextFormat& operator<<(const void* pCont)
{
if (m_uIndex >= CONT_BUFF_LEN)
{
return *this;
}
unsigned int buf_len = CONT_BUFF_LEN - m_uIndex;
int r_len = _MSNPRINTF(m_aContent + m_uIndex, buf_len, "%p", pCont);
if (r_len >= 0)
{
m_uIndex += r_len;
}
else
{
m_uIndex += buf_len;
}
m_aContent[m_uIndex] = 0;
return *this;
}
TextFormat& operator<<(short nVal)
{
return (*this << (int)nVal);
}
TextFormat& operator<<(char cVal)
{
if (m_uIndex >= CONT_BUFF_LEN)
{
return *this;
}
if (cVal > 126 || cVal < 32)
{
return *this << (int)cVal;
}
unsigned int buf_len = CONT_BUFF_LEN - m_uIndex;
int r_len = _MSNPRINTF(m_aContent + m_uIndex, buf_len, "%c", cVal);
if (r_len >= 0)
{
m_uIndex += r_len;
}
else
{
m_uIndex += buf_len;
}
m_aContent[m_uIndex] = 0;
return *this;
}
TextFormat& operator<<(unsigned short wVal)
{
return (*this << (unsigned int)wVal);
}
TextFormat& operator<<(unsigned char byVal)
{
return *this << (char)byVal;
}
TextFormat& operator<<(float fVal)
{
return *this << (double)fVal;
}
TextFormat& operator<<(double fVal)
{
if (m_uIndex >= CONT_BUFF_LEN)
{
return *this;
}
unsigned int buf_len = CONT_BUFF_LEN - m_uIndex;
int r_len = _MSNPRINTF(m_aContent + m_uIndex, buf_len, "%f", fVal);
if (r_len >= 0)
{
m_uIndex += r_len;
}
else
{
m_uIndex += buf_len;
}
m_aContent[m_uIndex] = 0;
return *this;
}
TextFormat& operator<<(const std::string& con)
{
if (m_uIndex >= CONT_BUFF_LEN)
{
return *this;
}
if (con.empty())
{
return *this;
}
unsigned int buf_len = CONT_BUFF_LEN - m_uIndex;
int r_len = _MSNPRINTF(m_aContent + m_uIndex, buf_len, "%s", con.c_str());
if (r_len >= 0)
{
if (r_len > (int)buf_len)
{
r_len = buf_len;
}
m_uIndex += r_len;
}
else
{
m_uIndex += buf_len;
}
m_aContent[m_uIndex] = 0;
return *this;
}
private:
TextFormat(const TextFormat&);
TextFormat& operator=(const TextFormat&);
private:
enum
{
CONT_BUFF_LEN = 1024
};
unsigned int m_uIndex;
char m_aContent[CONT_BUFF_LEN + 32];
};
typedef PthreadMutex MyMutex;
struct TraceImpl
{
public:
void Write(TRACE_OUTPUT_WHERE_TYPE ToWhere,
TRACE_LEVEL_TYPE Level,
const char* szTraceContent,
unsigned int uContentLen);
static TraceImpl* GetInstance();
static void DestroyInstance();
private:
TraceImpl() :m_File(NULL),
m_uFlushIndex(0),
m_uMaxFlush(FLUSH_FREQUENCY),
m_uAccessCount(0),
m_uFileNoIndex(0),
m_uMaxFileNums(FILE_NUMS_BY_REWRITE),
m_uFileSize(0),
m_uMaxSizeInFile(MAX_TRACE_FILESIZE),
m_uProcessID(0),
m_uMaxCharsOneLine(MAX_CHARS_IN_ONE_LINE),
m_FilterLevel(TRACE_LEVEL_INFO)
{
std::string pn;
m_uProcessID = (unsigned int)(::getpid());
pn = program_invocation_short_name;
if (!pn.empty())
{
size_t pos = pn.rfind('.');
if (pos != std::string::npos)
{
m_FilenamePrefix = pn.substr(0, pos);
}
else
{
m_FilenamePrefix = pn;
}
}
if (m_FilenamePrefix.empty())
{
m_FilenamePrefix = std::string(FILE_NAME_PREFIX);
}
GetFileName(true, _writable_fname);
}
~TraceImpl()
{
CloseFile();
}
unsigned int GetTracePrefix(int Level, char* szContentBuf, unsigned int uBufLen);
void WriteToStdout(const char* szAContent)
{
// std::cout << szAContent << std::endl;
// has append '\n'
s_Mutex.Lock();
::printf("%s", szAContent);
s_Mutex.UnLock();
}
enum
{
CONLEN_ONELINE = 1096
};
private:
FILE* OpenFile();
void CloseFile();
void WriteToFile(const char* szAContent, unsigned int uConLen);
void WriteImpl(const char* szAContent, unsigned int uConLen, TRACE_LEVEL_TYPE Level);
private:
void CheckFilesize();
void GetFileName(bool writealbe_file, std::string& fn);
private:
FILE* m_File; // m_File
unsigned int m_uFlushIndex; // m_uFlushIndex
unsigned int m_uMaxFlush; // m_uMaxFlush
unsigned int m_uAccessCount;
unsigned int m_uFileNoIndex;
unsigned int m_uMaxFileNums;
unsigned int m_uFileSize;
unsigned int m_uMaxSizeInFile;
unsigned int m_uProcessID;
unsigned int m_uMaxCharsOneLine;
TRACE_LEVEL_TYPE m_FilterLevel; // m_Filter
std::string m_FilenamePrefix;
std::string _writable_fname;
/
// static related members
static TraceImpl* s_LogInstance; // s_LogInstance
static int s_HasDeleted;
static MyMutex s_Mutex;
/
};
// trace function wrapper
struct TraceApi
{
public:
static void Destroy();
static void Write(TRACE_OUTPUT_WHERE_TYPE ToWhere,
TRACE_LEVEL_TYPE Level,
const char* szTraceContent,
unsigned int uContentLen);
};
#endif