日志是服务端开发必不可少的模块,工作过几个公司,基本每个公司或者项目组都有自己的日志模块,有使用开源的,也有自己写的,这是自己写的一个日志模块,经过线上大并发的测试,性能良好,使用也较方便。该日志采用的是单例模式,支持自动按天分文件,按日志行数自动分文件,是多线程安全的单例模式,没有外部依赖,linux操作系统均可以使用。在日志安全级别高的情况,不能丢失日志的情况,可以使用同步模式, 在要求应用程序性能高对日志安全级别不高的情况可以使用异步模式,异步模式就是在程序异常奔溃或者是重启服务的情况可能有日志丢失。
/********************************************
function: thread safe blocking queue.
author: liuyi
date: 2014.11.13
version: 2.0
********************************************/
#ifndef BLOCK_QUEUE_H
#define BLOCK_QUEUE_H
#include
#include
#include
#include
using namespace std;
template
class block_queue
{
public:
block_queue(int max_size = 1000)
{
if(max_size <= 0)
{
exit(-1);
}
m_max_size = max_size;
m_array = new T[max_size];
m_size = 0;
m_front = -1;
m_back = -1;
m_mutex = new pthread_mutex_t;
m_cond = new pthread_cond_t;
pthread_mutex_init(m_mutex, NULL);
pthread_cond_init(m_cond, NULL);
}
void clear()
{
pthread_mutex_lock(m_mutex);
m_size = 0;
m_front = -1;
m_back = -1;
pthread_mutex_unlock(m_mutex);
}
~block_queue()
{
pthread_mutex_lock(m_mutex);
if(m_array != NULL)
delete m_array;
pthread_mutex_unlock(m_mutex);
pthread_mutex_destroy(m_mutex);
pthread_cond_destroy(m_cond);
delete m_mutex;
delete m_cond;
}
bool full()const
{
pthread_mutex_lock(m_mutex);
if(m_size >= m_max_size)
{
pthread_mutex_unlock(m_mutex);
return true;
}
pthread_mutex_unlock(m_mutex);
return false;
}
bool empty()const
{
pthread_mutex_lock(m_mutex);
if(0 == m_size)
{
pthread_mutex_unlock(m_mutex);
return true;
}
pthread_mutex_unlock(m_mutex);
return false;
}
bool front(T& value)const
{
pthread_mutex_lock(m_mutex);
if(0 == m_size)
{
pthread_mutex_unlock(m_mutex);
return false;
}
value = m_array[m_front];
pthread_mutex_unlock(m_mutex);
return true;
}
bool back(T& value)const
{
pthread_mutex_lock(m_mutex);
if(0 == m_size)
{
pthread_mutex_unlock(m_mutex);
return false;
}
value = m_array[m_back];
pthread_mutex_unlock(m_mutex);
return true;
}
int size()const
{
int tmp = 0;
pthread_mutex_lock(m_mutex);
tmp = m_size;
pthread_mutex_unlock(m_mutex);
return tmp;
}
int max_size()const
{
int tmp = 0;
pthread_mutex_lock(m_mutex);
tmp = m_max_size;
pthread_mutex_unlock(m_mutex);
return tmp;
}
bool push(const T& item)
{
pthread_mutex_lock(m_mutex);
if(m_size >= m_max_size)
{
pthread_cond_broadcast(m_cond);
pthread_mutex_unlock(m_mutex);
return false;
}
m_back = (m_back + 1) % m_max_size;
m_array[m_back] = item;
m_size++;
pthread_cond_broadcast(m_cond);
pthread_mutex_unlock(m_mutex);
return true;
}
bool pop(T& item)
{
pthread_mutex_lock(m_mutex);
while(m_size <= 0)
{
if(0 != pthread_cond_wait(m_cond, m_mutex))
{
pthread_mutex_unlock(m_mutex);
return false;
}
}
m_front = (m_front + 1) % m_max_size;
item = m_array[m_front];
m_size--;
pthread_mutex_unlock(m_mutex);
return true;
}
bool pop(T& item, int ms_timeout)
{
struct timespec t = {0,0};
struct timeval now = {0,0};
gettimeofday(&now, NULL);
pthread_mutex_lock(m_mutex);
if(m_size <= 0)
{
t.tv_sec = now.tv_sec + ms_timeout/1000;
t.tv_nsec = (ms_timeout % 1000)*1000;
if(0 != pthread_cond_timedwait(m_cond, m_mutex, &t))
{
pthread_mutex_unlock(m_mutex);
return false;
}
}
if(m_size <= 0)
{
pthread_mutex_unlock(m_mutex);
return false;
}
m_front = (m_front + 1) % m_max_size;
item = m_array[m_front];m_size--;
pthread_mutex_unlock(m_mutex);
return true;
}
private:
pthread_mutex_t *m_mutex;
pthread_cond_t *m_cond;
T *m_array;
int m_size;
int m_max_size;
int m_front;
int m_back;
};
#endif
/********************************************************
function:log class
version:1.0
date:2013.4.19
modify:2014.12.21
*********************************************************/
#ifndef LOG_H
#define LOG_H
#include
#include
#include
#include
#include
#include "block_queue.h"
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(m_log_queue->pop(single_log))
{
pthread_mutex_lock(m_mutex);
fputs(single_log.c_str(), m_fp);
pthread_mutex_unlock(m_mutex);
}
}
private:
pthread_mutex_t *m_mutex;
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;
block_queue *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
/********************************************************
function:log class
version:1.0
date:2013.4.19
modify:2014.12.21
*********************************************************/
#include
#include
#include
#include
#include "log.h"
#include
using namespace std;
Log::Log()
{
m_count = 0;
m_mutex = new pthread_mutex_t;
m_is_async = false;
pthread_mutex_init(m_mutex, NULL);
}
Log::~Log()
{
if(m_fp != NULL)
{
fclose(m_fp);
}
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(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())
{
m_log_queue->push(log_str);
}
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);
}
#include "log.h"
void *f(void* args)
{
for(int i = 0;i < 100; 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
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", i,'a',"log", 1.000);
LOG_INFO("%d", 123456789);
LOG_ERROR("%d", 123456789);
LOG_DEBUG("%d", 123456789);
LOG_WARN("%d", 123456789);
pthread_t id;
for(int i = 0; i < 1; i++)
{
pthread_create(&id, NULL, f, NULL);
pthread_join(id,NULL);
}
//for(;;)
{
sleep(15);
Log::get_instance()->flush();
}
return 0;
}