自己寫的C++日誌類log
阿新 • • 發佈:2019-02-16
主要功能是在伺服器執行的時候可以列印日誌到日誌檔案中,主要運用到的知識點有執行緒, 執行緒鎖,條件變數,STL的deque。大致思路是這樣的:
這個類提供一個介面,可以直接呼叫他,並且可以列印日誌到日誌檔案中。但是不能影響到伺服器的正常執行,也就是說不能進行大量的檔案操作;
大致 實現流程:
物件初始化時候建立一個執行緒在後臺讀取佇列,並將讀到的資料寫入到日誌檔案中,並在佇列中刪除這一資料;
介面處,每次寫日誌時,其實是將日誌存到佇列中去。交由後臺執行緒寫入到檔案中。
整個程式難點:對於臨界區的控制,即執行緒鎖,條件變數的使用。
對deque的理解與使用。
在C++類內部實現類例項化;
下邊上程式碼
log.h #ifndef LOG_H
#define LOG_H
#include <stdio.h>
#include <iostream>
#include <string>
#include <stdarg.h>
#include <pthread.h>
#include <deque>
using namespace std;
class Log
{
public:
static Log* get_instance()
{
static Log instance;
return &instance ;
}
static void *flush_log_thread(void* args)
{
Log::get_instance()->async_write_log();
}
bool init(const char* file_name, int log_buf_size = 8192, int split_lines = 5000000, int max_queue_size = 0);
void write_log(int level, const char* format, ...);
void flush(void);
private :
Log();
virtual ~Log();
void *async_write_log()
{
string single_log;
while(1){
printf("%d\n",!m_log_queue.size());
if(!m_log_queue.size() )
{
pthread_mutex_lock(m_mutex);
{
pthread_cond_wait(m_cond, m_mutex);
single_log=m_log_queue.front();
fputs(single_log.c_str(), m_fp);
m_log_queue.pop_front();
pthread_mutex_unlock(m_mutex);
}
}else{
pthread_mutex_lock(m_mutex);
single_log=m_log_queue.front();
fputs(single_log.c_str(), m_fp);
m_log_queue.pop_front();
pthread_mutex_unlock(m_mutex);
}
}
printf("end\n");
}
private:
pthread_mutex_t *m_mutex;
pthread_cond_t *m_cond;
char dir_name[128];
char log_name[128];
int m_split_lines;
int m_log_buf_size;
long long m_count;
int m_today;
FILE *m_fp;
char *m_buf;
deque<string> m_log_queue;
bool m_is_async;
};
#define LOG_DEBUG(format, ...) Log::get_instance()->write_log(0, format, __VA_ARGS__)
#define LOG_INFO(format, ...) Log::get_instance()->write_log(1, format, __VA_ARGS__)
#define LOG_WARN(format, ...) Log::get_instance()->write_log(2, format, __VA_ARGS__)
#define LOG_ERROR(format, ...) Log::get_instance()->write_log(3, format, __VA_ARGS__)
#endif
log.cpp
*********************************************************/ #include <string.h> #include <time.h> #include <sys/time.h> #include <stdarg.h> #include "log.h" #include <pthread.h> using namespace std; Log::Log() { m_count = 0; m_mutex = new pthread_mutex_t; m_cond = new pthread_cond_t; m_is_async = false; pthread_mutex_init(m_mutex, NULL); } Log::~Log() { if(m_fp != NULL) { fclose(m_fp); } pthread_cond_destroy(m_cond); if(m_cond != NULL) { delete m_cond; } pthread_mutex_destroy(m_mutex); if(m_mutex != NULL) { delete m_mutex; } } bool Log::init(const char* file_name, int log_buf_size, int split_lines, int max_queue_size) { if(max_queue_size >= 1) { m_is_async = true; // m_log_queue = new block_queue<string>(max_queue_size); deque<string>(max_queue_size); pthread_t tid; pthread_create(&tid, NULL, flush_log_thread, NULL); } m_log_buf_size = log_buf_size; m_buf = new char[m_log_buf_size]; memset(m_buf, '\0', sizeof(m_buf)); m_split_lines = split_lines; time_t t = time(NULL); struct tm* sys_tm = localtime(&t); struct tm my_tm = *sys_tm; const char *p = strrchr(file_name, '/'); char log_full_name[256] = {0}; if(p == NULL) { snprintf(log_full_name, 255, "%d_%02d_%02d_%s",my_tm.tm_year+1900, my_tm.tm_mon+1, my_tm.tm_mday, file_name); } else { strcpy(log_name, p+1); strncpy(dir_name, file_name, p - file_name + 1); snprintf(log_full_name, 255, "%s%d_%02d_%02d_%s",dir_name, my_tm.tm_year+1900, my_tm.tm_mon+1, my_tm.tm_mday, log_name ); } m_today = my_tm.tm_mday; m_fp = fopen(log_full_name, "a"); if(m_fp == NULL) { return false; } return true; } void Log::write_log(int level, const char* format, ...) { struct timeval now = {0,0}; gettimeofday(&now, NULL); time_t t = now.tv_sec; struct tm* sys_tm = localtime(&t); struct tm my_tm = *sys_tm; char s[16] = {0}; switch(level) { case 0 : strcpy(s, "[debug]:"); break; case 1 : strcpy(s, "[info]:"); break; case 2 : strcpy(s, "[warn]:"); break; case 3 : strcpy(s, "[erro]:"); break; default: strcpy(s, "[info]:"); break; } pthread_mutex_lock(m_mutex); m_count++; if(m_today != my_tm.tm_mday || m_count % m_split_lines == 0) //everyday log { char new_log[256] = {0}; fflush(m_fp); fclose(m_fp); char tail[16] = {0}; snprintf(tail, 16, "%d_%02d_%02d_", my_tm.tm_year+1900, my_tm.tm_mon+1, my_tm.tm_mday); if(m_today != my_tm.tm_mday) { snprintf(new_log, 255, "%s%s%s", dir_name, tail, log_name); m_today = my_tm.tm_mday; m_count = 0; } else { snprintf(new_log, 255, "%s%s%s.%d", dir_name, tail, log_name, m_count/m_split_lines); } m_fp = fopen(new_log, "a"); } pthread_mutex_unlock(m_mutex); va_list valst; va_start(valst, format); string log_str; pthread_mutex_lock(m_mutex); int n = snprintf(m_buf, 48, "%d-%02d-%02d %02d:%02d:%02d.%06d %s ", my_tm.tm_year+1900, my_tm.tm_mon+1, my_tm.tm_mday, my_tm.tm_hour, my_tm.tm_min, my_tm.tm_sec, now.tv_usec, s); int m = vsnprintf(m_buf + n, m_log_buf_size-1, format, valst); m_buf[n + m - 1] = '\n'; log_str = m_buf; pthread_mutex_unlock(m_mutex); if(m_is_async)// && !m_log_queue->full()) { pthread_mutex_lock(m_mutex); m_log_queue.push_back(log_str); pthread_cond_broadcast(m_cond); pthread_mutex_unlock(m_mutex); } else { pthread_mutex_lock(m_mutex); fputs(log_str.c_str(), m_fp); pthread_mutex_unlock(m_mutex); } va_end(valst); } void Log::flush(void) { pthread_mutex_lock(m_mutex); fflush(m_fp); pthread_mutex_unlock(m_mutex); } 測試檔案 main.cpp
#include "log.h" void *f(void* args) { for(int i = 0;i < 10000; i++) { Log::get_instance()->write_log(1, "d=%d,c=%c,s=%s,f=%f", i,'a',"log", 1.000); Log::get_instance()->write_log(2, "d=%d,c=%c,s=%s,f=%f", i,'a',"log", 1.000); Log::get_instance()->write_log(3, "d=%d,c=%c,s=%s,f=%f", i,'a',"log", 1.000); LOG_INFO("%d", 123456789); LOG_ERROR("%d", 123456789); LOG_DEBUG("%d", 123456789); LOG_WARN("%d", 123456789); } } int main() { Log::get_instance()->init("./mylog.log", 100, 2000000, 10); //Log::get_instance()->init("./mylog.log", 100, 2000000, 0);//synchronization model // printf("11111111111111\n"); // sleep(1); int i = 0; Log::get_instance()->write_log(1, "d=%d,c=%c,s=%s,f=%f", i,'a',"log", 1.000); Log::get_instance()->write_log(2, "d=%d,c=%c,s=%s,f=%f", i,'a',"log", 1.000); Log::get_instance()->write_log(3, "d=%d,c=%c,s=%s,f=%f"
這個類提供一個介面,可以直接呼叫他,並且可以列印日誌到日誌檔案中。但是不能影響到伺服器的正常執行,也就是說不能進行大量的檔案操作;
大致 實現流程:
物件初始化時候建立一個執行緒在後臺讀取佇列,並將讀到的資料寫入到日誌檔案中,並在佇列中刪除這一資料;
介面處,每次寫日誌時,其實是將日誌存到佇列中去。交由後臺執行緒寫入到檔案中。
整個程式難點:對於臨界區的控制,即執行緒鎖,條件變數的使用。
對deque的理解與使用。
在C++類內部實現類例項化;
下邊上程式碼
log.h #ifndef
log.cpp
*********************************************************/ #include <string.h> #include <time.h> #include <sys/time.h> #include <stdarg.h> #include "log.h" #include <pthread.h> using namespace std; Log::Log() { m_count = 0; m_mutex = new pthread_mutex_t; m_cond = new pthread_cond_t; m_is_async = false; pthread_mutex_init(m_mutex, NULL); } Log::~Log() { if(m_fp != NULL) { fclose(m_fp); } pthread_cond_destroy(m_cond); if(m_cond != NULL) { delete m_cond; } pthread_mutex_destroy(m_mutex); if(m_mutex != NULL) { delete m_mutex; } } bool Log::init(const char* file_name, int log_buf_size, int split_lines, int max_queue_size) { if(max_queue_size >= 1) { m_is_async = true; // m_log_queue = new block_queue<string>(max_queue_size); deque<string>(max_queue_size); pthread_t tid; pthread_create(&tid, NULL, flush_log_thread, NULL); } m_log_buf_size = log_buf_size; m_buf = new char[m_log_buf_size]; memset(m_buf, '\0', sizeof(m_buf)); m_split_lines = split_lines; time_t t = time(NULL); struct tm* sys_tm = localtime(&t); struct tm my_tm = *sys_tm; const char *p = strrchr(file_name, '/'); char log_full_name[256] = {0}; if(p == NULL) { snprintf(log_full_name, 255, "%d_%02d_%02d_%s",my_tm.tm_year+1900, my_tm.tm_mon+1, my_tm.tm_mday, file_name); } else { strcpy(log_name, p+1); strncpy(dir_name, file_name, p - file_name + 1); snprintf(log_full_name, 255, "%s%d_%02d_%02d_%s",dir_name, my_tm.tm_year+1900, my_tm.tm_mon+1, my_tm.tm_mday, log_name ); } m_today = my_tm.tm_mday; m_fp = fopen(log_full_name, "a"); if(m_fp == NULL) { return false; } return true; } void Log::write_log(int level, const char* format, ...) { struct timeval now = {0,0}; gettimeofday(&now, NULL); time_t t = now.tv_sec; struct tm* sys_tm = localtime(&t); struct tm my_tm = *sys_tm; char s[16] = {0}; switch(level) { case 0 : strcpy(s, "[debug]:"); break; case 1 : strcpy(s, "[info]:"); break; case 2 : strcpy(s, "[warn]:"); break; case 3 : strcpy(s, "[erro]:"); break; default: strcpy(s, "[info]:"); break; } pthread_mutex_lock(m_mutex); m_count++; if(m_today != my_tm.tm_mday || m_count % m_split_lines == 0) //everyday log { char new_log[256] = {0}; fflush(m_fp); fclose(m_fp); char tail[16] = {0}; snprintf(tail, 16, "%d_%02d_%02d_", my_tm.tm_year+1900, my_tm.tm_mon+1, my_tm.tm_mday); if(m_today != my_tm.tm_mday) { snprintf(new_log, 255, "%s%s%s", dir_name, tail, log_name); m_today = my_tm.tm_mday; m_count = 0; } else { snprintf(new_log, 255, "%s%s%s.%d", dir_name, tail, log_name, m_count/m_split_lines); } m_fp = fopen(new_log, "a"); } pthread_mutex_unlock(m_mutex); va_list valst; va_start(valst, format); string log_str; pthread_mutex_lock(m_mutex); int n = snprintf(m_buf, 48, "%d-%02d-%02d %02d:%02d:%02d.%06d %s ", my_tm.tm_year+1900, my_tm.tm_mon+1, my_tm.tm_mday, my_tm.tm_hour, my_tm.tm_min, my_tm.tm_sec, now.tv_usec, s); int m = vsnprintf(m_buf + n, m_log_buf_size-1, format, valst); m_buf[n + m - 1] = '\n'; log_str = m_buf; pthread_mutex_unlock(m_mutex); if(m_is_async)// && !m_log_queue->full()) { pthread_mutex_lock(m_mutex); m_log_queue.push_back(log_str); pthread_cond_broadcast(m_cond); pthread_mutex_unlock(m_mutex); } else { pthread_mutex_lock(m_mutex); fputs(log_str.c_str(), m_fp); pthread_mutex_unlock(m_mutex); } va_end(valst); } void Log::flush(void) { pthread_mutex_lock(m_mutex); fflush(m_fp); pthread_mutex_unlock(m_mutex); } 測試檔案 main.cpp
#include "log.h" void *f(void* args) { for(int i = 0;i < 10000; i++) { Log::get_instance()->write_log(1, "d=%d,c=%c,s=%s,f=%f", i,'a',"log", 1.000); Log::get_instance()->write_log(2, "d=%d,c=%c,s=%s,f=%f", i,'a',"log", 1.000); Log::get_instance()->write_log(3, "d=%d,c=%c,s=%s,f=%f", i,'a',"log", 1.000); LOG_INFO("%d", 123456789); LOG_ERROR("%d", 123456789); LOG_DEBUG("%d", 123456789); LOG_WARN("%d", 123456789); } } int main() { Log::get_instance()->init("./mylog.log", 100, 2000000, 10); //Log::get_instance()->init("./mylog.log", 100, 2000000, 0);//synchronization model // printf("11111111111111\n"); // sleep(1); int i = 0; Log::get_instance()->write_log(1, "d=%d,c=%c,s=%s,f=%f", i,'a',"log", 1.000); Log::get_instance()->write_log(2, "d=%d,c=%c,s=%s,f=%f", i,'a',"log", 1.000); Log::get_instance()->write_log(3, "d=%d,c=%c,s=%s,f=%f"