一、借用标准库模板构造自己的模板
通常,模板设计是遵循当对象的类型不影响类中函数的行为时就使用模板。这也就是为何标准库提供大部分的模板都是与容器、迭代器、适配器、通用算法等有关,因为这些主要是除了对象集合行为,如读写、增删、遍历、排序、比较等大多与对象类型无关的逻辑行为。
在实际项目中,本人建议大家在使用模板时,采用的最好模板设计方式是走适配器方法,就像标准库基于vector、list、deque这些基准容器,通过适配封装去实现stack、queue、priority queue这些容器适配器一样。我们根据自己项目需要,采用标准库提供的容器及算法,创建自己的类模板应用到项目中。
二、基于标准库模板创建自定义类模板案例
2.1 自定义容器适配器
在本文中,将设计一个日志消息队列的容器适配器,基于双端队列(deque)容器和互斥锁,通过适配,创建一个线程安全的消息队列类模板。该消息队列类通过二次封装,实现数据末端添加数据,头部取出数据,队列大小有限制的跨线程安全消息缓冲区。
首先创建一个queuedata.h和queuedata.cpp源文件,在queuedata.h文件实现消息队列类模板进行声明,在queuedata.cpp源文件进行定义,在queuedata.h文件末通过#include "queuedata.cpp"引用定义内容。本文中,QueueData类模板虽然说类似容器适配器,但是没有提供配套的迭代器iterator,大家感兴趣的可以自行实现一下(就是将deque容器的迭代器iterator做一下中转即可)。
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#ifndef _QUEUE_DATA_H_
#define _QUEUE_DATA_H_
/***********************************************************************
*Copyright 2023-02-06, pyfree
*
*File Name : queuedata.h
*File Mark :
*Summary :
*数据队列类模板,线程安全,对std::deque进行适配,通过PYMutex锁约束线程安全,提供数据增删等功能接口
*
*Current Version : 1.00
*Author : pyfree
*FinishDate :
*
*Replace Version :
*Author :
*FinishDate :
************************************************************************/
#include <deque>
#include <queue>
#include <stdio.h>
#include <string.h>
#include <string>
#include "Mutex.h"
template <class T>
class QueueData
{
public:
QueueData(std::string desc = "thread_queue");
~QueueData();
//
/**
* 获取队列大小
* @return {int } 队列大小
*/
int size();
/**
* 判定队列是否为空
* @return {bool } 是否为空队列
*/
bool isEmpty();
/**
* 获取队列头元素
* @param it {T&} 头元素
* @return {bool } 是否成功
*/
bool getFirst(T &it);
/**
* 删除元素
* @return {bool } 是否成功
*/
bool removeFirst();
/**
* 获取队列头元素,并从队列终删除
* @param it {T&} 头元素
* @return {bool } 是否成功
*/
bool pop(T &it);
/**
* 从队列头开始逐步获取多个元素,并剔除
* @param its {queue<T>&} 获取到的元素集
* @param sizel {int} 一次获取多少个
* @return {bool } 至少获取一个元素以上则成功
*/
bool getList(std::queue<T> &its,unsigned int sizel=5);
/**
* 从队列尾部添加元素
* @param it {T} 被添加元素
* @return {void } 无返回
*/
void add(T it);
/**
* 从队列头部添加元素
* @param it {T} 被添加元素
* @return {void } 无返回
*/
void add_front(T it);
/**
* 清空元素
* @return {void }
*/
void clear();
private:
void init();
QueueData& operator=(const QueueData&) {return this;};
protected:
std::string queue_desc;
private:
/点集转发
//协议解析结果缓存
std::deque<T> datacache_queue; //队列容器
PYMutex m_Mutex; //线程锁,或者如果更彻底采用acl库,采用acl::thread_mutex替代
//
static unsigned int QSize; //队列大小约束,超出是会从队列头剔除旧数据腾出空位在对末添加数据
//
int queue_overS; //队列溢出次数计数
};
#include "queuedata.cpp"
#endif //_QUEUE_DATA_H_
2.2 非类型模板参数的结构体模板
传入消息队列类的信息是一个非类型模板参数的结构体模板,可以根据实际项目需要选择消息缓存区最大字节数。创建LogDef.h源文件,实现消息结构体模板,并定义日志等级枚举值。该结构体在定义对象时可以采用默认的方法MyLogStruct<> obj,也可以指定大小MyLogStruct<DATA_SIZE_MAX_LEVLE2> obj,或指定任意值作为缓存区域大小,MyLogStruct<100> obj。
#ifndef _LOG_DEF_H_
#define _LOG_DEF_H_
/***********************************************************************
*Copyright 2023-02-06, pyfree
*
*File Name : LogDef.h
*File Mark :
*Summary : Log线程类使用到的日志等级枚举类型,日志信息格式-用于日志缓冲区存放日志的结构体
*
*Current Version : 1.00
*Author : pyfree
*FinishDate :
*
*Replace Version :
*Author :
*FinishDate :
************************************************************************/
#ifdef __linux__
#include <string.h>
#endif
typedef enum eLogType
{
eLog_DEBUG = 1,
eLog_TRACE = 2,
eLog_NOTICE = 3,
eLog_WARNING = 4,
eLog_ERROR = 5
}LogLevel;
#define DATA_SIZE_MAX_LEVLE1 1024 //日志缓存信息空间大小(1024字节)
#define DATA_SIZE_MAX_LEVLE2 512 //日志缓存信息空间大小(512字节)
#define DATA_SIZE_MAX_LEVLE3 256 //日志缓存信息空间大小(256字节)
//日志缓存信息结构体,采用结构体默认赋值
template<int SIZE = DATA_SIZE_MAX_LEVLE3>
struct MyLogStruct
{
MyLogStruct():type(0)
{
memset(szBuffer, 0, SIZE);
};
int type;
char szBuffer[SIZE];
};
#endif //_LOG_DEF_H_
2.3 日志记录类设计
下来就是日志记录功能部分,创一个日志类,该类有一个QueueData<MyLogStruct<> > logs_cache对象用来缓存日志。该日志类还是一个单体模式设计,并继承一个线程类。线程循环体内,日志对象会不断去读取缓存区内的日志信息,如果读取到信息,就写入到日志文件内。另外,为了方式日志记录过大造成磁盘存储空间不足,将有一个DiskSpaceMgr类对象来定期巡检磁盘剩余空间,在存储空间不足时,去删除旧的日志信息。
创建Log.h和Log.cpp源文件,Log.h源文件如下:
#ifndef _CLOGGER_H_
#define _CLOGGER_H_
/***********************************************************************
*Copyright 2023-02-06, pyfree
*
*File Name : Log.h
*File Mark :
*Summary : Log写入独立线程类,提供添加日志信息进入缓存接口,自身不断从缓存读取日志信息写入文件
* 会启动一个磁盘空间管理线程对象DiskSpaceMgr,在磁盘空间不足时,删除旧日志
*
*Current Version : 1.00
*Author : pyfree
*FinishDate :
*
*Replace Version :
*Author :
*FinishDate :
************************************************************************/
#include "myThread.h"
#include "LogDef.h"
#include "queuedata.h"
class DiskSpaceMgr;
class CLogger : public MyThread
{
public:
void Log(const eLogType type, const char* lpszFormat, ...);
static CLogger* createInstance( void );
private:
CLogger();
~CLogger();
int Run();
void WriteLog( const int iMsgType, const char * strMsg);
void WriteLogToFile( std::string strPath,std::string strTime,const char * strMsg);
std::string createFileName(std::string strTime_);
std::string strTime();
std::string createLogTimeAndTypeDesc(const unsigned int iMsgType, std::string strTime_);
private:
static CLogger* m_pLogInstance;
bool running;
FILE * fpLog;
std::string cur_log_path;
//for cache
QueueData<MyLogStruct<> > logs_cache;
//
DiskSpaceMgr *disk_space_mgr;
};
#endif //_CLOGGER_H_
2.4 磁盘剩余空间管理类
对象DiskSpaceMgr *disk_space_mgr是由Logger类创建的,该磁盘空间管理类是继承了类线程类的,在线程循环体内,不间断地巡检指定磁盘的剩余空间是否小于限定空间大小,小于时,将按配置要求去删除旧的日志信息。
创建spaceMgr.h和spaceMgr.cpp源文件,spaceMgr.h内容如下:
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#ifndef _SPACE_MGR_H_
#define _SPACE_MGR_H_
/***********************************************************************
*Copyright 2023-02-06, pyfree
*
*File Name : spaceMgr.h
*File Mark :
*Summary : 磁盘空间巡检线程类,根据预留空间要求删除app日志文件
*
*Current Version : 1.00
*Author : pyfree
*FinishDate :
*
*Replace Version :
*Author :
*FinishDate :
************************************************************************/
#ifdef linux
#include <string>
#endif
#include <vector>
#include "myThread.h"
class DiskSpaceMgr : public MyThread
{
public:
/**
* 构造函数,如传入参数D 2000 log log,则指明当D盘空闲空间不足2000M时,删除当前目录/log下的log(后缀)文件
* @param disk_ {char} 指定盘符
* @param fsl_ {int} 预留空间大小,单位MB
* @param dir_ {string} 指定目录
* @param ext_ {string} 扩展名
* @return { }
*/
DiskSpaceMgr(char disk_, int fsl_,std::string dir_="log",std::string ext_="log");
virtual ~DiskSpaceMgr(void);
int Run();
void add(std::string dir_,std::string ext_);
private:
struct DelInfo
{
DelInfo(std::string dir_,std::string ext_)
: dir(dir_), ext(ext_)
{};
std::string dir;
std::string ext;
};
private:
//用于日志删除
std::vector<DelInfo> dirs; //存储目录
char DiskStr; //日志存储磁盘
int freeSizeLimit; //磁盘预留空间要求
};
#endif
2.5 其他辅助类及函数设计
日志类及磁盘管理类都继承了线程类MyThread,是通过myThread.h和myThread.cpp声明定义的,myThread.h内容如下:
#ifndef _MYTHREAD_H
#define _MYTHREAD_H
/***********************************************************************
*Copyright 2023-02-06, pyfree
*
*File Name : myThread.h
*File Mark :
*Summary :
*支持windows和linux的线程类,提供基本的线程功能(循环、启动、退出)
*
*Current Version : 1.00
*Author : pyfree
*FinishDate :
*
*Replace Version :
*Author :
*FinishDate :
************************************************************************/
#ifdef WIN32
#include <process.h>
#include <iostream>
typedef void* HANDLE_THREAD;
#else
#include <pthread.h>
#include <unistd.h>
typedef pthread_t HANDLE_THREAD;
#endif
class MyThread
{
public://func
MyThread(); // constructed function
~MyThread();
/**
* the entity for thread running
* @param null {void } 无参数
* @return {int} 运行返回结果
*/
virtual int Run()=0;
/**
* start thread
* @param null {void } 无参数
* @return {bool} 是否启动成功
*/
bool start();
/**
* get thread ID
* @param null {void } 无参数
* @return {HANDLE_THREAD} 线程句柄
*/
HANDLE_THREAD getThreadID();
#ifdef __linux__
//get thread status
int getState();
//wait for thread end
void join();
//wait for thread end in limit time
void join(unsigned long millisTime);
#endif
public://val
#ifdef __linux__
//threadStatus-new create
static const int THREAD_STATUS_NEW = 0;
//threadStatus-running
static const int THREAD_STATUS_RUNNING = 1;
//threadStatus-end
static const int THREAD_STATUS_EXIT = -1;
#endif
private://func
#ifdef WIN32
static void agent(void *p);
#else
//get manner pointer of execution
static void* run0(void* pVoid);
//manner of execution inside
void* run1();
#endif
private://val
HANDLE_THREAD hThread;
#ifdef __linux__
//thread status
int threadStatus;
#endif
};
#endif /* _MYTHREAD_H */
消息队列类在队列内容进行变更时,需要通过互斥锁确保跨线程操作安全,互斥锁是由Mutex.h和Mutex.cpp源文件声明定义的,Mutex.h内容如下:
#ifndef PYMUTEX_H
#define PYMUTEX_H
#ifdef WIN32
//#include <windows.h>
#else
#include <pthread.h>
#endif
typedef void *HANDLE;
class IMutex
{
public:
virtual ~IMutex() {}
virtual void Lock() const = 0;
virtual bool TryLock() const = 0;
virtual void Unlock() const = 0;
};
class PYMutex : public IMutex
{
public:
PYMutex();
~PYMutex();
virtual void Lock() const;
virtual bool TryLock() const;
virtual void Unlock() const;
private:
#ifdef _WIN32
HANDLE m_mutex;
#else
mutable pthread_mutex_t m_mutex;
#endif
};
#endif //PYMUTEX_H
磁盘管理类,在进行磁盘信息读取和文件删除时,是借用了DiskSpace.h和DiskSpace.cpp源文件声明定义的磁盘空间统计和日志删除相关函数集来实现的,DiskSpace.h内容如下:
#ifndef DISK_SPACE_H
#define DISK_SPACE_H
/***********************************************************************
*Copyright 2023-02-06, pyfree
*
*File Name : DiskSpace.h
*File Mark :
*Summary :
*磁盘空间统计和日志删除相关函数集
*Current Version : 1.00
*Author : pyfree
*FinishDate :
*
*Replace Version :
*Author :
*FinishDate :
************************************************************************/
#include <string>
namespace pyfree
{
/**
* 根据指定磁盘获取磁盘的总空间和剩余空间
* @param _DiskStr {char} 磁盘目录,一般win指定{C,D},linux指定{/}
* @param _totalSize {int} 返回磁盘总空间
* @param _freeSize {int} 返回磁盘剩余空间
* @return {int} 预留的返回值,暂指定是常量1
*/
int getDiskFreeSpace(char _DiskStr, int &_totalSize, int &_freeSize);
/**
* 根据指定目录 扩展名 天数限制等删除目录下的文件,项目用于删除旧日志操作
* @param _dir {string}} 目录
* @param extname {string} 扩展名,如txt/ini/log等
* @param dayForLimit_ {int} 指定天数,默认为0时会删除非当天的其他文件
* @return {void} 无返回
*/
void moveOldFile(std::string _dir, const std::string &extname, int dayForLimit_=0);
/**
* 获取当天凌晨时刻的偏移时间,单位秒,与1970-01-01 00:00:00起
* @param deviation {int} 偏移秒数
* @return {int} 当天凌晨时刻的偏移时间.单位秒
*/
int getCurDayZeroClockTime(int deviation=0);
};
#endif
三、工程结构及编程测试
3.1 工程目录结构
按上述源文件设计,给出组织这些源文件的工程目录结构:
log_test
bin #测试程序输出
build_win #win编译中间文件存储
build_linux #Linux编译中间文件存储
src
DiskSpace.h #磁盘访问、文件删除等功能实现
DiskSpace.cpp
Log.h #日志类,单体模式,继承线程类
Log.cpp
LogDef.h #日志信息定义
Mutex.h #互斥锁
Mutex.cpp
myThread.h #线程类
myThread.cpp
pysleep.h #线程睡眠等待统一宏转换设计
queuedata.h #消息队列类模板
queuedata.cpp
spaceMgr.h #磁盘空间管理类,继承线程类
spaceMgr.cpp
test
main.cpp #测试程序代码
CMakeLists.txt #cmake工程
compile.txt #工程编译指令
3.2 cmake工程配置
CMakeLists.txt,cmake工程配置文件
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (log_test)
#
if(WIN32)
message(STATUS "windows compiling...")
add_definitions(-D_PLATFORM_IS_WINDOWS_)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd")
set(WIN_OS true)
else(WIN32)
message(STATUS "linux compiling...")
add_definitions( -D_PLATFORM_IS_LINUX_)
add_definitions("-Wno-invalid-source-encoding")
# add_definitions("-O2")
set(UNIX_OS true)
set(_DEBUG true)
endif(WIN32)
#
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
# 指定源文件的目录,并将名称保存到变量
SET(source_h
${PROJECT_SOURCE_DIR}/src/pysleep.h
${PROJECT_SOURCE_DIR}/src/Mutex.h
${PROJECT_SOURCE_DIR}/src/myThread.h
${PROJECT_SOURCE_DIR}/src/queuedata.h
${PROJECT_SOURCE_DIR}/src/LogDef.h
${PROJECT_SOURCE_DIR}/src/Log.h
${PROJECT_SOURCE_DIR}/src/DiskSpace.h
${PROJECT_SOURCE_DIR}/src/spaceMgr.h
)
SET(source_cpp
#
${PROJECT_SOURCE_DIR}/src/Mutex.cpp
${PROJECT_SOURCE_DIR}/src/myThread.cpp
${PROJECT_SOURCE_DIR}/src/Log.cpp
${PROJECT_SOURCE_DIR}/src/DiskSpace.cpp
${PROJECT_SOURCE_DIR}/src/spaceMgr.cpp
${PROJECT_SOURCE_DIR}/test/main.cpp
)
#头文件目录
include_directories(${PROJECT_SOURCE_DIR}/src)
if (${UNIX_OS})
add_definitions(
"-W"
"-fPIC"
"-Wall"
# "-Wall -g"
"-Werror"
"-Wshadow"
"-Wformat"
"-Wpointer-arith"
"-D_REENTRANT"
"-D_USE_FAST_MACRO"
"-Wno-long-long"
"-Wuninitialized"
"-D_POSIX_PTHREAD_SEMANTICS"
"-DACL_PREPARE_COMPILE"
"-Wno-unused-parameter"
"-fexceptions"
)
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0")
link_directories()
# 指定生成目标
add_executable(log_test ${source_h} ${source_cpp})
#link
target_link_libraries(log_test
-lpthread -pthread -lz -lrt -ldl
)
endif(${UNIX_OS})
if (${WIN_OS})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4819")
add_definitions(
"-D_CRT_SECURE_NO_WARNINGS"
"-D_WINSOCK_DEPRECATED_NO_WARNINGS"
"-DNO_WARN_MBCS_MFC_DEPRECATION"
"-DWIN32_LEAN_AND_MEAN"
)
link_directories()
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${PROJECT_SOURCE_DIR}/bin)
# 指定生成目标
add_executable(log_testd ${source_h} ${source_cpp})
else(CMAKE_BUILD_TYPE)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${PROJECT_SOURCE_DIR}/bin)
# 指定生成目标
add_executable(log_test ${source_h} ${source_cpp})
endif (CMAKE_BUILD_TYPE)
endif(${WIN_OS})
compile.txt,编译指令指示文件
win:
cd log_test && mkdir build_win && cd build_win
cmake -G "Visual Studio 10 2010 Win64" -DCMAKE_BUILD_TYPE=Release ..
msbuild log_test.sln /p:Configuration="Release" /p:Platform="x64"
cmake -G "Visual Studio 10 2010 Win64" -DCMAKE_BUILD_TYPE=Debug ..
msbuild log_test.sln /p:Configuration="Debug" /p:Platform="x64"
Linux:
cd log_test
mkdir build_linux
cd build_linux
cmake ..
make
3.3 日志类调用,测试案例
测试工程文件main.cpp:
#include <stdio.h>
#include <stdlib.h>
#include "pysleep.h"
#include "Log.h"
namespace GlobalVar {
extern char disk_DescStr; //磁盘名
extern unsigned int disk_space_limit; //剩余空间限制为*MB
extern std::string logdir; //后期记录日志目录重配置文件读取
extern std::string logname; //可设置为服务名
extern std::string log_ext_flag; //日志扩展名
};
//log_test.exe D 2000 log log
//log_test.exe D
void arg_analysis(int argc, char *argv[]);
int main(int argc, char *argv[])
{
arg_analysis(argc,argv);
int i = 0;
while(i<100)
{
CLogger::createInstance()->Log(LogLevel(rand()%5),"log test for [%d]",i++);
pysleep(10);
}
//while(1){pysleep(10);}
return 0;
}
void arg_analysis(int argc, char *argv[])
{
for(int index=0; index<argc; index++)
{
switch(index)
{
case 0:
{
#ifdef WIN32
std::string appname = std::string(argv[0]);
GlobalVar::logname = appname.substr(0,appname.length()-4);
#else
GlobalVar::logname = std::string(argv[0]);
#endif
}
break;
case 1:
{
GlobalVar::disk_DescStr = argv[1][0];
}
break;
case 2:
{
GlobalVar::disk_space_limit = static_cast<unsigned int>(atoi(argv[2]));
}
break;
case 3:
{
GlobalVar::logdir = std::string(argv[3]);
}
break;
case 4:
{
GlobalVar::log_ext_flag = std::string(argv[4]);
}
break;
default:
break;
}
}
}
3.4 编译及测试
win:
linux:
四、补充源码
部分源码前面已经给出(头文件),下面给出其他源码文件:
pysleep.h
#ifndef _PY_SLEEEP_H_
#define _PY_SLEEEP_H_
#ifdef WIN32
#include <windows.h>
#define pysleep(x) Sleep(x)
#else
#define pysleep(x) usleep(1000*x)
#endif
#endif //_PY_SLEEEP_H_
DiskSpace.cpp
#include "DiskSpace.h"
#include <time.h>
#include <list>
#include <vector>
#ifdef WIN32
#include <io.h>
#include <direct.h>
#include <Windows.h>
#else
#include <sys/statfs.h>
#include <dirent.h>
/*#include <sys/vfs.h> or <sys/statfs.h> */
#include <sys/vfs.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#endif
#include "Log.h"
int pyfree::getDiskFreeSpace(char _DiskStr, int &_totalSize, int &_freeSize)
{
#ifdef WIN32
BOOL fResult;
unsigned _int64 i64FreeBytesToCaller;
unsigned _int64 i64TotalBytes;
unsigned _int64 i64FreeBytes;
char dir[4] = { _DiskStr, ':', '\\' };
fResult = GetDiskFreeSpaceEx(
dir,
(PULARGE_INTEGER)&i64FreeBytesToCaller,
(PULARGE_INTEGER)&i64TotalBytes,
(PULARGE_INTEGER)&i64FreeBytes);
//GetDiskFreeSpaceEx function,get the disk space status,return BOOL type
if (fResult)//jude the disk is in work status by the return value
{
_totalSize = static_cast<int>(static_cast<float>(i64TotalBytes) / 1024 / 1024);
_freeSize = static_cast<int>(static_cast<float>(i64FreeBytesToCaller) / 1024 / 1024);
//std::cout << " totalspace:" << _totalSize << " MB" << std::endl;//disk total size
//std::cout << " freespace:" << _freeSize << " MB" << std::endl;//disk free space sze
}
#endif // WIN32
#ifdef __linux__
//printf("DiskFlag(%c)\n", _DiskStr);
struct statfs diskInfo;
statfs("/", &diskInfo);
unsigned long long totalBlocks = diskInfo.f_bsize;
unsigned long long totalSize = totalBlocks * diskInfo.f_blocks;
_totalSize = static_cast<int>(totalSize >> 20);
//printf("TOTAL_SIZE == %d MB\n", _totalSize);
unsigned long long freeDisk = diskInfo.f_bfree*totalBlocks;
_freeSize = static_cast<int>(freeDisk >> 20);
//printf("DISK_FREE == %d MB\n", _freeSize);
#endif
return 1;
}
//delete old file which is older taday for the file modify time
void pyfree::moveOldFile(std::string _dir, const std::string &extname, int dayForLimit_)
{
try{
#ifdef WIN32
_finddata_t fileInfo;
intptr_t hFile;
std::string filter = _dir;
if (filter[filter.size() - 1] != '//' || filter[filter.size() - 1] != '\\') {
filter.push_back('\\');
}
filter += "*.";
filter += extname;
//time_t file_time_ = time(NULL);
time_t file_time_ = (time_t)getCurDayZeroClockTime(dayForLimit_*86400);
//get
hFile = _findfirst(filter.c_str(), &fileInfo);
if (hFile == -1) {
return;
}
std::string delOldFile = "";
//find the oldest file by modify time
do {
if (extname.empty())//no ext name, that is dir
{
if ((fileInfo.attrib & _A_SUBDIR)) {
if (0 == strcmp(fileInfo.name, ".") || 0 == strcmp(fileInfo.name, ".."))
{
continue;
}
}
}
//if(fileInfo.time_write<_time)//modify time
if (fileInfo.time_create<file_time_)//create time
{
file_time_ = fileInfo.time_create;
delOldFile = _dir + "//" + (std::string(fileInfo.name));
}
} while (_findnext(hFile, &fileInfo) == 0);
_findclose(hFile);
#endif
#ifdef linux
//printf("moveOldFile(*.%s) 1 from(%s) \n",extname.c_str(),_dir.c_str());
std::string curdir = _dir;
if (curdir[curdir.size() - 1] != '/') {
curdir.push_back('/');
}
DIR *dfd;
if ((dfd = opendir(curdir.c_str())) == NULL)
{
CLogger::createInstance()->Log(eLog_WARNING,"open %s error with msg is: %s\n", curdir.c_str(), strerror(errno));
return;
}
struct dirent *dp;
//time_t file_time_ = time(NULL);
time_t file_time_ = (time_t)getCurDayZeroClockTime(dayForLimit_ * 86400);
std::string delOldFile = "";
while ((dp = readdir(dfd)) != NULL)
{
if (extname.empty())//no ext name, that is dir
{
if (dp->d_type == DT_DIR) {
if (0 == strcmp(dp->d_name, ".") || 0 == strcmp(dp->d_name, ".."))
{
continue;
}
}
}
else {
if (NULL == strstr(dp->d_name, extname.c_str()))
{
continue;
}
}
std::string _path = _dir + "/";
_path += dp->d_name;
struct stat el;
stat(_path.c_str(), &el);
if (el.st_mtime<file_time_) {
file_time_ = el.st_mtime;
delOldFile = _path;
}
}
if (NULL != dp) {
delete dp;
dp = NULL;
}
if (NULL != dfd) {
closedir(dfd);
dfd = NULL;
}
#endif
if (!delOldFile.empty())
{
//printf("get old file: %s \n", delOldFile.c_str());
int ret = remove(delOldFile.c_str());
if (0 != ret) {
CLogger::createInstance()->Log(eLog_WARNING,"can't remove %s \n", delOldFile.c_str());
}else{
CLogger::createInstance()->Log(eLog_NOTICE,"success remove %s \n", delOldFile.c_str());
}
}
}catch(...){
CLogger::createInstance()->Log(eLog_ERROR,"moveOldFile(*.%s) from(%s) exception error\n"
,extname.c_str(),_dir.c_str());
}
}
int pyfree::getCurDayZeroClockTime(int deviation)
{
int ZeroClockTime_ = 0;
time_t cur_time_ = time(NULL);
struct tm _tt;
#ifdef WIN32
localtime_s(&_tt, &cur_time_);
#else
localtime_r(&cur_time_,&_tt);
#endif
//当日凌晨时刻
_tt.tm_hour = 0;
_tt.tm_min = 0;
_tt.tm_sec = 0;
ZeroClockTime_ = static_cast<int>(mktime(&_tt));
ZeroClockTime_ -= deviation;//偏移时间
return ZeroClockTime_;
}
Log.cpp
#include "Log.h"
#include <time.h>
#include <sys/timeb.h>
#include <stdio.h>
#include <stdarg.h>
#include <string>
#ifdef __linux__
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#ifndef sprintf_s
#define sprintf_s sprintf
#endif
#else //WIN32
#ifndef vsnprintf
#define vsnprintf vsnprintf_s
#endif
#endif
#include "pysleep.h"
#include "spaceMgr.h"
namespace GlobalVar
{
char disk_DescStr = 'D'; //磁盘名
unsigned int disk_space_limit = 2000; //剩余空间限制为*MB,少于该值去删除日志释放空间
std::string logdir = "log"; //后期记录日志目录重配置文件读取
std::string logname = "pyfree"; //可设置为服务名
std::string log_ext_flag = "log";//日志扩展名
};
CLogger* CLogger::m_pLogInstance = NULL;
CLogger::CLogger()
: running(true)
, fpLog(NULL)
, cur_log_path("log")
{
char buf[256] = {0};
sprintf_s(buf,"mkdir %s",GlobalVar::logdir.c_str());
system(buf);
this->start();
disk_space_mgr = new DiskSpaceMgr(GlobalVar::disk_DescStr
,GlobalVar::disk_space_limit,GlobalVar::logdir,GlobalVar::log_ext_flag);
disk_space_mgr->start();
};
CLogger::~CLogger()
{
delete disk_space_mgr;
disk_space_mgr = NULL;
running = false;
if (NULL != fpLog)
{
fclose(fpLog);
fpLog = NULL;
}
};
CLogger* CLogger::createInstance( void )
{
if (m_pLogInstance == NULL)
{
m_pLogInstance = new CLogger();
return m_pLogInstance;
}
else
return m_pLogInstance;
};
int CLogger::Run()
{
MyLogStruct<> log_item;
while (running)
{
if(logs_cache.pop(log_item))
{
WriteLog(log_item.type, log_item.szBuffer);
#ifdef _DEBUG
#ifndef WIN32
printf("Log::[%d]-->%s\n", getpid(), log_item.szBuffer);
#else
printf("Log::-->%s\n", log_item.szBuffer);
#endif //WIN32
#endif //_DEBUG
}
pysleep(1);
}
return 0;
};
void CLogger::Log(const eLogType type, const char* lpszFormat, ...)
{
va_list args;
MyLogStruct<> log_item;
log_item.type = static_cast<int>(type);
va_start(args, lpszFormat);
vsnprintf(log_item.szBuffer, sizeof(log_item.szBuffer), lpszFormat, args);
va_end(args);
logs_cache.add(log_item);
}
void CLogger::WriteLog( const int iMsgType, const char * strMsg)
{
try {
//system time to string
std::string strTime_ = strTime();
//file name
std::string strPath = createFileName(strTime_.substr(0,10));
// log time and type desc
std::string strTimeAndTypeDesc = createLogTimeAndTypeDesc(iMsgType, strTime_);
WriteLogToFile(strPath,strTimeAndTypeDesc,strMsg);
}
catch (...) {
printf("write log[%d]{%s}error\n", iMsgType, strMsg);
}
}
void CLogger::WriteLogToFile( std::string strPath,std::string strTime_,const char * strMsg)
{
if(cur_log_path!=strPath||NULL == fpLog)
{
if (NULL != fpLog)
{
fclose(fpLog);
fpLog = NULL;
}
//open
#ifdef WIN32
fopen_s(&fpLog, strPath.c_str(), "a+");
#else
fpLog = fopen(strPath.c_str(), "a+");
#endif
}
if (NULL != fpLog)
{
fseek(fpLog, 0, SEEK_END);
fwrite(strTime_.c_str(), strTime_.length(), 1, fpLog);
fwrite(strMsg, strlen(strMsg), 1, fpLog);
fwrite("\n", 1, 1, fpLog);
}
}
std::string CLogger::createFileName(std::string strTime_)
{
//file name
std::string strPath = GlobalVar::logdir;
#ifdef WIN32
strPath += "\\";
#else
strPath += "/";
#endif
strPath += strTime_;
strPath += "_";
strPath += GlobalVar::logname;
strPath += ".log";
return strPath;
}
std::string CLogger::strTime()
{
time_t tt;
struct timeb tm0;
struct tm tm1;
char buf[64];
ftime(&tm0);
tt = tm0.time;
#ifdef WIN32
localtime_s(&tm1, &tt);
#else
localtime_r(&tt, &tm1);
#endif
sprintf_s(buf, "%04d-%02d-%02d %02d:%02d:%02d.%03d "
, tm1.tm_year + 1900, tm1.tm_mon + 1, tm1.tm_mday
, tm1.tm_hour, tm1.tm_min, tm1.tm_sec, tm0.millitm);
std::string strTime_ = buf;
buf[10] = '\0';
return strTime_;
}
//日志等级枚举值的字符串描述
std::string eLogTypeDesc[6] = {
"[UNKNOWN] ",
"[eLog_DEBUG] ",
"[eLog_TRACE] ",
"[eLog_NOTICE] ",
"[eLog_WARNING] ",
"[eLog_ERROR] "};
std::string CLogger::createLogTimeAndTypeDesc(const unsigned int iMsgType, std::string strTime_)
{
std::string strTimeAndTypeDesc = strTime_;
unsigned int msg_type = iMsgType;
if(msg_type>6) msg_type = 0; //非枚举值外的均为未知
strTimeAndTypeDesc += eLogTypeDesc[msg_type];
return strTimeAndTypeDesc;
};
/*以下是LogFunction*/
/*
*在queuedata.cpp中应用,该函数主要用来解决QueueData类嵌套调用CLogger的异常问题:
*“嵌套名指定中使用了不完全的类型”
*/
void reportLogCacheOver(const char *queue_desc,int size)
{
//每溢出10次,报告一次
CLogger::createInstance()->Log(eLog_WARNING,
"add item to queue %s, but the size of QueueData is up to limmit size: %d.\n"
, queue_desc, size);
}
Mutex.cpp
#include "Mutex.h"
#ifdef WIN32
#include <windows.h>
#endif
//#include <iostream>
#include <stdio.h>
PYMutex::PYMutex()
{
#ifdef _WIN32
m_mutex = ::CreateMutex(NULL, FALSE, NULL);
#else
pthread_mutex_init(&m_mutex, NULL);
#endif
}
PYMutex::~PYMutex()
{
#ifdef _WIN32
::CloseHandle(m_mutex);
#else
pthread_mutex_destroy(&m_mutex);
#endif
}
void PYMutex::Lock() const
{
#ifdef _WIN32
//DWORD d = WaitForSingleObject(m_mutex, INFINITE);
WaitForSingleObject(m_mutex, INFINITE);
/// \todo check 'd' for result
#else
pthread_mutex_lock(&m_mutex);
#endif
}
bool PYMutex::TryLock() const
{
#ifdef _WIN32
DWORD dwWaitResult = WaitForSingleObject(m_mutex, 0);
if (dwWaitResult != WAIT_OBJECT_0 && dwWaitResult != WAIT_TIMEOUT) {
printf("thread WARNING: bad result from try-locking mutex\n");
}
return (dwWaitResult == WAIT_OBJECT_0) ? true : false;
#else
return (0==pthread_mutex_trylock(&m_mutex))?true:false;
#endif
};
void PYMutex::Unlock() const
{
#ifdef _WIN32
::ReleaseMutex(m_mutex);
#else
pthread_mutex_unlock(&m_mutex);
#endif
}
myThread.cpp
#include "myThread.h"
#ifdef WIN32
#include <windows.h>
#else
#include <stdio.h>
#endif
MyThread::MyThread()
{
#ifdef __linux__
hThread = 0;
threadStatus = THREAD_STATUS_NEW;
#endif
}
MyThread::~MyThread()
{
#ifdef WIN32
WaitForSingleObject(hThread, INFINITE);
#else
join(10);
#endif
}
int MyThread::Run()
{
#ifdef WIN32
printf("Base Thread\n");
#else
while(true){
printf("thread is running!\n");
sleep(100);
}
#endif
return 0;
}
bool MyThread::start()
{
#ifdef WIN32
return NULL!=(hThread =(HANDLE_THREAD)_beginthread(agent, 0, (void *)this));
#else
return pthread_create(&hThread, NULL, run0, this) == 0;
#endif
}
HANDLE_THREAD MyThread::getThreadID()
{
return hThread;
}
#ifdef WIN32
void MyThread::agent(void *p)
{
MyThread *agt = (MyThread *)p;
agt->Run();
}
#else
void* MyThread::run0(void* pVoid)
{
MyThread* p = (MyThread*) pVoid;
p->run1();
return p;
}
void* MyThread::run1()
{
threadStatus = THREAD_STATUS_RUNNING;
hThread = pthread_self();
Run();
threadStatus = THREAD_STATUS_EXIT;
hThread = 0;
pthread_exit(NULL);
}
int MyThread::getState()
{
return threadStatus;
}
void MyThread::join()
{
if (hThread > 0)
{
pthread_join(hThread, NULL);
}
}
void MyThread::join(unsigned long millisTime)
{
if (hThread == 0)
{
return;
}
if (millisTime == 0)
{
join();
}else
{
unsigned long k = 0;
while (threadStatus != THREAD_STATUS_EXIT && k <= millisTime)
{
usleep(100);
k++;
}
}
}
#endif
queuedata.cpp
#include "queuedata.h"
#include "LogDef.h"
#include "Log.h"
extern void reportLogCacheOver(const char*,int);
template <class T>
unsigned int QueueData<T>::QSize = 100;
template <class T>
QueueData<T>::QueueData(std::string desc)
: queue_desc(desc)
{
init();
};
template <class T>
void QueueData<T>::init()
{
queue_overS = 0;
};
template <class T>
QueueData<T>::~QueueData()
{
}
//
template <class T>
int QueueData<T>::size()
{
int ret = 0;
m_Mutex.Lock();
ret = static_cast<int>(datacache_queue.size());
m_Mutex.Unlock();
return ret;
}
template <class T>
bool QueueData<T>::isEmpty()
{
bool ret = false;
m_Mutex.Lock();
ret = datacache_queue.empty();
m_Mutex.Unlock();
return ret;
}
template <class T>
bool QueueData<T>::getFirst(T &it)
{
bool ret = false;
m_Mutex.Lock();
if (!datacache_queue.empty())
{
it = datacache_queue.front();
ret = true;
}
m_Mutex.Unlock();
return ret;
}
template <class T>
bool QueueData<T>::removeFirst()
{
bool ret = false;
m_Mutex.Lock();
if (!datacache_queue.empty())
{
datacache_queue.pop_front();
ret = true;
}
m_Mutex.Unlock();
return ret;
}
template <class T>
bool QueueData<T>::pop(T &it)
{
bool ret = false;
m_Mutex.Lock();
if (!datacache_queue.empty())
{
it = datacache_queue.front();
datacache_queue.pop_front();
ret = true;
}
m_Mutex.Unlock();
return ret;
};
template <class T>
bool QueueData<T>::getList(std::queue<T> &its,unsigned int sizel)
{
m_Mutex.Lock();
while (!datacache_queue.empty())
{
its.push(datacache_queue.front());
datacache_queue.pop_front();
if (its.size() >= sizel)
{
break;
}
}
m_Mutex.Unlock();
return !its.empty();
};
template <class T>
void QueueData<T>::add(T it)
{
m_Mutex.Lock();
if (datacache_queue.size() > QSize)
{
queue_overS++;
datacache_queue.pop_front();
}
datacache_queue.push_back(it);
m_Mutex.Unlock();
if (queue_overS >= 10)
{
//每溢出10次,报告一次
reportLogCacheOver(queue_desc.c_str(),QSize);
queue_overS = 0;
}
}
template <class T>
void QueueData<T>::add_front(T it)
{
m_Mutex.Lock();
if (datacache_queue.size() > QSize)
{
queue_overS++;
datacache_queue.pop_front();
}
datacache_queue.push_front(it);
m_Mutex.Unlock();
if (queue_overS >= 10)
{
reportLogCacheOver(queue_desc.c_str(),QSize);
queue_overS = 0;
}
}
template <class T>
void QueueData<T>::clear()
{
m_Mutex.Lock();
datacache_queue.clear();
m_Mutex.Unlock();
queue_overS = 0;
}
spaceMgr.cpp
#include "spaceMgr.h"
#include "pysleep.h"
#include "DiskSpace.h"
DiskSpaceMgr::DiskSpaceMgr(char disk_, int fsl_
, std::string dir_/*="log"*/, std::string ext_/*="log"*/)
: DiskStr(disk_),freeSizeLimit(fsl_)
{
DelInfo del_(dir_,ext_);
dirs.push_back(del_);
}
DiskSpaceMgr::~DiskSpaceMgr(void)
{
}
int DiskSpaceMgr::Run()
{
int i_totalSize = 0;
int i_freeSize = 0;
while (true)
{
if (pyfree::getDiskFreeSpace(DiskStr, i_totalSize, i_freeSize) > 0)
{
if (freeSizeLimit > i_freeSize)
{
for(unsigned int i=0; i<dirs.size(); ++i)
{
std::string ext = dirs.at(i).ext;
pyfree::moveOldFile(dirs.at(i).dir, ext);
}
}
}
pysleep(10);
}
return 0;
}
void DiskSpaceMgr::add(std::string dir_,std::string ext_)
{
DelInfo del_(dir_,ext_);
dirs.push_back(del_);
}