logger(一):C++ & logger模塊配置及開發

該logger模塊是一種本地緩存形式的logger處理方法:用於相關軟件開發過程中的日誌記錄;適用於windows和linux

Linux下的主要使用的是syslog-ng庫,以下貼出源碼,詳情請下載開發包。


 logger.h

-----------------------------------------------------------------------------------------------------------------------------

/*
* logger.h
* Author: ws
* Data: 2015-12-24
*/
#ifndef WS_LOGGER_H_
#define WS_LOGGER_H_


#include <cstdlib>
#include <cstdio>
#include <string>


//------------------------------------------------------------------------
// 日誌系統
// 根據不同的系統使用不同的庫
// 若是windows系統使用宏#define WIN32
// 若是linux系統中舊的log日誌系統,使用宏#define L_LOG_OLD
// 若是linux系統中的syslog-ng日誌系統(需要單獨安裝及配置),使用宏#define L_LOG_NG
//------------------------------------------------------------------------


//#define WIN32 // 表示使用的是windows系統
//#define L_LOG_OLD // 表示使用的是linux舊的syslog庫
#define L_LOG_NG // 表示使用的是linux新的syslog-ng庫,syslog的升級版




//-----------------------------------------------------------------------
// 聲明部分
//-----------------------------------------------------------------------
# define MAXLOGLINE1024


#ifdef WIN32  // windows
//#define FOLDER_PATH // 表示日誌地址是使用系統路徑
#define MODEL_PATH  // 表示日誌地址是進程所在的文件路徑
#include <fstream>
#include <shlwapi.h>
enum LogLevel {
Fatal = 0,//0  最高級,致命的
Alert = 1,//1 最高級,必須採取措施
Crit = 2,//2  最高級,臨界狀態
Error = 3,//3  高級,錯誤
Warn = 4,//4  高級,警告
Notice = 5, //5  高級,正常但重要
Info = 6,//6  高級,一般信息
Debug = 7,//7  一般,調試信息
};
#else   // "Linux syslog"(L_LOG_OLD)  Or  "linux syslog-ng"(L_LOG_NG)
#include <syslog.h>
enum LogLevel {
Fatal = LOG_EMERG,//0  最高級,致命的
Alert = LOG_ALERT,//1 最高級,必須採取措施
Crit = LOG_CRIT,//2  最高級,臨界狀態
Error = LOG_ERR,//3  高級,錯誤
Warn = LOG_WARNING,//4  高級,警告
Notice = LOG_NOTICE, //5  高級,正常但重要
Info = LOG_INFO,//6  高級,一般信息
Debug = LOG_DEBUG,//7  一般,調試信息
};
#endif




//--------------------------------------------------------------
// 日誌功能 
//--------------------------------------------------------------
extern int syslogDevLevel; // 默認日誌等級(默認等級debug)
void initLog(); // 初始化系統記錄器
void logxxx(int l, char *file, int line, const char *fmt, ...); // 打印日誌
void closeLog(); // 關閉log系統
int setLogLevel(); // 獲取日誌等級
int setLogLevel(int l); // 設置日誌等級,返回舊的等級(這裏不修改環境變量,防止更改所有進程的輸出等級)




// 日誌檢測平臺接入
struct ISyslogObserver {
virtual void watch(int level, const char *file, int line, const char *fmt, ...) = 0;
virtual bool interested(int level) = 0; // 判斷是否對當前Level感興趣,感興趣才傳遞過去
virtual ~ISyslogObserver(){}
};
extern ISyslogObserver * SyslogWatcher;  // 系統日誌檢測
extern ISyslogObserver * logStatWatcher; // 日誌狀態檢測






// 日誌
#define log(l, ...) \
if (SyslogWatcher && SyslogWatcher->interested(l)) { SyslogWatcher->watch(l, __FILE__, __LINE__, __VA_ARGS__); } \
if (logStatWatcher && logStatWatcher->interested(l)) { logStatWatcher->watch(l, __FILE__, __LINE__, __VA_ARGS__); } \
if (syslogDevLevel >= l) {logxxx(l, (char *)__FILE__, __LINE__, __VA_ARGS__); }






#endif // WS_LOGGER_H_








 logger.cpp

--------------------------------------------------------------------------------------------------------------------------------------------

/*
* logger.cpp
* Author: ws
* Data: 2015-12-24
*/


