在程序开发过程中,我们需要动态了解程序运行状况,以及排查问题时程序的调用流程,尤其是在多线程程序中,调用关系用日志记录下来比较重要,方便以后排查问题。以下是自己开发过程中封装的日志功能类。
common.h
#ifndef __COMMON__H_
#define __COMMON__H_
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include <time.h>
#include "LocalDefine.h"
#include <string>
#include <iostream>
#include <stdint.h>
#include <map>
#include <vector>
#if defined(WIN32) || defined(_WIN32)
#include <io.h>
#include <Ws2tcpip.h>
#include <winsock2.h>
#include <windows.h>
#include <process.h>
#include <sys/timeb.h>
#elif __linux__
#include<stdarg.h>
#include <sys/time.h>
#include <unistd.h>
#include <pthread.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <errno.h>
#endif
#endif
LocalDefine.h
#ifndef __LOCALDEFINE__H_
#define __LOCALDEFINE__H_
#if defined(WIN32) || defined(_WIN32)
#elif __linux__
typedef unsigned long DWORD;
typedef unsigned short WORD;
typedef long LONG;
#define INFINITE 0xFFFFFFFF
typedef struct tagBITMAPFILEHEADER {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;
typedef struct tagBITMAPINFOHEADER{
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER;
#endif
#if defined(WIN32) || defined(_WIN32)
#elif __linux__
#define _TRUNCATE ((size_t)-1)
#define _strdup strdup
#endif
struct systemtime_t
{
int tmsec; /* seconds after the minute - [0,59] */
int tmmin; /* minutes after the hour - [0,59] */
int tmhour; /* hours since midnight - [0,23] */
int tmmday; /* day of the month - [1,31] */
int tmmon; /* months since January - [0,11] */
int tmyear; /* years since 1900 */
int tmwday; /* days since Sunday - [0,6] */
int tmyday; /* days since January 1 - [0,365] */
int tmisdst; /* daylight savings time flag */
int tmmilliseconds;/*milliseconds after the sec[0,1000]*/
};
struct len_str
{
unsigned char* pStr;
size_t iLen;
};
#endif
在日志中需要用到的功能函数封装如下
#ifndef __UTIL__H_
#define __UTIL__H_
#include <algorithm>
#include "common.h"
//mem
#define DOFREE(X) do{if(nullptr != X) {free(X); X = nullptr;}}while(0)
#define DODELETE(X) do{if(nullptr != X) {delete X; X = nullptr;}}while(0)
#define BZERO(X) memset(&X, 0, sizeof(X))
void* do_malloc(size_t iLen);
//time
struct systemtime_t get_now_time();
uint64_t get_time_ms(); //获取时间毫秒级
void sleep_ms(DWORD dwMillions);
//thread
DWORD get_thread_id_self();
long atomic_change(volatile long* value, int off);
//strings
bool str_cmp(const char* pStr1, const char* pStr2, bool bIgnoreCase);
int safe_snprintf(char * _DstBuf, size_t _SizeInBytes, size_t _MaxCount, const char * _Format, ...);
bool str_start_with(const std::string& str, const std::string& prefix);
bool str_end_with(const std::string& str, const std::string& suffix);
bool str_cmp_nocase(std::string str1, std::string str2);
std::string& str_ltrim(std::string& str);
std::string& str_rtrim(std::string& str);
std::string& str_trim(std::string& str);
std::string str_2_lower(std::string& str);
std::string str_2_upper(std::string& str);
wchar_t* char_2_wchar(const char* pStr);
char* wchar_2_char(const wchar_t* pStr);
int split_str(std::string strSource, std::string strSep, std::vector<std::string>& vecRet);
//files
FILE* safe_open_file(const char* pFileName, const char* pMode);
len_str get_bmp(const void* pBmp, int width, int height, int bitCount);
const long get_file_len(const char* pFileName, const char* pMode);
size_t file_write(const char* pFileName, const char* pMode, const unsigned char* pData, const size_t iLen);
size_t file_read(const char* pFileName, const char* pMode, unsigned char* pData, const size_t iLen, const long lOffset);
char* file_read(const char* pFileName, const char* pMode, size_t& lLen);
std::string get_app_path();
bool file_exits(const char* strFileName);
bool directory_exists(const char* strDirectory);
int make_dir(const char* path); //创建目录
int make_dirs(const char* path); //创建多层目录
const char* get_flietype(const char *pFileName);
//network
int init_platform();
int uninit_platform();
//error
void perror_desc(const char* pStr);
#endif
#include "util.h"
void* do_malloc(size_t iLen)
{
if (iLen <= 0)
{
return nullptr;
}
void* pRet = nullptr;
while (nullptr == pRet)
{
pRet = malloc(iLen);
}
memset(pRet, 0, iLen);
#if defined(WIN32) || defined(_WIN32)
fprintf(stderr, "malloc mem:%I64u\n", iLen);
#elif __linux__
fprintf(stderr, "malloc mem:%llu\n", iLen);
#endif
return pRet;
}
const char* get_flietype(const char *pFileName)
{
int n = (int)strlen(pFileName);
if (n < 5)
return nullptr;
if (!strcmp(&pFileName[n - 4], ".ico"))
return "image/x-icon";
if (!strcmp(&pFileName[n - 4], ".png"))
return "image/png";
if (!strcmp(&pFileName[n - 5], ".html"))
return "text/html";
if (!strcmp(&pFileName[n - 4], ".css"))
return "text/css";
if (!strcmp(&pFileName[n - 3], ".js"))
return "text/javascript";
return nullptr;
}
uint64_t get_time_ms()
{
uint64_t iRet = 0;
#if defined(WIN32) || defined(_WIN32)
iRet = GetTickCount();
#elif __linux__
struct timeval current;
gettimeofday(¤t, nullptr);
iRet = (uint64_t)(current.tv_sec * 1000) + current.tv_usec/1000;
#endif
return iRet;
}
void sleep_ms(DWORD dwMillions)
{
#if defined(WIN32) || defined(_WIN32)
Sleep(dwMillions);
#elif __linux__
usleep(dwMillions * 1000);
#endif
}
struct systemtime_t get_now_time()
{
struct systemtime_t stRet;
time_t rawtime;
struct tm tmnow;
#if defined(WIN32) || defined(_WIN32)
time(&rawtime);
localtime_s(&tmnow, &rawtime);
struct timeb tp;
ftime(&tp);
stRet.tmmilliseconds = tp.millitm;
#elif __linux__
struct timeval tv_now;
gettimeofday(&tv_now, nullptr);
rawtime = (time_t)tv_now.tv_sec;
localtime_r(&rawtime, &tmnow);
stRet.tmmilliseconds = static_cast<int>(tv_now.tv_usec);
#endif
stRet.tmyear = tmnow.tm_year + 1900;
stRet.tmmon = tmnow.tm_mon + 1;
stRet.tmmday = tmnow.tm_mday;
stRet.tmhour = tmnow.tm_hour;
stRet.tmmin = tmnow.tm_min;
stRet.tmsec = tmnow.tm_sec;
stRet.tmwday = tmnow.tm_wday;
stRet.tmyday = tmnow.tm_yday;
stRet.tmisdst = tmnow.tm_isdst;
return stRet;
}
DWORD get_thread_id_self()
{
DWORD iRet = 0;
#if defined(WIN32) || defined(_WIN32)
iRet = ::GetCurrentThreadId();
#elif __linux__
iRet = pthread_self();
#endif
return iRet;
}
long atomic_change(volatile long* value, int off)
{
long lRet = 0;
if (nullptr != value)
{
#ifdef _WIN32
lRet = InterlockedExchangeAdd(value, off);
#elif __linux__
lRet = __sync_fetch_and_add(value, off);
#endif
}
return lRet;
}
int split_str(std::string strSource, std::string strSep, std::vector<std::string>& vecRet)
{
size_t i = 0;
vecRet.clear();
while (i < strSource.size()) {
size_t iPos = strSource.find(strSep, i);
if (iPos == std::string::npos) {
vecRet.push_back(strSource.substr(i));
break;
}
else {
vecRet.push_back(strSource.substr(i, iPos - i));
i = iPos + strSep.size();
}
}
return (int)vecRet.size();
}
bool str_cmp(const char* pStr1, const char* pStr2, bool bIgnoreCase)
{
if (pStr1 == pStr2)
{
return true;
}
if (nullptr == pStr1 || nullptr == pStr2)
{
return false;
}
size_t iLen = strlen(pStr1);
if (iLen != strlen(pStr2))
{
return false;
}
for (int i = 0; i <= iLen; ++i)
{
char c1 = pStr1[i];
char c2 = pStr2[i];
if (bIgnoreCase)
{
c1 = toupper(c1);
c2 = toupper(c2);
}
if (c1 != c2)
{
return false;
}
}
return true;
}
int safe_snprintf(char * _DstBuf, size_t _SizeInBytes, size_t _MaxCount, const char * _Format, ...)
{
int iRet = 0;
va_list ap;
va_start(ap, _Format);
#if defined(WIN32) || defined(_WIN32)
iRet = vsnprintf_s(_DstBuf, _SizeInBytes, _MaxCount, _Format, ap);
#elif __linux__
iRet = vsnprintf(_DstBuf, _MaxCount, _Format, ap);
#endif
va_end(ap);
return iRet;
}
bool str_start_with(const std::string& str, const std::string& prefix)
{
if (prefix.length() > str.length())
{
return false;
}
if (memcmp(str.c_str(), prefix.c_str(), prefix.length()) == 0)
{
return true;
}
return false;
}
bool str_end_with(const std::string& str, const std::string& suffix)
{
if (suffix.length() > str.length())
{
return false;
}
return (str.substr(str.length() - suffix.length()) == suffix);
}
std::string& str_ltrim(std::string& str)
{ // NOLINT
std::string::iterator it = find_if(str.begin(), str.end(), (int(*)(int))isspace);
str.erase(str.begin(), it);
return str;
}
std::string& str_rtrim(std::string& str)
{ // NOLINT
std::string::reverse_iterator it = find_if(str.rbegin(),
str.rend(), (int(*)(int))isspace);
str.erase(it.base(), str.end());
return str;
}
std::string& str_trim(std::string& str)
{ // NOLINT
return str_rtrim(str_ltrim(str));
}
std::string str_2_lower(std::string& str)
{
std::string strRet = str;
transform(strRet.begin(), strRet.end(), strRet.begin(), (int(*)(int))tolower);
return strRet;
}
std::string str_2_upper(std::string& str)
{
std::string strRet = str;
transform(strRet.begin(), strRet.end(), strRet.begin(), (int(*)(int))toupper);
return strRet;
}
bool str_cmp_nocase(std::string str1, std::string str2)
{
std::string strTmp1 = str_2_upper(str1);
std::string strTmp2 = str_2_upper(str2);
return strTmp1 == strTmp2;
}
wchar_t* char_2_wchar(const char* pStr)
{
wchar_t* pRet = nullptr;
if (nullptr != pStr)
{
size_t iLen = strlen(pStr);
if (iLen > 0)
{
iLen += 1;
fprintf(stderr, "char_2_wchar malloc\n");
pRet = (wchar_t*)do_malloc(iLen * sizeof(wchar_t));
if (nullptr != pRet)
{
#if defined(WIN32) || defined(_WIN32)
size_t converted = 0;
mbstowcs_s(&converted, pRet, iLen, pStr, _TRUNCATE);
#elif __linux__
mbstowcs(pRet, pStr, iLen - 1);
#endif
}
}
}
return pRet;
}
char* wchar_2_char(const wchar_t* pStr)
{
char* pRet = nullptr;
if (nullptr != pStr)
{
size_t iLen = wcslen(pStr);
if (iLen > 0)
{
iLen += 1;
fprintf(stderr, "wchar_2_char malloc\n");
pRet = (char*)do_malloc(iLen * sizeof(char));
if (nullptr != pRet)
{
#if defined(WIN32) || defined(_WIN32)
size_t converted = 0;
wcstombs_s(&converted, pRet, iLen, pStr, _TRUNCATE);
#elif __linux__
wcstombs(pRet, pStr, iLen - 1);
#endif
}
}
}
return pRet;
}
const long get_file_len(const char* pFileName, const char* pMode)
{
long lRet = 0;
FILE *pFile = safe_open_file(pFileName, pMode);
if (nullptr != pFile)
{
fseek(pFile, 0L, SEEK_END);
lRet = ftell(pFile);
fclose(pFile);
}
return lRet;
}
size_t file_write(const char* pFileName, const char* pMode, const unsigned char* pData, const size_t iLen)
{
size_t iWrite = 0;
do
{
if (nullptr == pData || iLen <= 0)
{
break;
}
FILE *pFile = safe_open_file(pFileName, pMode);
if (nullptr != pFile)
{
while (iWrite < iLen)
{
iWrite += fwrite((void*)(pData + iWrite), sizeof(unsigned char), iLen - iWrite, pFile);
}
fclose(pFile);
}
} while (0);
return iWrite;
}
size_t file_read(const char* pFileName, const char* pMode, unsigned char* pData, const size_t iLen, const long lOffset)
{
size_t iRead = 0;
do
{
if (nullptr == pData || iLen <= 0)
{
break;
}
FILE *pFile = safe_open_file(pFileName, pMode);
if (nullptr != pFile)
{
if (fseek(pFile, lOffset, SEEK_SET) != 0)
{
break;
}
while (iRead < iLen && !feof(pFile))
{
iRead += fread((void*)(pData + iRead), sizeof(unsigned char), 1, pFile);
}
fclose(pFile);
}
} while (0);
return iRead;
}
char* file_read(const char* pFileName, const char* pMode, size_t& lLen)
{
char* pRet = nullptr;
lLen = get_file_len(pFileName, pMode);
if (lLen > 0)
{
pRet = (char*)do_malloc(lLen);
if (pRet != nullptr)
{
FILE *pFile = safe_open_file(pFileName, pMode);
if (nullptr != pFile)
{
size_t iRead = 0;
while (iRead < lLen && !feof(pFile))
{
iRead += fread((void*)(pRet + iRead), sizeof(char), lLen - iRead, pFile);
}
fclose(pFile);
}
}
}
return pRet;
}
bool file_exits(const char* strFileName)
{
bool bRet = false;
#if defined(WIN32) || defined(_WIN32)
DWORD dwRet = GetFileAttributesA(strFileName);
bRet = (dwRet != INVALID_FILE_ATTRIBUTES) && (!(dwRet & FILE_ATTRIBUTE_DIRECTORY));
#elif __linux__
bRet = (access(strFileName, F_OK) == 0);
#endif
return bRet;
}
bool directory_exists(const char* strDirectory)
{
bool bRet = false;
#if defined(WIN32) || defined(_WIN32)
DWORD dwRet = GetFileAttributesA(strDirectory);
bRet = (dwRet != INVALID_FILE_ATTRIBUTES) && (dwRet & FILE_ATTRIBUTE_DIRECTORY);
#elif __linux__
struct stat sb;
bRet = (lstat(strDirectory, &sb) == 0 && S_ISDIR(sb.st_mode));
#endif
return bRet;
}
std::string get_app_path()
{
std::string strRet = "";
#if defined(WIN32) || defined(_WIN32)
char szExePath[MAX_PATH] = { 0 };
GetModuleFileNameA(nullptr, szExePath, MAX_PATH);
char *pstr = strrchr(szExePath, '\\');
memset(pstr + 1, '\0', 1);
strRet = szExePath;
#elif __linux__
#define MAX_PATH 0x100
char szFilePath[MAX_PATH];
int cnt = readlink("/proc/self/exe", szFilePath, MAX_PATH);
if (cnt < 0 || cnt >= MAX_PATH)
{
return strRet;
}
for (int i = cnt; i >= 0; --i)
{
if (szFilePath[i] == '/')
{
szFilePath[i + 1] = '\0';
break;
}
}
strRet = szFilePath;
#endif
return strRet;
}
FILE* safe_open_file(const char* pFileName, const char* pMode)
{
FILE* pRet = nullptr;
#if defined(WIN32) || defined(_WIN32)
/*if (0 != fopen_s(&pRet, pFileName, pMode))
{
pRet = nullptr;
}*/
//fopen_s(&pRet,pFileName, pMode);
pRet = fopen(pFileName, pMode);
#elif __linux__
pRet = fopen(pFileName, pMode);
#endif
//设置非缓冲
if (nullptr != pRet)
{
setvbuf(pRet, nullptr, _IONBF, 0);
}
return pRet;
}
len_str get_bmp(const void* pBmp, int width, int height, int bitCount)
{
len_str stImg;
memset(&stImg, 0, sizeof(len_str));
if (nullptr == pBmp)
{
return stImg;
}
int bmp_size = width*height*(bitCount / 8);
// 【写位图文件头】
BITMAPFILEHEADER bmpHeader;
bmpHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + bmp_size; // BMP图像文件的大小
bmpHeader.bfType = 0x4D42; // 位图类别,根据不同的操作系统而不同,在Windows中,此字段的值总为‘BM’
bmpHeader.bfOffBits = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER); // BMP图像数据的偏移位置
bmpHeader.bfReserved1 = 0; // 总为0
bmpHeader.bfReserved2 = 0; // 总为0
stImg.iLen = bmpHeader.bfSize;
fprintf(stderr, "get_bmp malloc\n");
stImg.pStr = (unsigned char*)do_malloc(sizeof(unsigned char)*bmpHeader.bfSize);
if (nullptr == stImg.pStr)
{
stImg.iLen = 0;
return stImg;
}
size_t iOffset = 0;
memcpy(stImg.pStr, &bmpHeader, sizeof(bmpHeader));
iOffset += sizeof(bmpHeader);
BITMAPINFOHEADER bmiHeader;
bmiHeader.biSize = sizeof(bmiHeader); // 本结构所占用字节数,即sizeof(BITMAPINFOHEADER);
bmiHeader.biWidth = width; // 位图宽度(单位:像素)
bmiHeader.biHeight = height; // 位图高度(单位:像素)
bmiHeader.biPlanes = 1; // 目标设备的级别,必须为1
bmiHeader.biBitCount = bitCount; // 像素的位数(每个像素所需的位数,范围:1、4、8、24、32)
bmiHeader.biCompression = 0; // 压缩类型(0:不压缩 1:BI_RLE8压缩类型 2:BI_RLE4压缩类型)
bmiHeader.biSizeImage = bmp_size; // 位图大小(单位:字节)
bmiHeader.biXPelsPerMeter = 0; // 水平分辨率(像素/米)
bmiHeader.biYPelsPerMeter = 0; // 垂直分辨率(像素/米)
bmiHeader.biClrUsed = 0; // 位图实际使用的彩色表中的颜色索引数
bmiHeader.biClrImportant = 0; // 对图象显示有重要影响的颜色索引的数目
// 【写位图信息头(BITMAPINFO的bmiHeader成员)】
memcpy(stImg.pStr + iOffset, &bmiHeader, sizeof(bmiHeader));
iOffset += sizeof(bmiHeader);
// 【写像素内容】
memcpy(stImg.pStr + iOffset, pBmp, bmp_size);
return stImg;
}
int make_dir(const char* path)
{
if (nullptr == path || strlen(path) <= 0)
{
return 1;
}
if(directory_exists(path))
{
return 0;
}
#if defined(WIN32) || defined(_WIN32)
if (!::CreateDirectoryA(path, nullptr))
{
return 1;
}
#elif __linux__
if (mkdir(path, 0755) != 0)
{
fprintf(stderr, "mkdir %s failed(%s)\n", path, strerror(errno));
return 1;
}
#endif
return 0;
}
int make_dirs(const char* path)
{
size_t len = 0;
if (nullptr == path || (len = strlen(path)) <= 0)
{
return 1;
}
fprintf(stderr, "make_dirs malloc\n");
char* pTmp = (char*)do_malloc(len + 3);
if (nullptr == pTmp)
{
return 1;
}
safe_snprintf(pTmp, len + 1, _TRUNCATE, "%s", path);
for (int i = 0; i < (int)len; i++)
{
if (pTmp[i] != '\\' && pTmp[i] != '/')
{
continue;
}
if (0 == i) {
continue;
}
pTmp[i] = '\0';
if (make_dir(pTmp) != 0)
{
break;
}
pTmp[i] = '/';
}
DOFREE(pTmp);
return make_dir(path);
}
int init_platform()
{
int iRet = 0;
#if defined(WIN32) || defined(_WIN32)
WSADATA wsd;
if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
{
iRet = 1;
}
_setmode(0, _O_BINARY);
_setmode(1, _O_BINARY);
_setmode(2, _O_BINARY);
#elif __linux__
signal(SIGPIPE, SIG_IGN);
#endif
//输出非缓冲
setvbuf(stdout, nullptr, _IONBF, 0);
setvbuf(stderr, nullptr, _IONBF, 0);
return iRet;
}
int uninit_platform()
{
#if defined(WIN32) || defined(_WIN32)
WSACleanup();
#elif __linux__
#endif
return 0;
}
void perror_desc(const char* pStr)
{
#if defined(WIN32) || defined(_WIN32)
wchar_t* pWstr = char_2_wchar(pStr);
if (nullptr == pWstr)
{
return;
}
wchar_t* pBuf = nullptr;
if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_MAX_WIDTH_MASK,
nullptr,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
pBuf,
0,
nullptr))
{
wchar_t* pFormat = char_2_wchar("%s: %s");
if (nullptr != pFormat)
{
fwprintf_s(stderr, pFormat, pWstr, pBuf);
fflush(stderr);
DOFREE(pFormat);
}
LocalFree((HLOCAL)pBuf);
}
else
{
wchar_t* pFormat = char_2_wchar("%s: unknown Windows error\n");
if (nullptr != pFormat)
{
fwprintf_s(stderr, pFormat, pWstr);
fflush(stderr);
DOFREE(pFormat);
}
}
DOFREE(pWstr);
#elif __linux__
if (nullptr != pStr)
{
perror(pStr);
}
#endif
}
以下是日志库
#ifndef __LOGMANAGER__H_
#define __LOGMANAGER__H_
#include <cstdio>
#include <queue>
#include <string>
#include "singleton.h"
#include "CMutex.h"
#include "util.h"
enum LogType{ LOG_TYPE_SCREEN = 0, LOG_TYPE_FILE, LOG_TYPE_TEE, LOG_TYPE_MAX};
enum LogLevel{
LOG_LEVEL_ERR = 0,
LOG_LEVEL_DBG,
LOG_LEVEL_FUNCTION,
LOG_LEVEL_INFO,
LOG_LEVEL_BLANK,
LOG_LEVEL_MAX
};
#define MAX_PER_LOGFILE_SIZE 500*1024*1024 //单个日志文件的大小为500M
#define MAX_PER_LINE_LOG 2048 //一行日志最大缓存
typedef void(*log_cb)(int iLevel, const char *pData);
struct tagLogItem
{
int iLevel;
std::string strLog;
};
class CLogmanger : public CSingleton<CLogmanger>
{
SINGLE_CLASS_INITIAL(CLogmanger);
public:
int Init(int iType, int iLevel, const char* szDir, log_cb pLogCb=nullptr);
~CLogmanger();
void AddLogItem(int iLevel, const char *format, ...);
int StopLog();
int SetLogType(int iType);
int SetLogLevel(int iLevel);
int SetLogPath(const char* pPath);
private:
int Check();
FILE* GetFile();
int PrintItem(tagLogItem& stLogItem);
private:
int miType;
int miLevel;
unsigned long mlCount;
bool mbInit;
std::string mstrDir;
FILE* mpFile;
FILE* mpTmpFile;
CMutex mcMutex;
log_cb mpLogCb;
};
#define sLog CLogmanger::Instance()
#define LOG_ERR(fmt, ...) sLog->AddLogItem(LOG_LEVEL_ERR, "[ERROR](%s:%s:%d)[Thread:%u] " fmt, __FILE__, __FUNCTION__, __LINE__, (unsigned int)get_thread_id_self(), ##__VA_ARGS__)
#define LOG_DBG(fmt, ...) sLog->AddLogItem(LOG_LEVEL_DBG, "[DEBUG](%s:%s:%d)[Thread:%u] " fmt, __FILE__, __FUNCTION__, __LINE__, (unsigned int)get_thread_id_self(), ##__VA_ARGS__)
#define LOG_FUNCTION(fmt, ...) sLog->AddLogItem(LOG_LEVEL_FUNCTION, "[FUNCTION](%s)[Thread:%u] " fmt, __FUNCTION__, (unsigned int)get_thread_id_self(), ##__VA_ARGS__)
#define LOG_INFO(fmt, ...) sLog->AddLogItem(LOG_LEVEL_INFO, "[Thread:%u] " fmt, (unsigned int)get_thread_id_self(), ##__VA_ARGS__)
#define LOG_BLANK(fmt, ...) sLog->AddLogItem(LOG_LEVEL_BLANK, fmt, ##__VA_ARGS__)
#define ASSERT(expr) do{if (!(expr)){LOG_ERR("assert \"%s\" failed", #expr);}}while(0)
#define ASSERT_RET(expr) do{if (!(expr)){LOG_ERR("assert \"%s\" failed", #expr); return ; }}while(0)
#define ASSERT_RET_VALUE(expr, retval) do{if (!(expr)){LOG_ERR("assert \"%s\" failed", #expr); return retval; }}while(0)
#define ASSERT_ERR_MSG(expr, fmt, ...) \
do \
{ \
if (!(expr)) \
{ \
LOG_ERR("assert \"%s\", msg:" fmt, #expr, ##__VA_ARGS__); \
} \
} while (0);
#define ASSERT_ERR_MSG_RET(expr, retval, fmt, ...) \
do \
{ \
if (!(expr)) \
{ \
LOG_ERR("assert \"%s\", msg:" fmt, #expr, ##__VA_ARGS__); \
return retval; \
} \
} while (0);
#endif
#include "CLogmanager.h"
#define RESET 0
#define BRIGHT 1
#define DIM 2
#define UNDERLINE 4
#define BLINK 5
#define REVERSE 7
#define HIDDEN 8
#define BLACK 0
#define RED 1
#define GREEN 2
#define YELLOW 3
#define BLUE 4
#define MAGENTA 5
#define CYAN 6
#define WHITE 7
#if defined(WIN32) || defined(_WIN32)
#elif __linux__
#define COLOR_PRINT_BEGIN \033
#define COLOR_PRINT_END \033[0m
#endif
CLogmanger::CLogmanger()
{
miLevel = LOG_LEVEL_ERR;
miType = LOG_TYPE_SCREEN;
mlCount = 0;
mstrDir = "";
mbInit = false;
mpFile = stdout;
mpTmpFile = nullptr;
mpLogCb = nullptr;
}
CLogmanger::~CLogmanger()
{
if (nullptr != mpFile)
{
fclose(mpFile);
mpFile = nullptr;
}
if (nullptr != mpTmpFile)
{
fclose(mpTmpFile);
mpTmpFile = nullptr;
}
}
FILE* CLogmanger::GetFile()
{
if (mpTmpFile != nullptr && mpTmpFile != stdout)
{
fclose(mpTmpFile);
mpTmpFile = nullptr;
}
mpTmpFile = mpFile;
if (miType == LOG_TYPE_FILE || miType == LOG_TYPE_TEE)
{
struct systemtime_t stNow = get_now_time();
char szFileName[1000];
safe_snprintf(szFileName, 1000, _TRUNCATE, "%02d-%02d-%02d-%02d-%02d-%02d.log", stNow.tmyear, stNow.tmmon, stNow.tmmday, stNow.tmhour, stNow.tmmin, stNow.tmsec);
std::string strTmp = mstrDir;
if (!str_end_with(strTmp, "\\") && !str_end_with(strTmp, "/"))
{
strTmp += "/";
}
strTmp += szFileName;
FILE* pTmpTile = safe_open_file(strTmp.c_str(), "a+");
if (nullptr == pTmpTile)
{
fprintf(stderr, "open log file err:%s", strTmp.c_str());
mpFile = stdout;
}
else
{
mpFile = pTmpTile;
}
}
else
{
mpFile = stdout;
}
return mpFile;
}
int CLogmanger::Check()
{
//文件过大创建新文件
if (miType == LOG_TYPE_FILE || miType == LOG_TYPE_TEE)
{
if (mlCount >= MAX_PER_LOGFILE_SIZE)
{
mcMutex.Lock();
if (mlCount >= MAX_PER_LOGFILE_SIZE)
{
mlCount = 0;
GetFile();
}
mcMutex.UnLock();
}
}
return 0;
}
int CLogmanger::SetLogType(int iType)
{
if (!mbInit || iType < LOG_TYPE_SCREEN || iType >= LOG_TYPE_MAX)
{
return -1;
}
if (iType == miType)
{
return 0;
}
miType = iType;
GetFile();
return 0;
}
int CLogmanger::SetLogLevel(int iLevel)
{
if (!mbInit || iLevel < LOG_LEVEL_ERR)
{
return -1;
}
miLevel = iLevel;
return 0;
}
int CLogmanger::SetLogPath(const char* pPath)
{
if (!mbInit || nullptr == pPath || str_cmp(pPath, mstrDir.c_str(), true))
{
return 1;
}
mstrDir = pPath;
GetFile();
return 0;
}
void CLogmanger::AddLogItem(int iLevel, const char *format, ...)
{
if (nullptr != mpLogCb)
{
va_list arg;
va_start(arg, format);
int nPos = 0;
char szBuf[MAX_PER_LINE_LOG];
#if defined(WIN32) || defined(_WIN32)
nPos += vsnprintf_s(szBuf + nPos, MAX_PER_LINE_LOG - nPos - 1, _TRUNCATE, format, arg);
#elif __linux__
nPos += vsnprintf(szBuf + nPos, MAX_PER_LINE_LOG - nPos - 1, format, arg);
#endif
if (nPos > MAX_PER_LINE_LOG - 1)
{
nPos = MAX_PER_LINE_LOG - 1;
}
if (nPos > 0)
{
szBuf[nPos] = '\0';
}
va_end(arg);
mpLogCb(iLevel, szBuf);
return;
}
if (iLevel > miLevel || iLevel > LOG_LEVEL_MAX || !mbInit)
{
return;
}
char szBuf[MAX_PER_LINE_LOG];
memset(szBuf, 0, sizeof(szBuf));
int nPos = 0;
if (iLevel <= LOG_LEVEL_INFO)
{
struct systemtime_t stNow = get_now_time();
nPos = safe_snprintf(szBuf, MAX_PER_LINE_LOG - 1, _TRUNCATE, "[%02d/%02d/%02d %02d:%02d:%02d.%06d] ", stNow.tmyear, stNow.tmmon, stNow.tmmday, stNow.tmhour, stNow.tmmin, stNow.tmsec, stNow.tmmilliseconds);
}
va_list ap;
va_start(ap, format);
#if defined(WIN32) || defined(_WIN32)
nPos += vsnprintf_s(szBuf + nPos, MAX_PER_LINE_LOG - nPos - 1, _TRUNCATE, format, ap);
#elif __linux__
nPos += vsnprintf(szBuf + nPos, MAX_PER_LINE_LOG - nPos - 1, format, ap);
#endif
va_end(ap);
if (nPos > MAX_PER_LINE_LOG - 1)
{
nPos = MAX_PER_LINE_LOG - 1;
}
if (nPos > 0)
{
szBuf[nPos] = '\0';
}
tagLogItem stLog;
stLog.iLevel = iLevel;
stLog.strLog = szBuf;
PrintItem(stLog);
}
int CLogmanger::Init(int iType, int iLevel, const char* szDir, log_cb pLogCb)
{
if (nullptr != pLogCb)
{
mpLogCb = pLogCb;
return 0;
}
if (mbInit)
{
return 1;
}
miType = iType;
miLevel = iLevel;
if (nullptr != szDir && strlen(szDir) > 0 && !str_cmp(szDir, ".", true))
{
mstrDir = szDir;
}
else
{
mstrDir = get_app_path();
mstrDir += "log";
}
if (make_dirs(mstrDir.c_str()))
{
fprintf(stderr, "make_dirs error!");
}
GetFile();
if (nullptr == mpFile)
{
return 1;
}
mbInit = true;
return 0;
}
int CLogmanger::StopLog()
{
mbInit = false;
return 0;
}
int CLogmanger::PrintItem(tagLogItem& stLogItem)
{
#if defined(WIN32) || defined(_WIN32)
if (miType == LOG_TYPE_SCREEN && stLogItem.iLevel == LOG_LEVEL_ERR)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_INTENSITY);
}
#endif
fprintf(mpFile, "%s\n", stLogItem.strLog.c_str());
if (miType == LOG_TYPE_TEE)
{
fprintf(stdout, "%s\n", stLogItem.strLog.c_str());
}
#if defined(WIN32) || defined(_WIN32)
if (miType == LOG_TYPE_SCREEN && stLogItem.iLevel == LOG_LEVEL_ERR)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);
}
#endif
atomic_change((long*)&mlCount, (int)stLogItem.strLog.size());
return Check();
}