前段时间公司运维部门要求我对CDN节点的日志进行分析,具体要求如下
1、输入参数为文件名,支持统配。文件为gz压缩,可用zcat解压。统配的多个文件,
必须是一天内的。
2、文件内为一条条的访问日志。格式如下:
2011-01-02 15:55:01 122.245.127.73
"/down.eebbk.net/xzzx/h1sp/\xb8\xdf\xd6\xd0\xc9\xfa\xce\xef\xb1\xd8\xd0\xde2
\xc8\xbe\xc9\xab\xcc\xe5\xb1\xe4\xd2\xec(\xb6\xfe).avi" 206 11568567 3316812
" http://www.eebbk.com/downlist.asp?sid=12814&classid=17356&tinyclassid=17374
&dhbig=\xbb\xc6\xb8\xd4\xbf\xce\xcc\xc3&dhsmall=\xb8\xdf\xd6\xd0\xb1\xd8\xd0
\xde2&dhtiny=\xc9\xfa\xce\xef&title=\xca\xd3\xc6\xb5\xd1\xa7\xcf\xb0xbb\xfa
H1&mode=6846" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET
CLR 1.1.4322; .NET CLR 2.0.50727)"
3、该条日志中,2011-01-02 15:55:01 为日志时间点。其基准时间点的计算方法为,
将该时间点转换为标准的秒,再模300,得到基准时间点。如15:55。再比如,
18:23:08,其基准时间点位18:20。
4、该条日志中,表示应用层在3316812 毫秒,输出了11568567 字节。该信息折算到日
志统计上,是按如下处理:
--3316812 毫秒为 3316812/300000+1 = 11+1 = 12个日志计费点。(每个日志计费点
代表一个5分钟的区间)
--11568567字节平均分到12个日志计费点上,为该记录在该计费点上的带宽占用。如
11568567/12/300=3213bps
5、则该日志的流量将给15:00、15:05、15:10、15:15、15:20、15:25、15:30、
15:35、15:40、15:45、15:50、15:55这12个基准时间点,每个增加3123bps的值。
6、要求统计所有指定日志文件综合的各基准时间点的带宽
7、将各基准时间点的带宽值以如下格式输出显示:
time     bps
00:00   1903342
00:05   1833133
....
23:55   2312342
Total Traffic
 
8、由于该程序将是手动运行在Apache服务器上的。所以程序要轻量级,同时不用占用
太多的磁盘。或许比较理想的是直接处理zcat的输出。(服务器上没有php)
我花了5个小时,先写出了一个单线程的程序,后来又改为多线程,感觉用perl写起来比较快,之间学会了用parsewords进行解析日志,parsewords太好用了。
执行命令:perl analy_log.pl CT-ZHZ-1-N004-A-bbg04_2011010414*
测试结果,分析几十M的压缩日志,才2秒多

 

用perl对CDN节点日志进行统计_第1张图片

