4729698049
git-subtree-dir: paste-framework git-subtree-split: 34e8684c4bc3cebbe177509f42ab4ef5b5425a7a
165 lines
5.4 KiB
Python
165 lines
5.4 KiB
Python
import os
|
|
from typing import Optional
|
|
|
|
from paste.core import config
|
|
|
|
|
|
class TailRead:
|
|
"""
|
|
文件逆向读取器。
|
|
主要针对读取日志文件。当遇到大日志文件时,需要从后向前读取,这样读取的速度更快。
|
|
当日志文件动态增加时,再正向读取,此时仅读取差异内容,实现小数据量交互。
|
|
"""
|
|
|
|
@classmethod
|
|
def logReader(cls, log_fn: str = None):
|
|
"""
|
|
取得配置文件设置的日志文件读取器。
|
|
|
|
:param log_fn: 日志文件名
|
|
:return: 默认日志文件读取器
|
|
"""
|
|
if log_fn is None:
|
|
log_fn = config.get_config('logger.filename')
|
|
return TailRead(fn=log_fn)
|
|
|
|
def __init__(self, fn: str):
|
|
self.file_name = fn
|
|
"""
|
|
要读取的文件名。
|
|
"""
|
|
|
|
self.file_io = open(self.file_name, 'rb')
|
|
"""
|
|
文件 IO 对象。
|
|
"""
|
|
|
|
self.current_position: Optional[int] = None
|
|
"""
|
|
当前读取点位置。
|
|
"""
|
|
|
|
_f_size = self.size()
|
|
if _f_size > 1:
|
|
# 移动到文件末尾
|
|
self.file_io.seek(_f_size - 1)
|
|
else:
|
|
self.file_io.seek(0)
|
|
|
|
def size(self):
|
|
"""
|
|
取得文件大小。
|
|
|
|
:return: 文件大小
|
|
"""
|
|
return os.path.getsize(self.file_name)
|
|
|
|
def readTail(self, lines: int = 100):
|
|
"""
|
|
从文件中,逆向读取 lines 行。读取结束后,将读取定位移动到所有读取到的字节的最后。
|
|
|
|
:param lines: 读取的行数
|
|
:return: 读取到的数据,读取完成点
|
|
"""
|
|
_buffer: bytes = b''
|
|
_c_pos = self.file_io.tell()
|
|
|
|
# 从当前位置读取一位,判断是否是回车
|
|
# 若是,则增加一行,确保读取足够的行数
|
|
_byte = self.file_io.read(1)
|
|
if _byte == b'\n':
|
|
lines += 1
|
|
# 重新回到原始位置
|
|
self.file_io.seek(_c_pos)
|
|
|
|
_r_pos = _c_pos
|
|
while lines > 0:
|
|
# 读取一个字节
|
|
_byte = self.file_io.read(1)
|
|
if _byte == b'':
|
|
# 无数据,退出
|
|
break
|
|
if _byte == b'\n':
|
|
# 减少行数
|
|
lines -= 1
|
|
# 逆向前移
|
|
_r_pos -= 1
|
|
if _r_pos <= 0:
|
|
# 超出第一位时,退出
|
|
break
|
|
self.file_io.seek(_r_pos)
|
|
# 加入缓存
|
|
_buffer = _byte + _buffer
|
|
|
|
# 扣除首字节回车符号
|
|
if _buffer[0:1] == b'\n':
|
|
_buffer = _buffer[1:]
|
|
|
|
# 第一位已经读取,因此正向移动一位
|
|
self.current_position = _c_pos + 1
|
|
self.file_io.seek(self.current_position)
|
|
return _buffer, self.current_position
|
|
|
|
def readLines(self, lines: int = 100, crt_pos: int = None):
|
|
"""
|
|
读取文件数据,默认读取 100 行。当不传入 crt_pos 时逆向读取,传入时正向读取。
|
|
具有动态方向,确保第一次是最大量读取,以后每次都是增量读取,减少传递的数据量。
|
|
|
|
:param lines: 要读取的行数
|
|
:param crt_pos: 当前读取位置
|
|
:return:
|
|
"""
|
|
_buffer: bytes = b''
|
|
if crt_pos is None:
|
|
# 参数 cur_pos 为 None 时,逆向读取
|
|
self.file_io.seek(self.size()-1)
|
|
_buffer, crt_pos = self.readTail(lines)
|
|
else:
|
|
# 参数 cur_pos 有值时,正向读取
|
|
self.file_io.seek(crt_pos)
|
|
while lines:
|
|
_bytes = self.file_io.readline()
|
|
if _bytes == b'':
|
|
# 无数据,退出
|
|
break
|
|
else:
|
|
# 减少行数
|
|
lines -= 1
|
|
# 加入缓存
|
|
_buffer += _bytes
|
|
crt_pos = self.file_io.tell()
|
|
|
|
self.current_position = crt_pos
|
|
return _buffer, self.current_position
|
|
|
|
def read(self, lines: int = 200, crt_pos: int = None):
|
|
"""
|
|
读取文件数据。注意::
|
|
|
|
1、首次读取时 crt_pos 应为 None 此时逆向读取,返回读取到的数据流和读取点位置。
|
|
2、当有 crt_pos 参数时,先检查文件是否发生了变化,若文件变大,则正向读取增量部分,若文件变小则置空。
|
|
3、若有 crt_pos 且文件没有发生变化,则返回空字节流,读取位置不变。
|
|
|
|
:param lines: 要读取的最大行数,默认 200 行
|
|
:param crt_pos: 当前读取位置,为 None 时逆向读取,否则正向读取
|
|
:return: 读取到的字节流
|
|
"""
|
|
_buffer = b''
|
|
if crt_pos is None:
|
|
# 参数 cur_pos 为 None 时,逆向读取
|
|
_buffer, crt_pos = self.readLines(lines, crt_pos=crt_pos)
|
|
else:
|
|
# 参数 cur_pos 有值时
|
|
# 检查文件是否发生了变化
|
|
_log_size = self.size()
|
|
if _log_size > crt_pos + 1:
|
|
# 内容增加,继续正向读取
|
|
_buffer, crt_pos = self.readLines(lines, crt_pos=crt_pos)
|
|
elif _log_size < crt_pos:
|
|
# 内容减少,置空读取位置
|
|
# 置空后,再次调用本函数,执行逆向读取
|
|
crt_pos = None
|
|
|
|
self.current_position = crt_pos
|
|
return _buffer, self.current_position
|