首次提交
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
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
|
||||
Reference in New Issue
Block a user