代码如下:

 

   
   
   
   
  1. use Text::ParseWords;  
  2. use threads;  
  3. use threads::shared;  
  4. use Time::Local;  
  5. #print @ARGV;  
  6.  
  7. #分析结果集  
  8.  
  9. my %result:shared;  
  10. #最大线程数  
  11.  
  12. my $max_thread = 2;  
  13. #线程池  
  14.  
  15. my @thread_array;  
  16. my $current_thread = 0;  
  17.  
  18. #检查参数  
  19.  
  20. $argv_len = @ARGV;  
  21. if($argv_len == 0)  
  22. {  
  23.     print "it need filename\n";  
  24.     exit(1);  
  25. }  
  26.  
  27. #处理参数开始  
  28.  
  29. $cmd="ls '".join("' '",@ARGV)."' |";  
  30. #print $cmd."\n";  
  31.  
  32.  
  33. #处理参数结束  
  34.  
  35.  
  36. open(PIPE, $cmd);   
  37. @filenames = ;   
  38. close(PIPE);  
  39. #print "[";  
  40.  
  41. #print @filenames;  
  42.  
  43. #print "]\n";  
  44.  
  45.  
  46. #多线程分析多个日志文件  
  47.  
  48. foreach(@filenames){   
  49.     if( $current_thread >= $max_thread )  
  50.   {  
  51.     foreach my $thread( @thread_array )  
  52.     {  
  53.       $thread -> join( );  
  54.     }  
  55.     $current_thread = 0;  
  56.     @thread_array=();  
  57.   }  
  58.   $thread_array[$current_thread] = threads -> new(\&analy,$_);  
  59.   $current_thread ++;   
  60. }  
  61.  
  62. #等待线程结束  
  63.  
  64. foreach my $thread( @thread_array )  
  65. {  
  66.    $thread -> join();  
  67. }  
  68.  
  69. #按时间排序输出  
  70.  
  71. my @key=sort(keys(%result));  
  72. print "time\t\t\tbps\n";  
  73. foreach (@key){   
  74.         $k=$_;  
  75.         print $k."\t\t".$result{$k}."\n";  
  76. }  
  77.  
  78. #去除前后空白字符  
  79.  
  80. sub trim  
  81. {  
  82.         my $string = shift;  
  83.         $string =~ s/^\s+//;  
  84.         $string =~ s/\s+$//;  
  85.         return $string;  
  86. }  
  87.  
  88. #分析一个日志文件  
  89.  
  90. sub analy  
  91. {  
  92.     my $filename= trim(shift);  
  93.     $cmd="zcat $filename |";  
  94.     open(PIPEFILE,$cmd);  
  95.     @lines=;  
  96.     close(PIPEFILE);  
  97.     foreach(@lines)  
  98.     {  
  99.         #分解每一行  
  100.  
  101.         my @line=quotewords(" ", 1, $_);  
  102.         #print join(",",@line)."\n";  
  103.  
  104.         #print $line[6]."\n";  
  105.  
  106.           
  107.         #根据时间合并  
  108.  
  109.         my($h,$m)=split(/:/g,$line[1]);  
  110.         my($y,$mon,$d)=split(/-/g,$line[0]);  
  111.         $m=int($m/5)*5;  
  112.         my $point=int($line[6]/300000+1);  
  113.         my $bps=int($line[5]/$point/300);  
  114.      #print $line[0].$line[1]." m=$m,point=$point,bps=$bps\n";  
  115.  
  116.         $endtime=timelocal(0,$m,$h,$d,$mon,$y-1900);#秒,分,时,日,月,年(year-1900)  
  117.  
  118.         my @time_list=get_time_list($endtime,$point);  
  119.         #累计字节数  
  120.  
  121.         my $i=0;  
  122.         for($i=0;$i<$point;$i++)  
  123.         {  
  124.             $result{$time_list[$i]}+=$bps;  
  125.         }  
  126.     }  
  127. }  
  128. #获得时间列表  
  129.  
  130. sub get_time_list  
  131. {  
  132.     my $endtime=trim(shift);  
  133.     my $point=trim(shift);  
  134.     my @time_list;  
  135.     my $i=0;  
  136.     for($i=0;$i<$point;$i++)  
  137.     {  
  138.         push(@time_list,get_time_str($endtime));  
  139.         $endtime-=300;  
  140.     }  
  141.     @time_list;  
  142. }  
  143. #获得时间字符串  
  144.  
  145. sub get_time_str  
  146. {  
  147.     my $endtime=trim(shift);  
  148.     #@t=gmtime($endtime);  
  149.  
  150.     ($sec, $min, $hour, $day, $mon, $year, $wday, $yday, $isdst) = localtime($endtime);  
  151.     if($min<10)  
  152.     {  
  153.         $min="0$min";  
  154.     }  
  155.     if($hour<10)  
  156.     {  
  157.         $hour="0$hour";  
  158.     }  
  159.     if($day<10)  
  160.     {  
  161.         $day="0$day";  
  162.     }  
  163.     if($mon<10)  
  164.     {  
  165.         $mon="0$mon";  
  166.     }  
  167.     $year+=1900;  
  168.     my $str="$year-$mon-$day_$hour:$min";