teleport/common/libex/src/ex_log.cpp

707 lines
15 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include <ex/ex_log.h>
#include <ex/ex_path.h>
//#include <ex/ex_thread.h>
//#include <vector>
//#include <deque>
//#include <algorithm>
#ifdef EX_OS_WIN32
# include <io.h>
# include <stdio.h>
# include <direct.h>
#else
//# include <dirent.h>
//# include <sys/time.h>
#endif
#define EX_LOG_CONTENT_MAX_LEN 2048
//typedef std::deque<unsigned long long> log_file_deque;
static ExLogger* g_exlog = NULL;
void EXLOG_USE_LOGGER(ExLogger* logger)
{
g_exlog = logger;
}
void EXLOG_LEVEL(int min_level)
{
if(NULL != g_exlog)
g_exlog->min_level = min_level;
}
void EXLOG_DEBUG(bool debug_mode)
{
if (NULL != g_exlog)
g_exlog->debug_mode = debug_mode;
}
void EXLOG_CONSOLE(bool output_to_console)
{
if(NULL != g_exlog)
g_exlog->to_console = output_to_console;
}
void EXLOG_FILE(const wchar_t* log_file, const wchar_t* log_path /*= NULL*/, ex_u32 max_filesize /*= EX_LOG_FILE_MAX_SIZE*/, ex_u8 max_filecount /*= EX_LOG_FILE_MAX_COUNT*/)
{
if(NULL == g_exlog)
return;
ex_wstr _path;
if (NULL == log_path)
{
ex_exec_file(_path);
ex_dirname(_path);
ex_path_join(_path, false, L"log", NULL);
}
else
{
_path = log_path;
}
g_exlog->set_log_file(_path, log_file, max_filesize, max_filecount);
}
ExLogger::ExLogger()
{
#ifdef EX_OS_WIN32
console_handle = GetStdHandle(STD_OUTPUT_HANDLE);
#endif
min_level = EX_LOG_LEVEL_INFO;
debug_mode = false;
to_console = true;
m_file = NULL;
m_filesize = 0;
}
ExLogger::~ExLogger()
{
if (NULL != m_file)
{
#ifdef EX_OS_WIN32
CloseHandle(m_file);
#else
fclose(m_file);
#endif
m_file = NULL;
}
}
void ExLogger::log_a(int level, const char* fmt, va_list valist)
{
if (NULL == fmt)
return;
if (0 == strlen(fmt))
return;
char szTmp[4096] = { 0 };
size_t offset = 0;
if (level == EX_LOG_LEVEL_ERROR)
{
szTmp[0] = '[';
szTmp[1] = 'E';
szTmp[2] = ']';
szTmp[3] = ' ';
offset = 4;
}
#ifdef EX_OS_WIN32
vsnprintf_s(szTmp+offset, 4096-offset, 4095-offset, fmt, valist);
if(to_console)
{
if (NULL != console_handle)
{
printf_s("%s", szTmp);
fflush(stdout);
}
else
{
if(debug_mode)
OutputDebugStringA(szTmp);
}
}
#else
vsnprintf(szTmp+offset, 4095-offset, fmt, valist);
if(to_console)
{
// On linux, the stdout only output the first time output format (char or wchar_t).
// e.g.: first time you use printf(), then after that, every wprintf() not work, and vice versa.
// so we always use wprintf() to fix that.
ex_astr tmp(szTmp);
ex_wstr _tmp;
ex_astr2wstr(tmp, _tmp);
wprintf(L"%ls", _tmp.c_str());
fflush(stdout);
// printf("%s", szTmp);
// fflush(stdout);
}
#endif
write_a(szTmp);
}
void ExLogger::log_w(int level, const wchar_t* fmt, va_list valist)
{
if (NULL == fmt || 0 == wcslen(fmt))
return;
wchar_t szTmp[4096] = { 0 };
size_t offset = 0;
if (level == EX_LOG_LEVEL_ERROR)
{
szTmp[0] = L'[';
szTmp[1] = L'E';
szTmp[2] = L']';
szTmp[3] = L' ';
offset = 4;
}
#ifdef EX_OS_WIN32
_vsnwprintf_s(szTmp+offset, 4096-offset, 4095-offset, fmt, valist);
if(to_console)
{
if (NULL != console_handle)
{
wprintf_s(_T("%s"), szTmp);
fflush(stdout);
}
else
{
if(debug_mode)
OutputDebugStringW(szTmp);
}
}
#else
vswprintf(szTmp+offset, 4095-offset, fmt, valist);
if(to_console)
{
wprintf(L"%ls", szTmp);
fflush(stdout);
}
#endif
write_w(szTmp);
}
#define EX_PRINTF_XA(fn, level) \
void fn(const char* fmt, ...) \
{ \
if(NULL == g_exlog) \
return; \
if (g_exlog->min_level > level) \
return; \
ExThreadSmartLock locker(g_exlog->lock); \
va_list valist; \
va_start(valist, fmt); \
g_exlog->log_a(level, fmt, valist); \
va_end(valist); \
}
#define EX_PRINTF_XW(fn, level) \
void fn(const wchar_t* fmt, ...) \
{ \
if(NULL == g_exlog) \
return; \
if (g_exlog->min_level > level) \
return; \
ExThreadSmartLock locker(g_exlog->lock); \
va_list valist; \
va_start(valist, fmt); \
g_exlog->log_w(level, fmt, valist); \
va_end(valist); \
}
EX_PRINTF_XA(ex_printf_d, EX_LOG_LEVEL_DEBUG)
EX_PRINTF_XA(ex_printf_v, EX_LOG_LEVEL_VERBOSE)
EX_PRINTF_XA(ex_printf_i, EX_LOG_LEVEL_INFO)
EX_PRINTF_XA(ex_printf_w, EX_LOG_LEVEL_WARN)
EX_PRINTF_XA(ex_printf_e, EX_LOG_LEVEL_ERROR)
EX_PRINTF_XW(ex_printf_d, EX_LOG_LEVEL_DEBUG)
EX_PRINTF_XW(ex_printf_v, EX_LOG_LEVEL_VERBOSE)
EX_PRINTF_XW(ex_printf_i, EX_LOG_LEVEL_INFO)
EX_PRINTF_XW(ex_printf_w, EX_LOG_LEVEL_WARN)
EX_PRINTF_XW(ex_printf_e, EX_LOG_LEVEL_ERROR)
#ifdef EX_OS_WIN32
void ex_printf_e_lasterror(const char* fmt, ...)
{
ExThreadSmartLock locker(g_exlog->lock);
va_list valist;
va_start(valist, fmt);
g_exlog->log_a(EX_LOG_LEVEL_ERROR, fmt, valist);
va_end(valist);
//=========================================
LPVOID lpMsgBuf;
DWORD dw = GetLastError();
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR)&lpMsgBuf, 0, NULL);
ex_printf_e(" - WinErr(%d): %s\n", dw, (LPSTR)lpMsgBuf);
LocalFree(lpMsgBuf);
}
void ex_printf_e_lasterror(const wchar_t* fmt, ...)
{
ExThreadSmartLock locker(g_exlog->lock);
va_list valist;
va_start(valist, fmt);
g_exlog->log_w(EX_LOG_LEVEL_ERROR, fmt, valist);
va_end(valist);
//=========================================
LPVOID lpMsgBuf;
DWORD dw = GetLastError();
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR)&lpMsgBuf, 0, NULL);
ex_printf_e(" - WinErr(%d): %s\n", dw, (LPSTR)lpMsgBuf);
LocalFree(lpMsgBuf);
}
#endif
void ex_printf_bin(const ex_u8* bin_data, size_t bin_size, const char* fmt, ...)
{
if(NULL == g_exlog)
return;
if (!g_exlog->debug_mode)
return;
ExThreadSmartLock locker(g_exlog->lock);
va_list valist;
va_start(valist, fmt);
g_exlog->log_a(EX_LOG_LEVEL_DEBUG, fmt, valist);
va_end(valist);
ex_printf_d(" (%d/0x%02x Bytes)\n", bin_size, bin_size);
const ex_u8* line = bin_data;
size_t thisline = 0;
size_t offset = 0;
unsigned int i = 0;
char szTmp[128] = { 0 };
size_t _offset = 0;
while (offset < bin_size)
{
memset(szTmp, 0, 128);
_offset = 0;
snprintf(szTmp + _offset, 128 - _offset, "%06x ", (int)offset);
_offset += 8;
thisline = bin_size - offset;
if (thisline > 16)
thisline = 16;
for (i = 0; i < thisline; i++)
{
snprintf(szTmp + _offset, 128 - _offset, "%02x ", line[i]);
_offset += 3;
}
snprintf(szTmp + _offset, 128 - _offset, " ");
_offset += 2;
for (; i < 16; i++)
{
snprintf(szTmp + _offset, 128 - _offset, " ");
_offset += 3;
}
for (i = 0; i < thisline; i++)
{
snprintf(szTmp + _offset, 128 - _offset, "%c", (line[i] >= 0x20 && line[i] < 0x7f) ? line[i] : '.');
_offset += 1;
}
snprintf(szTmp + _offset, 128 - _offset, "\n");
_offset += 1;
ex_printf_d("%s", szTmp);
offset += thisline;
line += thisline;
}
fflush(stdout);
}
bool ExLogger::set_log_file(const ex_wstr& log_path, const ex_wstr& log_name, ex_u32 max_filesize, ex_u8 max_count)
{
m_max_filesize = max_filesize;
m_max_count = max_count;
m_filename = log_name;
m_path = log_path;
ex_abspath(m_path);
ex_mkdirs(m_path);
m_fullname = m_path;
ex_path_join(m_fullname, false, log_name.c_str(), NULL);
return _open_file();
}
bool ExLogger::_open_file()
{
if (m_file)
{
#ifdef EX_OS_WIN32
CloseHandle(m_file);
#else
fclose(m_file);
#endif
m_file = NULL;
}
#ifdef EX_OS_WIN32
// ע<><EFBFBD><E2A3BA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9> CreateFile() <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>־<EFBFBD>ļ<EFBFBD><C4BC><EFBFBD>ʹ<EFBFBD><CAB9>FILEָ<45><D6B8><EFBFBD>޷<EFBFBD><DEB7><EFBFBD><EFBFBD>ݸ<EFBFBD><DDB8><EFBFBD>̬<EFBFBD><CCAC><EFBFBD><EFBFBD><EFBFBD>в<EFBFBD><D0B2><EFBFBD><EFBFBD><EFBFBD>
m_file = CreateFileW(m_fullname.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE == m_file)
{
m_file = NULL;
return false;
}
SetFilePointer(m_file, 0, NULL, FILE_END);
m_filesize = GetFileSize(m_file, NULL);
#else
ex_astr _fullname;
ex_wstr2astr(m_fullname, _fullname);
m_file = fopen(_fullname.c_str(), "a");
if (NULL == m_file)
{
return false;
}
fseek(m_file, 0, SEEK_END);
m_filesize = (ex_u32)ftell(m_file);
#endif
return _rotate_file();
}
bool ExLogger::_rotate_file(void)
{
if (m_filesize < m_max_filesize)
return true;
if (m_file)
{
#ifdef EX_OS_WIN32
CloseHandle(m_file);
#else
fclose(m_file);
#endif
m_file = NULL;
}
//if (!_backup_file())
// return false;
// make a name for backup file.
wchar_t _tmpname[64] = { 0 };
#ifdef EX_OS_WIN32
SYSTEMTIME st;
GetLocalTime(&st);
//StringCbPrintf(_tmpname, 64, L"%s.%04d%02d%02d%02d%02d%02d.bak", m_filename.c_str(), st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
swprintf_s(_tmpname, 64, L"%s.%04d%02d%02d%02d%02d%02d.bak", m_filename.c_str(), st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
// sprintf_s(szBaseNewFileLogName, EX_LOG_PATH_MAX_LEN, "%04d%02d%02d%02d%02d%02d",
// st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
#else
time_t timep;
time(&timep);
struct tm *p = localtime(&timep);
if (p == NULL)
return false;
//swprintf(_tmpname, L"%ls.%04d%02d%02d%02d%02d%02d.bak", m_filename.c_str(), p->tm_year + 1900, p->tm_mon + 1, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec);
ex_wcsformat(_tmpname, 64, L"%ls.%04d%02d%02d%02d%02d%02d.bak", m_filename.c_str(), p->tm_year + 1900, p->tm_mon + 1, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec);
// sprintf(szBaseNewFileLogName, "%04d%02d%02d%02d%02d%02d",
// p->tm_year + 1900, p->tm_mon + 1, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec);
#endif
ex_wstr _new_fullname(m_path);
ex_path_join(_new_fullname, false, _tmpname, NULL);
#ifdef EX_OS_WIN32
if (!MoveFileW(m_fullname.c_str(), _new_fullname.c_str()))
{
EXLOGE_WIN("can not rename log file, remove old one and try again.");
DeleteFileW(_new_fullname.c_str());
if (!MoveFileW(m_fullname.c_str(), _new_fullname.c_str()))
return false;
}
#else
ex_astr _a_fullname;
ex_astr _a_new_fullname;
ex_wstr2astr(m_fullname, _a_fullname);
ex_wstr2astr(_new_fullname, _a_new_fullname);
if (rename(_a_fullname.c_str(), _a_new_fullname.c_str()) != 0)
{
remove(_a_new_fullname.c_str());
if (0 != (rename(_a_fullname.c_str(), _a_new_fullname.c_str())))
return false;
}
#endif
return _open_file();
}
#if 0
bool ExLogFile::_backup_file()
{
char szNewFileLogName[EX_LOG_PATH_MAX_LEN] = { 0 };
char szBaseNewFileLogName[EX_LOG_PATH_MAX_LEN] = { 0 };
#ifdef EX_OS_WIN32
SYSTEMTIME st;
GetLocalTime(&st);
sprintf_s(szNewFileLogName, EX_LOG_PATH_MAX_LEN, "%s\\%04d%02d%02d%02d%02d%02d.log",
m_log_file_dir.c_str(), st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
sprintf_s(szBaseNewFileLogName, EX_LOG_PATH_MAX_LEN, "%04d%02d%02d%02d%02d%02d",
st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
#else
time_t timep;
struct tm *p;
time(&timep);
p = localtime(&timep); //get server's time
if (p == NULL)
{
return NULL;
}
sprintf(szNewFileLogName, "%s/%04d%02d%02d%02d%02d%02d.log",
m_log_file_dir.c_str(), p->tm_year + 1900, p->tm_mon + 1, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec);
sprintf(szBaseNewFileLogName, "%04d%02d%02d%02d%02d%02d",
p->tm_year + 1900, p->tm_mon + 1, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec);
#endif
if (m_hFile)
{
fclose(m_hFile);
m_hFile = 0;
}
#ifdef EX_OS_WIN32
if (!MoveFileA(m_path.c_str(), szNewFileLogName))
{
DWORD dwError = GetLastError();
DeleteFileA(szNewFileLogName);
MoveFileA(m_path.c_str(), szNewFileLogName);
}
#else
if (rename(m_path.c_str(), szNewFileLogName) != 0)
{
remove(szNewFileLogName);
rename(m_path.c_str(), szNewFileLogName);
}
#endif
unsigned long long value = atoll(szBaseNewFileLogName);
if (value != 0)
{
m_log_file_list.push_back(value);
}
int try_count = 0;
while ((m_log_file_list.size() > m_max_count))
{
unsigned long long value = m_log_file_list.front();
char szDeleteFile[256] = { 0 };
#ifdef EX_OS_WIN32
sprintf_s(szDeleteFile, 256, "%s\\%llu.log", m_log_file_dir.c_str(), value);
if (DeleteFileA(szDeleteFile))
{
m_log_file_list.pop_front();
}
#else
sprintf(szDeleteFile, "%s/%llu.log", m_log_file_dir.c_str(), value);
if (remove(szDeleteFile) == 0)
{
m_log_file_list.pop_front();
}
#endif
else
{
if (try_count > 5)
{
break;
}
try_count++;
}
}
return true;
}
#endif // if 0
bool ExLogger::write_a(const char* buf)
{
if (NULL == m_file)
return false;
size_t len = strlen(buf);
if (len > EX_LOG_CONTENT_MAX_LEN)
return false;
char szTime[100] = { 0 };
#ifdef EX_OS_WIN32
SYSTEMTIME st;
GetLocalTime(&st);
sprintf_s(szTime, 100, "[%04d-%02d-%02d %02d:%02d:%02d] ", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
int lenTime = strlen(szTime);
DWORD dwWritten = 0;
WriteFile(m_file, szTime, lenTime, &dwWritten, NULL);
m_filesize += lenTime;
WriteFile(m_file, buf, len, &dwWritten, NULL);
m_filesize += len;
FlushFileBuffers(m_file);
#else
time_t timep;
struct tm *p;
time(&timep);
p = localtime(&timep);
if (p == NULL)
return false;
sprintf(szTime, "[%04d-%02d-%02d %02d:%02d:%02d] ", p->tm_year + 1900, p->tm_mon + 1, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec);
size_t lenTime = strlen(szTime);
fwrite(szTime, lenTime, 1, m_file);
m_filesize += lenTime;
fwrite(buf, len, 1, m_file);
m_filesize += len;
fflush(m_file);
#endif
return _rotate_file();
}
bool ExLogger::write_w(const wchar_t* buf)
{
ex_astr _buf;
ex_wstr2astr(buf, _buf, EX_CODEPAGE_UTF8);
return write_a(_buf.c_str());
}
#if 0
bool ExLogFile::_load_file_list()
{
#ifdef EX_OS_WIN32
struct _finddata_t data;
ex_astr log_match = m_log_file_dir;
log_match += "\\*.log";
//log_match += "*.log";
long hnd = _findfirst(log_match.c_str(), &data); // find the first file match `*.log`
if (hnd < 0)
{
return false;
}
int nRet = (hnd < 0) ? -1 : 1;
int count = 0;
while (nRet > 0)
{
if (data.attrib == _A_SUBDIR)
{
// do nothing to a folder.
}
else
{
if (m_filename.compare(data.name) == 0)
{
}
else
{
char* match = strrchr(data.name, '.');
if (match != NULL)
{
*match = '\0';
}
unsigned long long value = atoll(data.name);
if (value == 0)
{
continue;
}
m_log_file_list.push_back(value);
}
}
nRet = _findnext(hnd, &data);
count++;
if (count > 100)
{
break;
}
}
_findclose(hnd);
#else
DIR *dir;
struct dirent *ptr;
dir = opendir(m_log_file_dir.c_str());
while ((ptr = readdir(dir)) != NULL)
{
if (ptr->d_type == 8)
{
char temp_file_name[PATH_MAX] = { 0 };
strcpy(temp_file_name, ptr->d_name);
if (m_filename.compare(temp_file_name) == 0)
{
}
else
{
char* match = strrchr(temp_file_name, '.');
if (match != NULL)
{
*match = '\0';
}
unsigned long long value = atoll(temp_file_name);
if (value == 0)
{
continue;
}
m_log_file_list.push_back(value);
}
}
}
closedir(dir);
#endif // EX_OS_WIN32
std::sort(m_log_file_list.begin(), m_log_file_list.end(), std::less<unsigned long long>());
return true;
}
#endif // if 0