上一篇日志,写到了C程序下面logger日志类的实现,但是由于有缺陷,并且日志文件看起来比较麻烦(同时创建了几个日志文件)。后面在看到读写锁的机制后,觉得可以利用读写锁来重写这个日志类。
关键在于所有日志输出都加读锁,只有在日志大小达到了限制,才进行写锁加锁,修改相关信息。
(这里我一直有个误解,就是其实进行锁操作应该是很快的,关键在于加锁之后,临界区的代码执行职能处于单个线程执行,而其他等待的情况,导致多线程速度变慢,之前的版本一直坚持不做锁操作,直到某种条件满足,是源于这个误解)
关键代码如下:
//任何些日子操作都加读锁
pthread_rwlock_rdlock(&rwLock);
int currentSize = ftell(fpIn);
if(currentSize>=logger::fileCapability){
//解读锁,以便能进行写操作,修改fp指向
pthread_rwlock_unlock(&rwLock);
//加写锁
pthread_rwlock_wrlock(&rwLock);
if(ftell(fpIn)>=logger::fileCapability){
fclose(fpIn);
char tmp[10] = {0};
sprintf(tmp,"%d",index);
std::string newName = fileName+"_"+tmp;
rename(fileName.c_str(),newName.c_str());
fpIn = fopen(fileName.c_str(),"w");
index ++;
if(index>=fileNumLimit)
index = 1;
}
//解写锁
pthread_rwlock_unlock(&rwLock);
//加读锁
pthread_rwlock_rdlock(&rwLock);
printf("thread:%lu current err file fp is %x, size is %d\n", pthread_self(), fpIn, currentSize);
//output to stdout
fprintf(stdout, "%s:[TIME: %s] [MSG: %s]\n", preFixStr.c_str(), timestamp.c_str(), bufferMsg.c_str());
fprintf(fpIn, "%s:[TIME: %s] [MSG: %s]\n", preFixStr.c_str(), timestamp.c_str(), bufferMsg.c_str());
//解读锁
pthread_rwlock_unlock(&rwLock);
}else{
printf("thread:%lu current err file fp is %x, size is %d\n", pthread_self(), fpIn, currentSize);
//output to stdout
fprintf(stdout, "%s:[TIME: %s] [MSG: %s]\n", preFixStr.c_str(), timestamp.c_str(), bufferMsg.c_str());
fprintf(fpIn, "%s:[TIME: %s] [MSG: %s]\n", preFixStr.c_str(), timestamp.c_str(), bufferMsg.c_str());
pthread_rwlock_unlock(&rwLock);
}
再附上完整代码:
logger_v2.h
#ifndef LOGGER_H__
#define LOGGER_H__
#include <string>
#include <stdlib.h>
#include <vector>
#include <pthread.h>
struct FileInfo{
std::string fileBaseName;
FILE* fp;
FileInfo(){
fileBaseName = "";
fp = NULL;
}
};
class logger{
public:
//错误日志文件名配置名称
static const std::string LOGGER_ERROR_FILE_NAME_CONF_NAME;
//信息日志文件名配置名称
static const std::string LOGGER_INFO_FILE_NAME_CONF_NAME;
//调试日志文件名配置名称
static const std::string LOGGER_DEBUG_FILE_NAME_CONF_NAME;
//单个文件大小配置名称
static const std::string LOGGER_SINGLE_FILE_CAPA_CONF_NAME;
//保存的文件个数配置名称
static const std::string LOGGER_FILE_NUM_CONF_NAME;
//输出日志的级别设定配置名称
static const std::string LOGGER_LEVEL_CONF_NAME;
private:
//错误日志文件名和文件描述符
static std::string errFileName;
static FILE* errFp;
//信息日志文件名和文件描述符
static std::string infoFileName;
static FILE* infoFp;
//调试日志文件名和文件描述符
static std::string debugFileName;
static FILE* debugFp;
static unsigned char currentIndexOfErr;
static unsigned char currentIndexOfInfo;
static unsigned char currentIndexOfDebug;
//单个日志文件大小
static int fileCapability;
//log 文件个数
static int fileNumLimit;
//输出的日志级别( error, info, debug )
static std::string loggerLevel;
static int prefixSize;
//读写锁
static pthread_rwlock_t rwLock;
public:
static bool init(const std::string configFileName);
public:
void info(const char* format, ...);
void error(const char* format, ...);
void debug(const char* format, ...);
private:
void writeLog(std::string &preFixStr, std::string& fileName, FILE* &fpIn, unsigned char& index, std::string &bufferMsg);
};
#endif
logger_v2.cpp
#include "logger_v2.h"
#include <time.h>
#include <sys/time.h>
#include "confReader.h"
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#define MAX_MSG_LEN 1024
#define TIMESTAMP_LEN 100
const std::string logger::LOGGER_ERROR_FILE_NAME_CONF_NAME = "error_file_name";
const std::string logger::LOGGER_INFO_FILE_NAME_CONF_NAME = "info_file_name";
const std::string logger::LOGGER_DEBUG_FILE_NAME_CONF_NAME = "debug_file_name";
const std::string logger::LOGGER_SINGLE_FILE_CAPA_CONF_NAME = "single_file_capability";
const std::string logger::LOGGER_FILE_NUM_CONF_NAME = "file_num";
const std::string logger::LOGGER_LEVEL_CONF_NAME = "log_level";
std::string logger::errFileName = "";
FILE* logger::errFp = NULL;
std::string logger::infoFileName = "";
FILE* logger::infoFp = NULL;
std::string logger::debugFileName = "";
FILE* logger::debugFp = NULL;
unsigned char logger::currentIndexOfErr = 1;
unsigned char logger::currentIndexOfInfo = 1;
unsigned char logger::currentIndexOfDebug = 1;
int logger::fileCapability = 512;
int logger::fileNumLimit = 1;
std::string logger::loggerLevel = "error";
int logger::prefixSize = 0;
pthread_rwlock_t logger::rwLock;
bool logger::init(const std::string configFileName){
ConfReader reader;
reader.readConfFile(configFileName);
errFileName = reader.getParameterValue(logger::LOGGER_ERROR_FILE_NAME_CONF_NAME);
infoFileName = reader.getParameterValue(logger::LOGGER_INFO_FILE_NAME_CONF_NAME);
debugFileName = reader.getParameterValue(logger::LOGGER_DEBUG_FILE_NAME_CONF_NAME);
std::string singleFileCapabilityStr = reader.getParameterValue(logger::LOGGER_SINGLE_FILE_CAPA_CONF_NAME);
fileCapability = atoi(singleFileCapabilityStr.c_str());
std::string fileNumLimitStr = reader.getParameterValue(logger::LOGGER_FILE_NUM_CONF_NAME);
fileNumLimit = atoi(fileNumLimitStr.c_str());
errFp = NULL;
currentIndexOfErr = 1;
infoFp = NULL;
currentIndexOfInfo = 1;
debugFp = NULL;
currentIndexOfDebug = 1;
//set log level
loggerLevel = reader.getParameterValue(logger::LOGGER_LEVEL_CONF_NAME);
prefixSize = strlen("ERROR:[TIME: ] [MSG: ]\n");
pthread_rwlock_init(&rwLock,NULL);
return true;
}
static std::string getCurrentFullTime(){
char timestamp[TIMESTAMP_LEN];
struct timeval time;
gettimeofday(&time, NULL);
struct tm* tm_t = localtime(&time.tv_sec);
strftime(timestamp , sizeof(timestamp),"%F.%H:%M:%S",tm_t);
return timestamp;
}
void logger::writeLog(std::string& preFixStr, std::string& fileName, FILE* &fpIn, unsigned char& index, std::string& bufferMsg){
std::string timestamp = getCurrentFullTime();
pthread_rwlock_rdlock(&rwLock);
//检查是否文件已经打开
if(fpIn==NULL){
pthread_rwlock_unlock(&rwLock);
pthread_rwlock_wrlock(&rwLock);
if(fpIn==NULL){
fpIn = fopen(fileName.c_str(),"w");
}
pthread_rwlock_unlock(&rwLock);
pthread_rwlock_rdlock(&rwLock);
}
int currentSize = ftell(fpIn);
if(currentSize>=logger::fileCapability){
//解读锁,以便能进行写操作,修改fp指向
pthread_rwlock_unlock(&rwLock);
//加写锁
pthread_rwlock_wrlock(&rwLock);
if(ftell(fpIn)>=logger::fileCapability){
fclose(fpIn);
char tmp[10] = {0};
sprintf(tmp,"%d",index);
std::string newName = fileName+"_"+tmp;
rename(fileName.c_str(),newName.c_str());
fpIn = fopen(fileName.c_str(),"w");
index ++;
if(index>=fileNumLimit)
index = 1;
}
//解写锁
pthread_rwlock_unlock(&rwLock);
//加读锁
pthread_rwlock_rdlock(&rwLock);
printf("thread:%lu current err file fp is %x, size is %d\n", pthread_self(), fpIn, currentSize);
//output to stdout
fprintf(stdout, "%s:[TIME: %s] [MSG: %s]\n", preFixStr.c_str(), timestamp.c_str(), bufferMsg.c_str());
fprintf(fpIn, "%s:[TIME: %s] [MSG: %s]\n", preFixStr.c_str(), timestamp.c_str(), bufferMsg.c_str());
//解读锁
pthread_rwlock_unlock(&rwLock);
}else{
printf("thread:%lu current err file fp is %x, size is %d\n", pthread_self(), fpIn, currentSize);
//output to stdout
fprintf(stdout, "%s:[TIME: %s] [MSG: %s]\n", preFixStr.c_str(), timestamp.c_str(), bufferMsg.c_str());
fprintf(fpIn, "%s:[TIME: %s] [MSG: %s]\n", preFixStr.c_str(), timestamp.c_str(), bufferMsg.c_str());
pthread_rwlock_unlock(&rwLock);
}
}
void logger::error(const char* format, ...){
char buf[ MAX_MSG_LEN + 1];
memset(buf, 0x00, MAX_MSG_LEN + 1);
if ( logger::loggerLevel != "error" && logger::loggerLevel != "info" && logger::loggerLevel != "debug"){
return;
}
va_list ap;
va_start(ap, format);
vsnprintf( buf, MAX_MSG_LEN, format, ap);
va_end(ap);
std::string type = "ERROR";
std::string bufferMsg = buf;
writeLog(type, errFileName, errFp, currentIndexOfErr, bufferMsg);
}
void logger::info(const char* format, ...){
char buf[ MAX_MSG_LEN + 1];
memset(buf, 0x00, MAX_MSG_LEN + 1);
if ( logger::loggerLevel != "info" && logger::loggerLevel != "debug"){
return;
}
va_list ap;
va_start(ap, format);
vsnprintf( buf, MAX_MSG_LEN, format, ap);
va_end(ap);
std::string type = "INFO";
std::string bufferMsg = buf;
writeLog(type, infoFileName, infoFp, currentIndexOfInfo, bufferMsg);
}
void logger::debug(const char* format, ...){
char buf[ MAX_MSG_LEN + 1];
memset(buf, 0x00, MAX_MSG_LEN + 1);
if ( logger::loggerLevel != "debug"){
return;
}
va_list ap;
va_start(ap, format);
vsnprintf( buf, MAX_MSG_LEN, format, ap);
va_end(ap);
std::string type = "DEBUG";
std::string bufferMsg = buf;
writeLog(type, debugFileName, debugFp, currentIndexOfDebug, bufferMsg);
}
#ifdef LOGGER_TEST
void* printLog(void* arg){
int i = 200000000,j=0;
logger myLogger;
while(i--){
myLogger.info("thread id:%lu testLog:%s:%d",pthread_self(), "test1",j++);
}
return NULL;
}
int main(int argc, char* argv[]){
if(argc!=2){
printf("please input pragram, configFileName\n");
return -1;
}
logger::init(argv[1]);
pthread_t id[3];
for(int i=0;i<3;i++){
pthread_create(&(id[i]), NULL, printLog, NULL);
}
for(int i=0;i<3;i++){
pthread_join(id[i], NULL);
}
return 0;
}
#endif