服务端研发应具备的技能(2)

1,日志处理


通常我们处理文件,大多数都是处理完即程序退出,但在IT行业里,尤其是互联网公司,日志不是一时性的,而是源源不断的一直生成中,所以要求你的程序也需要像linux 下的tail -f命令一样,可以一直跟着文件读并处理日志,当日志文件按时天(或小时或分钟)切换时,也需要日志处理程序也可以无缝的处理

那么如何实现模拟linux下的tail -f方式处理目录下日志文件呢?


首先,需要将目录中的文件读取到map 中,然后按生成时间一个个的处理,当处理到最后一个的时候需要进行以下判断:

1,是否有新的文件生成

2,是否读到文件尾


当有新的文件生成时需要进行文件切换,否则在当前文件尾端等待新的追加日志


获取目录中文件列map如下:

其中,searchDir为目录, pattern为查找的日志文件(一般为2011-01-01-01.log,这样这个 pattern为20*.log即可)

输出的map中,key为日志文件的时间,value为日志文件名(这里需要注意,如果文件日志时间一样将会导致map中的key只有一个而遗漏日志,这时需要将key换成计数的整数则可以避免)

#include <iostream>
#include <map>


#include <libgen.h>//basename | dirname
#include <fstream>
#include <dirent.h>
#include <fnmatch.h>
#include <sys/stat.h>
#include <signal.h>
#include <unistd.h>
#include <ctime>
#include <stdlib.h>



using namespace std;

std::map<int,string> getfilelist( std::string &searchDir ,std::string &pattern ) { 
        struct stat buf;


        std::map <int, std::string> dirNames;
        DIR* dir = opendir( searchDir.c_str() );


        if( NULL != dir ) { 
                struct dirent* entry;
                if( '/' != searchDir[searchDir.size() - 1] ) { 
                        searchDir += '/';
                }   
                int ret = 0;
                for(; NULL != (entry = readdir(dir)); ) { 
                        if(('.' == entry->d_name[0] && ('\0' == entry->d_name[1] || ('.' == entry->d_name[1] && '\0' == entry->d_name[2])))){
                                continue;
                        }   
                        ret = fnmatch( pattern.c_str(), entry->d_name, FNM_PATHNAME|FNM_PERIOD );
                        if(ret == 0) {
                                string tmp = string(searchDir) + entry->d_name;
                                //int retst = lstat(entry->d_name, &buf);
                                int retst = lstat(tmp.c_str(), &buf);
                                if( 0 != retst ) { 
                                        //cout << "lstat_error"<<endl;
                                }   
                                dirNames.insert( pair<int, string>(buf.st_ctime, searchDir + entry->d_name) );
                        } else if( ret == FNM_NOMATCH ) { 
                                continue ;
                        }   
                }   
                closedir( dir );
        }   
        return dirNames;
}



int main(int argc, char** argv){
        std::map<int,std::string>file_list;
        std::string dir="yourfile_dir/";
        std::string pat="*";


        time_t current = time(NULL);
        cout << current <<endl;


        file_list = getfilelist(dir, pat);
        for(std::map<int, std::string>::iterator it=file_list.begin(); it != file_list.end(); it++) {
                std::string filename = it->second;
                size_t ftime = it->first;
                cout << filename << "\t" << ftime <<endl;
                char*base = basename(filename.c_str());
                cout << base <<endl;
        }
}


有这这个getfilelist对应的map后,剩下的就是遍历这个map,按次序处理每一个value对应的文件,

只不是在处理时要看下是否达到上面说的1,2两个条件,需要注意的地方如下示:

为方便描述这里给出php程序示例,同时没有对文件操作等进行返回值判断

并且,每一个处理的日志文件都有一个对应的 .fpos的记录处理位置的文件(存放处理的位置),如果文件处理正常结束切换则将文件改名为  .fdone否则 .fbad

这里这个f_no就是为了上面提到的1,2所设置的判断条件

另外,这个逻辑虽然可以处理基本的tail -f方式处理文件,但是仍然存在很多问题(这里我希望需要你自己来修改,代码就不上了,这里只是给出思路与注意事项而已)



function process_filelist($filename_lists) {   
$f_no = 0;
foreach($filename_lists as $key => $filename) {
$fp = fopen($filename, 'r');
$skip_pos = GetFilePos($filename);//get position
$f_size = filesize($filename);
if($skip_pos == $f_size) {
if(1 == $len_lists){
return ERR_SAME_FILE;
}
}
$seek_ret = fseek($fp,$skip_pos,SEEK_SET);
if (0 != $seek_ret) {
return ERR_SEEK_FILE;
}


$fon = $filename . ".fpos";
$fpos = fopen("$fon","w");


$buffer = ""; 
$file_tail_not_valid = 0;
while($buffer = fgets($fp)) {
$len = strlen($buffer);
if ($buffer[$len - 1] != "\n" && feof($fp)) {
$file_tail_not_valid = 1;
break;
}
                                /*to do somethine*/
//    use $buffer
$skip_pos += $len;
fseek($fpos,0);
$str_pos = strval( $skip_pos );
$wret = fwrite($fpos,$str_pos);
}


fclose($fp);
fclose($fpos);
if($f_no < $len_lists && $len_lists > 1) {
if (1 == $file_tail_not_valid) {
$re_ok = rename("$filename","$filename.fbad");
} else {
if (($len_lists-1) != $f_no) {
$tmp_f = $filename . ".fdone";
$re_ok = rename("$filename","$tmp_f");
}
}
}
$f_no++;
}
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值