//--------------------------------------------------------------
// 日誌系統 
//--------------------------------------------------------------
#include "logger.h"
#include <iostream>
#include <stdarg.h>
#include <ctime>
#include <cassert>
#include <stdlib.h>


#ifdef WIN32 // windows
#include <tchar.h>
#include <shlobj.h>
#include <windows.h>
#else // linux
#include <syslog.h>
#ifdef L_LOG_NG // linux syslog-ng
#include "syslog-nb.h"
#endif
#endif




#include <iostream>
using namespace std;


//--------------------------------------------------------------
// 全局變量
//--------------------------------------------------------------
ISyslogObserver * SyslogWatcher = NULL;
ISyslogObserver * logStatWatcher = NULL;




char * pDevLevel = getenv("DEV_LOG_LEVEL");  // 獲取環境變量,系統默認日誌等級
int syslogDevLevel = (NULL == pDevLevel) ? Debug : atol(pDevLevel);
// 獲取日誌等級
int setLogLevel()
{
return  syslogDevLevel;
}
// 設置日誌等級,返回舊的等級(這裏不修改環境變量,防止更改所有進程的輸出等級)
int setLogLevel(int l)
{
int old_level = syslogDevLevel;
syslogDevLevel = l;
return old_level;
}






#ifdef WIN32 // windows
std::ofstream fout;  // 文件句柄
static long __log_lock = 0; //加鎖計數器
// 等級名稱
const char * levelnames[] = { "Fatal - ", "Alert  - ", "Crit  - ", "Error - ", "Warn - ", "Notice - ", "Info - ", "Debug - ", "      - " };
const char * LevelToName(int l) {
return levelnames[l];
}


// 獲取當前時間
std::string safe_time()
{
time_t t = time(NULL);
char *ct = ctime(&t);
if(ct){
size_t len = strlen(ct);
if(len > 0){
ct[len - 1] = '\0';
return std::string(ct, len - 1);
}
}
return "empty time";
}


// 長字符串轉短
std::string Wchar2Ansi(const wchar_t* pStr, int len)
{
int nChars = 0;
std::string buf;


if (pStr == NULL)
{
assert(NULL);
return buf;
}


if (len < 0 && len != -1)
{
assert(NULL);
//OutputDebugStringW(_T("Invalid string length: ") + len); // 調試器
return buf;
}


// 長轉短
nChars = WideCharToMultiByte(CP_ACP, 0, pStr, len, NULL, 0, NULL, NULL);
if (len == -1)
--nChars;
if (nChars == 0)
return "";


buf.resize(nChars);
WideCharToMultiByte(CP_ACP, 0, pStr, len, const_cast<char*>(buf.c_str()), nChars, NULL, NULL);


return buf;
}


// 短轉長
std::wstring Ansi2WChar(LPCSTR pszSrc)
{
int nSize = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pszSrc, _tcslen(pszSrc), 0, 0);
if (nSize <= 0) return NULL;


WCHAR *pwszDst = new WCHAR[nSize + 1];
if (NULL == pwszDst) return NULL;


MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pszSrc, _tcslen(pszSrc), pwszDst, nSize);
pwszDst[nSize] = 0;


if (pwszDst[0] == 0xFEFF)                // skip Oxfeff
{
for (int i = 0; i < nSize; i++)
{
pwszDst[i] = pwszDst[i + 1];
}
}


std::wstring wcharString(pwszDst);
delete pwszDst;
return wcharString;
}




// 解析出文件路徑
std::string GetFileDirFromPath(LPCTSTR filepath)
{
unsigned int orgLen = _tcslen(filepath);
TCHAR end = filepath[orgLen - 1];
if (end != _T('\\') && end != _T(':'))
{
LPCTSTR FN = PathFindFileName(filepath);  // 獲取路徑中的文件名
return std::string(filepath, orgLen - _tcslen(FN));
}
else
{
return std::string(filepath);
}
}




// 獲取當前模塊進程文件所在的路徑
std::string GetModulePathDir()
{
TCHAR buffer[MAX_PATH];
ZeroMemory(buffer, sizeof(TCHAR) * MAX_PATH);
::GetModuleFileName(NULL, buffer, MAX_PATH); // 獲取當前進程模塊的文件名,含路徑
std::string path = GetFileDirFromPath(buffer); // 獲取文件路徑
return path;
}


