Python切割超大日志文件、保留文件最后几行

前言

关于日志这个东西的存在,主要是为了记录发生的事情,编程的过程中也常常用到,记得我们在刚刚学习编程的时候,常常会出现程序错误,这时候就需要输出一下,其实这个输出也是日志的一种体现,随着编程水平的提升,各种调试工具和方法渐渐进入我们的视线,但是输出一下这种方法却一直被使用,特别是一些偶发性问题,调试工具很难捕捉到他们,这时候往往需要将中间过程输出到日志文件中,这些日志文件就是我们分析问题的基础。

随着程序规模的渐渐扩大,出现问题时需要打印的日志也越来越多,最近就出现这样一个情况,游戏程序总是莫名的崩溃,看代码找不到问题的原因,所以采用了打印日志文件的方法,有时候大约跑半天就能出现崩溃,日志文件大概600M,Windows系统自带的记事本很难打开,但是使用Notepad++等几秒钟是可以看到内容的。

比较变态的是最近一次跑了2天才崩溃,导出日志文件发现大概有3G,这次使用Notepad++打开时也卡死了,使用sublime打开时进度卡在了80%左右,据说非常强大的GVim打开文件时也毫无反应了,这就尴尬了,崩溃之前的日志内容就在文件中,可是我们却看不见。

问题出现

问题很明显摆在这了,文件由于太大无法看到其中的内容,得想个办法。很直接的一个想法就进入了脑海,把文件拆开成几份,这样每个文件缩小了就可以看到了啊,所以我们找到了一个解决问题的办法,接下来使用Python来简单写一下切分文件。

分割日志文件

按照文件大小分割

分割文件的规则需要先确定一下,可以很简单的按照文件大小分割,一个源文件大小为10M的日志文件,可以切分成10个大小为1M的日志文件,分割的大小不用太绝对,每一份近似相等就可以,整体思路就是先获得源文件的大小,然后计算出分割结束每个文件的大小,接着不断从源文件中读内容,往目标文件中写内容,达到之前计算的字节大小时,再生成新的目标文件,简单代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import os

DATA_LEN_PER_READ = 1024 * 1024

def split_file_by_size(file_name, parts=3):
file_size = os.path.getsize(file_name)
per_file_size = file_size//parts
file_size_list = []

for x in range(parts-1):
file_size_list.append(per_file_size)
file_size_list.append(file_size-per_file_size*(parts-1))

output_file, ext = os.path.splitext(file_name)
with open(file_name, 'rb') as rfile:
for n in range(parts):
with open('{0}_part{1}{2}'.format(output_file, n+1, ext), 'wb') as wfile:
read_size = 0
while read_size < file_size_list[n]:
want_read = min(DATA_LEN_PER_READ, file_size_list[n] - read_size)
wfile.write(rfile.read(want_read))
read_size += want_read

按照文件行数分割

以上按照文件大小分割的日志文件有一点小问题,不能保证行是完整的,当遇到汉字这种占用多字节的字符,甚至都不能保证汉字是完整的,所以我们可以换一个思路,尝试使用按照行数分割,整体思路就是先获得文件的行数,然后计算出分割结束每个文件的行数,接着不断从源文件中一行行读内容,往目标文件中一行行写内容,达到之前计算的文件行数时,再生成新的目标文件,简单代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import os

def get_file_line_count(file_name):
line_count = 0
for index, line in enumerate(open(file_name, 'r')):
line_count += 1
return line_count

def split_file_by_line(file_name, parts=3):
total_line = get_file_line_count(file_name)
per_file_line = total_line//parts
file_line_list = []

for x in range(parts-1):
file_line_list.append(per_file_line)
file_line_list.append(total_line-per_file_line*(parts-1))

output_file, ext = os.path.splitext(file_name)
with open(file_name, 'rb') as rfile:
for n in range(parts):
with open('{0}_part{1}{2}'.format(output_file, n+1, ext), 'wb') as wfile:
read_line = 0
while read_line < file_line_list[n]:
wfile.write(rfile.readline())
read_line += 1

获取日志文件尾部内容

对于分析奔溃这类问题,出现问题的日志往往就在最后几行,所以没有必要非得打开整个日志文件,也不一定需要将整个文件分割成几部分,只需将日志文件的最后一部分读出来写到新的文件中供我们分析就可以了,这时候开头几个字符的不完整也是可以接受的,所以没必要按行读取,只需按照经验,从尾部截取指定字节大小的内容就可以了,比如我们的日志,最后有用的部分也就10M左右,一般的文本文件就都能打开了。

代码思路就是先打开文件,然后将读指针定位到尾部往前10M左右,然后读取所有内容保存到新的文件中,简单的代码示例如下:

1
2
3
4
5
6
7
8
import os

def tailslice(file_name, data_size= 1024):
output_file, ext = os.path.splitext(file_name)
with open(file_name, 'rb') as rfile:
rfile.seek(-data_size, 2)
with open('{0}_tail{1}'.format(output_file, ext), 'wb') as wfile:
wfile.write(rfile.read())

总结

  1. 有时候要问题需要变通一下,如果一个文件大到打不开的地步,我们就把它分割成可以接受的范围
  2. 当文件中的内容只有一部分可用时,我们完全可以不分割,直接取出可用部分即可。

代码传送门

  1. 分割超大日志文件
  2. 获取日志文件尾部内容
Albert Shi wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客