python(2):使用python分析大日志文件思路及过程

1.做服务器开发的经常会遇到要分析大量的日志,统计大量数据;这里介绍几种统计日志数据的方法和思路

之前有遇到过要统计几天内的url出现次数的事情,一天有24个gz压缩文件,每个文件大概6G左右,URL的不重复率也很高

使用方法:

1.用shell 解压然后在统计,shell脚本写起来麻烦,统计那一块很多人也不是很熟悉(不也不咋熟悉),sort又很慢,用下面的方法进行md5转换就更慢了

 while read line
   do 
     # 将字符串使用md5sum转换然后截取有用的部分
     m5=$(echo $line |md5sum | awk '{print $1}')
     echo $m5 >> ./1/$filename"_txt"
   done < ./$filename"_txt" 

2.使用lua(这个因为是本人最熟悉的脚本,所以先考虑的这个),在分析小数据的时候还是挺快的,数据量大了之后可能会产生内存分配失败的异常,32位下最大是2G,64位下理论上是可以达到2的64次方的,但是只要内有多余的内存分配就是抛出内存异常;而且在windows和linux下都需要独立安装,公司的有些服务器不提供安装许可,所以就不能用了


3.第三种使用python就行分片处理,python的问题和上面的lua一样,32位下允许2G,64位下允许2的64次方;linux系统基本都自带的有;但是即使是64位下加载数据大了之后也是会各种问题;

 问题: 1.空闲可用内存用完后python就会很卡着不动,或者抛出MemoryErr的异样,这个可以用try -catch处理,也可以分段处理

   2. python在读取文件的时候readline如果遇到无法识别的结束符eof的时候会报错(在gzip的部分版本是会出现的),可以使用try-catch处理或者更新gzip的库


4.第四种思路也是用python,就是将数据存入数据库就行处理,可以存入python自带的sqlite数据库(试过解析插入会比较慢);也可以存入redis这样的存储数据库会比较快


下面提供第三种的切片代码:

  切片的思路有两种:

     第一种是横向切片:就是按照常规的逻辑,每个文件的url就行合并统计,然后再就行一层一层的文件合并;这个的问题是如果url的重复率很低的话,后面的合并文件会越来越大,最终都会出现memoryErr的情况,好处是当URL重复率较高的话扫描一遍文件就可能可并完毕。

     第二种是纵向切片:就是按照数据就行分片,区url转换成md5之后的两个值就行取模,然后按照模的大小一遍一遍的扫描所有文件就行统计,可以根据分片的数据量调整模的大小,就不会出现内存不够的情况了,不足之处是要扫描好多次。


# -*- coding: utf-8 -*-
import os, sys
import hashlib
import gzip
import threading
from array import *
import gc
md = hashlib.md5()

# do file
class DoFile:

    # ??????
 def __init__(self):
  self.LineList = {}
  self.max = 0
  self.all_num_array = [0,0,0,0,0,0,0,0,0,0,0]
  self.out_file_num = 1
 
  self.all_max = 0
  self.file_num = 0
  self.one_max = 0
  self.one_file_num = 0

 #输出dic中的数据到文件
 def putOut(self, file_name):
  print(file_name)
  dirfile_f = open(file_name, "w")
  for key, value in self.LineList.items() :
   str_1 = key+" "+str(value)+" \n"
   dirfile_f.write(str_1)
  dirfile_f.close()

  self.max = 0
  self.LineList.clear()
  gc.collect()

 # 扫描统计,比较耗时
 def getTongJi(self, fen_duan):
  all_sum = 0
  file_num = 0
  max_1 = 0
  max_1_file_num = 0
  for key, value in self.LineList.items():
   all_sum = all_sum + value
   file_num = file_num + 1
   if value > 1 :
    max_1 = max_1 + value
    max_1_file_num = max_1_file_num + 1
  print(fen_duan,all_sum,file_num,max_1,max_1_file_num)
  self.LineList.clear()
  self.max = 0
  gc.collect()

 #输出统计内容
 def getTongjiAll(self, num):
  print(num, self.all_max, self.file_num, self.one_max, self.one_file_num)
  self.all_max = 0
  self.file_num = 0
  self.one_max = 0
  self.one_file_num = 0
  self.LineList.clear()
  self.max = 0
  gc.collect() 
   
   
 def GetTowInfo(self, file_name) :
  f_handl = open(file_name, "r")
  while f_handl != None:
   line = f_handl.readline()
   if not line:
    break;
   line = line.replace("\n","")
   line = line.replace("\r","")
   key, value = line.split(' ')
   #print(key, value)
   if self.LineList.get(key) == None:
    self.LineList[key] = int(value)
   else:
    self.LineList[key] = self.LineList[key] + int(value)
   self.max = self.max + 1
  f_handl.close()
  return self.max

 #统计解析后的文件(k v格式)的数据
 def GetModeInf(self,file_name, mode, mode_to):
  f_handl = open(file_name, "r")
  for line in f_handl.readlines():
   if not line:
    break;
   #line = line.replace("\n","")
   #line =line.replace("\r","")
   line = line.strip()
   #print(line)
   key, value= line.split(' ')
   if int(key[len(key)-2], 16) % mode == mode_to:
        #print(key, value)
        if self.LineList.get(key) == None:
                self.LineList[key] = int(value)
      self.file_num = self.file_num + 1
      if int(value) > 1:
       self.one_max = self.one_max + int(value)
       self.one_file_num = self.one_file_num + 1
        else:
      if self.LineList[key] == 1 :
                              self.one_max = self.one_max + int(value) + 1
       self.one_file_num = self.one_file_num + 1
      else :
       self.one_max = self.one_max + int(value) 

                self.LineList[key] = self.LineList[key] + int(value)
   self.all_max = self.all_max + int(value)
 f_handl.close()
 return self.max
 

 def SetOutFileNum(self,out_num):
  self.out_file_num = out_num


 # 当符合输出条件时就将dic中的数据输出到文件
 def MarkOutPutFile(self, max_num):
  if max_num > 10000000:
   out_file = "./1/uzip_txt_"+str(self.out_file_num)
   self.out_file_num = self.out_file_num + 1
   self.putOut(out_file)


 #解析gz压缩文件,存入dic中
 def GetZipFile(self, file_name):
  print("zip",file_name)
  f = gzip.open(file_name, 'rb')
  #for t_line in f.readlines():
  while 1:
   t_line = None
   try:
    t_line = f.readline()
    if not t_line:
     break
   except  Exception, err: 
    print(Exception, ":",err) 
    return 30000000
   #print(t_line)
   t_value = t_line.split(' ')
   #print(t_value[6])
   line = t_value[6]
   #line = line.replace("\n","")
   #line = line.replace("\r","")
   #line = line.replace(" ","")
   line = line.strip()
   md = hashlib.md5()
   md.update(line)
   key = md.hexdigest()
   mark = int(key[len(key)-2], 16)%10
   #self.all_num_array[mark] = self.all_num_array[mark]+1
   #print(key)
                        if self.LineList.get(key) == None:
                                self.LineList[key] = 1
     self.all_num_array[mark] = self.all_num_array[mark]+1
     self.max = self.max + 1
                        else:
                                self.LineList[key] = self.LineList[key] + 1
   #MarkOutPutFile(self.max,None)
  f.close()
  print("array_info:",self.all_num_array)
  return self.max

 

 def GetCountNum(self):
  max_sum_all = 0
  max_one = 0
  for i in range(0, 10):
      max_sum_all = max_sum_all + self.all_num_array[i]
      max_one = max_one + self.all_num_array[i]/20000000
  print("splite",max_sum_all, max_one)
  return max_one

 def GetCountFileNum(self):
  return self.out_file_num

 


dofile=DoFile()
#unzgrep file
def UnZgrepFile(out_file_num, zipfile_s, zipfile_e):
 dofile.SetOutFileNum(out_file_num)
 for i in range(zipfile_s, zipfile_e):
  str_k = "image_"+str(i)+ ".gz" 
  if i < 10:
    str_k = "image_0"+str(i)+".gz" 
  max_num = dofile.GetZipFile(str_k)
  dofile.MarkOutPutFile(max_num)  

 

#count
def CountFile(c_s, c_max, file_num):
 for j in range(c_s, c_max):
  print("count",c_max, j)
  for k in range(1, file_num):
   file_k = "./1/uzip_txt_"+str(k)
   print(file_k)
   dofile.GetModeInf(file_k, c_max, j)      
  dofile.getTongjiAll(j) 


if __name__ == '__main__' :
 #unzgrep file
 UnZgrepFile(1, 0, 24)

 #get count num
 split_num = dofile.GetCountNum()
 out_file_num = dofile.GetCountFileNum()
 
 #count
 CountFile(0, split_num, out_file_num)








你可能感兴趣的:(python)