1 需求场景

     考虑如下需求场景:

     终端按固定时间间隔(单位为分钟)生成诊断日志(格式为UserName-Status-yyyy-mm-dd-hh-mm.log),并上传至服务器。若终端与服务器的传输通道中断,则终端本地暂存最新的N个日志文件,即第(N+1)个周期生成的新日志将覆盖第1个周期的旧日志,以此类推。待传输恢复后,终端一次性上传该N个日志。

     本文主要讨论该需求中“最新日志覆盖最旧日志”的功能点。为突出层次,文中“覆盖”用先删除旧日志后创建新日志实现。实际中先删后建和先建后删各有优点,前者适于内存受限(如仅允许存在N个固定大小的日志),后者更为安全(避免创建新日志失败但已删除旧日志)。

     创建文件时调用现成库函数即可,而删除时需确定当前最旧的那个文件。下文将介绍几种删除判决的实现方式(时间复杂度依次降低)。

 

2 实现思路

2.1 比较文件的创建时间

     Linux系统中文件没有创建时间的概念,只有访问时间(atime)、修改时间(mtime)和状态改动时间(ctime)。因该需求中日志创建后立即写入内容,其后内容和状态(权限与属性等)均不再改变,故可用mtime或ctime表征创建时间。

     调用stat库函数即可获取日志的三种时间,比较各日志的mtime或ctime(整数)信息即可。

 1 #define FILE_NUM       (unsigned short)30 //允许共存的文件数目
 2 #define FILE_NAME_LEN  sizeof("%Log-Clover-65535.log")
 3 
 4 #include <sys/types.h>
 5 #include <sys/stat.h>
 6 #include <dirent.h>
 7 #include <errno.h>
 8 #define DIR_FILE_LEN   96 //目录和文件组成的绝对路径最大长度
 9 const char *gDirectoryName = "/sdb1/Clover/linux_test/log";
10 
11 
12 int CreateLogFile1(const char *pszCreateFileName){//比较文件时间戳
13     DIR *pDir = opendir(gDirectoryName);
14     if(NULL == pDir){
15        fprintf(stderr,"Cannot open directory: %s!\n", gDirectoryName);
16        return -1;
17     }
18 
19     unsigned char ucFileNum = 0;
20     time_t tFileTempCtime = 0;
21     char szDelFileName[NAME_MAX+1] = {0};   //待删除的文件名
22     struct dirent *pFileEntry = NULL;
23     while((pFileEntry = readdir(pDir)) != NULL){
24         //剔除当前目录.,上一级目录..及隐藏文件,避免死循环遍历目录
25         if(0 == strncmp(pFileEntry->d_name, ".", 1))
26             continue;
27 
28         struct stat tFileStatus;          //文件状态信息
29         char szAbsFile[DIR_FILE_LEN] = {0};  //文件绝对路径
30         sprintf(szAbsFile, "%s/%s", gDirectoryName, pFileEntry->d_name);
31         if(stat(szAbsFile, &tFileStatus) != 0){
32             fprintf(stderr,"Call stat error(errno:%d)!\n", errno);
33             return -1;
34         }
35 
36         if(S_ISDIR(tFileStatus.st_mode)) //跳过子目录
37             continue;
38 
39         if((ucFileNum < 1) ||
40            (tFileStatus.st_ctime < tFileTempCtime)){ //首个文件或当前文件更老
41             tFileTempCtime = tFileStatus.st_ctime;
42             strcpy(szDelFileName, pFileEntry->d_name);
43         }
44         ucFileNum++;
45         if(ucFileNum >= FILE_NUM){
46             char szDeleteAbsFile[DIR_FILE_LEN] = {0};  //待删除的文件绝对路径
47             sprintf(szDeleteAbsFile, "%s/%s", gDirectoryName, szDelFileName);
48             if(0 != remove(szDeleteAbsFile)){
49                 fprintf(stderr,"Fail to delete file: %s!\n", szDeleteAbsFile);
50                 return -1;
51             }
52         }
53     }
54 
55     char szCreateAbsFile[DIR_FILE_LEN] = {0};  //待创建的文件绝对路径
56     sprintf(szCreateAbsFile, "%s/%s", gDirectoryName, pszCreateFileName);
57     FILE *pFile = fopen(szCreateAbsFile, "w+");
58     if(NULL == pFile){
59         fprintf(stderr,"Cannot open file: %s\n", szCreateAbsFile);
60         return -1;
61     }
62 
63     fputs(szCreateAbsFile, pFile); //暂时写入绝对路径
64     fclose(pFile);
65     printf("Create file(%s) success!\n", pszCreateFileName);
66 
67     closedir(pDir);
68 
69     return 0;
70 }
View Code

相关文章: