ついでにファイルオープン時に致命的なバグがあったため、合わせて修正。
from collections import OrderedDict from datetime import datetime, timedelta from functools import reduce from os.path import exists from re import match, search, split from sys import argv from shutil import rmtree from os import makedirs MAX_FILE_HANDLE = 500 WORK_DIR = 'work' if len(argv) != 3: print("\nUsage:\npython", argv[0], "logfile yyyy/mm/dd") exit() rmtree(WORK_DIR, True) if not exists(WORK_DIR): makedirs(WORK_DIR) mydate = datetime.strptime(argv[2], '%Y/%m/%d') mydateStr = mydate.strftime('%Y/%m/%d ') try: f = open(argv[1]) mem = open(WORK_DIR + "/mem.log", 'w') mem.write("time,mem av,mem used,mem free,mem shard,mem buff,mem actv, mem in_d,swap av,swap used,swap free,swap cached\n") processes = OrderedDict() maxtime = "" for line in f: line = line.strip() if match(r"^\d\d:\d\d:\d\d", line): if line[0:2] == "00" and timestamp[0:2] != "00": mydate = mydate + timedelta(1) mydateStr = mydate.strftime('%Y/%m/%d ') timestamp = line[0:8] elif match(r"^Mem", line): data = split(r"\s+", line)[1:9:2] elif match(r"^\d+k", line): data = data + split(r"\s+", line)[0:5:2] elif match(r"^Swap:", line): data = data + split(r"\s+", line)[1:8:2] prefix = mydateStr + timestamp result = reduce((lambda x,y: x + ',' + y.rstrip('k')), data, prefix) mem.write(result + "\n") elif search(r"java", line): data = split(r"\s+", line) pid = data[0] process = processes.get(pid) if process == None: while len(processes) >= MAX_FILE_HANDLE: processes.popitem(last=False)[1].close() filename = WORK_DIR + "/Pid-" + pid + ".log" if exists(filename): process = open(filename, 'a') else: process = open(filename, 'w') process.write("time,PID,USER,PRI,NI,SIZE,RSS,SHARE,STAT,%CPU,%MEM,TIME,CPU,COMMAND\n") else: del processes[pid] processes[pid] = process prefix = mydateStr + timestamp result = reduce((lambda x,y: x + ',' + y), data, prefix) process.write(result + "\n") if data[10] > maxtime: maxtime = data[10] maxpid = pid print("maxpid:", maxpid) finally: if f != None: f.close() if mem != None: mem.close() map(lambda x: x.close(), processes)
キモは51~63行あたり。
OrderedDictは名が示すとおり、(key, value)セットの追加順序を記憶する辞書型である。ただし、一度追加した(key, value)は、valueの更新が行われても順序を入れ替えない。今回はLRUを実装しなければならないため、既に追加されている(key, value)にアクセスした場合は必ず一度削除してから再度追加するようにしている。
さらに、もし管理しているファイルハンドルがMAX_FILE_HANDLEを超えそうになったら、一番使われていないファイルハンドルを取得しクローズ処理を行なっている(54行目)。popitemメソッドは、引数がtrueの場合はLIFO、falseの場合はFIFOとして動作する。
前回バグっていたのを直したのは56~60行目あたり。前回はopen関数の第2引数を常に'w'と指定していたため、ファイルハンドルが増えたために一旦クローズしてしまったファイルについて、再度書きこみを行おうとした場合にクローズ前までのデータを消してしまっていた。オープン時にファイルが存在する場合は追記('a')するように修正。
150MB程度のログファイルの処理に、僕の非常に非力なマシン(PentiumM 1.2GHz 752MB RAM)で約2分。生成されるファイル数は4,000あまりというところ。
時間があったら、次はもう少しモジュール化してみたい。