// 字符串中特殊字符替換
static std::string TransformInstallPath2FolderName(std::string const& strPath)
{
std::string strReturn;
strReturn.resize(strPath.size());
for (size_t i = 0; i < strPath.length(); i++)
{
TCHAR c = TCHAR(strPath[i]);
if (c == _T(':') || c == _T('\\') || c == _T(' ') || c == _T('/'))
{
strReturn[i] = _T('_');
}
else
{
strReturn[i] = strPath[i];
}
}
return strReturn;
}




// 獲取指定系統路徑
std::wstring GetUserAppDataDir()
{
TCHAR buffer[MAX_PATH];
ZeroMemory(buffer, MAX_PATH*sizeof(TCHAR));


SHGetSpecialFolderPath(NULL,buffer, CSIDL_APPDATA, NULL); // 獲取指定系統路徑


std::string path = std::string(buffer) + _T("\\ws\\");
path += TransformInstallPath2FolderName(GetModulePathDir());
path += _T("\\");


int nReturn = SHCreateDirectoryEx(NULL, path.c_str(), NULL);


std::wstring strPath = Ansi2WChar(path.c_str());
return strPath;
}










//-----------------------------------------
// 功能部分
//-----------------------------------------
// 初始化系統記錄器,獲取日誌文件路徑
void initLog() {
char buffer[MAX_PATH * 2];
ZeroMemory(buffer, sizeof(char) * MAX_PATH * 2);

std::string str;
#ifdef MODEL_PATH // 表示日誌地址是進程所在的文件路徑
str = GetModulePathDir();
#else // 表示日誌地址是使用系統路徑
std::wstring strW = GetUserAppDataDir();
strW = Wchar2Ansi(strW.c_str(), strW.length());
#endif

sprintf(buffer, "%sprotocol.log", str.data());
fout.open(buffer, std::ios_base::trunc);
fout << "program start: " << safe_time() << std::endl;
}






// 輸出log
void logxxx(int l, char *file, int line, const char *fmt, ...) 
{
va_list param;
va_start(param, fmt);
// 鎖
while (true) 
{
if (InterlockedIncrement(&__log_lock) == 1)
{
break;
}
InterlockedDecrement(&__log_lock);
}


if (l <= syslogDevLevel) 
{
char buf[MAXLOGLINE * 4];
_vsnprintf(buf, MAXLOGLINE * 4, fmt, param);
fout << safe_time()<< file << line << ":" << LevelToName(l) << buf << std::endl;
fout.flush();
}


InterlockedDecrement(&__log_lock);
va_end(param);
}






// 關閉log系統
void closeLog()
{
fout << "program end:" << safe_time() << std::endl;
fout.close();
}




#else // linux


//--------------------------------------------------------------
// 日誌系統 
//--------------------------------------------------------------
// 初始化系統記錄器
void initLog(){
#ifdef L_LOG_OLD // "Linux syslog"(L_LOG_OLD)
openlog(NULL, LOG_PID, LOG_LOCAL0); // LOG_PID:包括每個消息的進程號; LOG_LOCAL0:保留到本地
#else // "linux syslog-ng"(L_LOG_NG)
openlog_nb(NULL, LOG_PID, LOG_LOCAL0); // LOG_PID:包括每個消息的進程號; LOG_LOCAL0:保留到本地
#endif
}




// 輸出log
void logxxx(int l, char *file, int line, const char *fmt, ...) {
if (l <= syslogDevLevel) {
char msg[MAXLOGLINE];
va_list ap;
va_start(ap, fmt);
int ret = vsnprintf(msg, MAXLOGLINE, fmt, ap);
va_end(ap);
if (ret < 0) { return; }
#ifdef L_LOG_OLD // "Linux syslog"(L_LOG_OLD)
syslog(l, "[%s:%d]: %s\n", file, line, msg);
#else // "linux syslog-ng"(L_LOG_NG)
syslog_nb(l, "[%s:%d]: %s\n", file, line, msg);
#endif
}
else
{
return;
}
}


// 關閉log系統
void closeLog()
{
#ifdef L_LOG_OLD // "Linux syslog"(L_LOG_OLD)
closelog();
#else // "linux syslog-ng"(L_LOG_NG)
closelog_nb();
#endif
}


#endif


--------------------------------------------------------------

配置信息請參照下一篇:syslog &syslog-ng詳解



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章