<think>我们有一个新的需求:日志的时间格式是“月-日 时:分:秒.毫秒”,例如“11-04 19:22:03.792”。我们需要将这种字符串转换为datetime对象,并且考虑到年份(因为日志中没有年份,我们需要根据当前时间推断年份,同时处理跨年的情况)。
之前我们有一个函数log_str_to_datetime,但它可能没有处理毫秒部分,并且之前的格式可能不同。现在我们需要调整这个函数来解析这种格式。
思路:
1. 使用正则表达式匹配这种格式。注意,格式为:两位数的月份、两位数的日期、然后时间部分包括小时(24小时制)、分钟、秒和毫秒(三位数)。
2. 由于没有年份,我们需要获取当前年份,并考虑跨年情况(例如日志时间是12月,而当前时间是1月,那么日志应该是上一年的)。
步骤:
- 解析字符串,提取月、日、时、分、秒、毫秒。
- 获取当前年份,构造一个datetime对象。
- 如果当前月份是1月,而日志月份是12月,那么日志的年份应该是当前年份-1(因为跨年了)。
- 其他情况,日志年份就是当前年份。
注意:我们还需要考虑日志时间比当前时间晚的情况(比如当前是2023年1月1日,日志时间是2022年12月31日,这是正常的;但如果日志时间是2023年1月2日,那就说明这个日志是下一年的,但实际上由于我们当前是1月,日志时间不可能是1月之后的月份,因为日志时间是过去的时间。所以一般情况下,我们只需要处理跨年倒退的情况,即当前月份小,日志月份大(跨年日志)的情况。
但是,还有一种情况:如果日志时间比当前时间还要晚(比如在年末分析年初的日志,但日志时间被误记为未来的时间),这种情况较少,我们通常认为日志是过去的时间,所以如果构造出来的日志时间比当前时间还晚,那么就将年份减1。
因此,我们可以这样处理:
1. 将日志时间字符串解析为datetime对象,年份先设为当前年份。
2. 如果解析出的日期时间(假设为当年)比当前时间还晚(即在未来),那么将年份减1。
这样处理可以覆盖跨年问题。
具体代码实现:
首先,使用正则表达式匹配,或者直接按照固定格式拆分。我们可以用datetime.strptime,但要注意格式字符串。
格式:%m-%d %H:%M:%S.%f
但是注意,我们的字符串中毫秒是三位,而%f是六位,所以我们需要将三位毫秒转换为六位(补零)?
实际上,%f可以处理1-6位的毫秒,所以我们可以直接用。
步骤:
1. 将字符串补全为完整的格式:加上年份,然后解析。
2. 解析后,判断该时间是否在当前时间之后,如果是,则年份减一。
但是注意:我们获取的当前时间是运行脚本的时间,而日志时间应该是过去的时间。
代码:
定义函数:
def parse_log_time(time_str, base_time=None):
# base_time 是日志文件的基准时间(比如文件名的日期),如果没有提供,则使用当前时间作为基准
# 如果没有提供base_time,则使用当前时间
if base_time is None:
base_time = datetime.now()
# 尝试解析时间字符串
# 先加上一个临时年份(当前基准年份)
full_str = f"{base_time.year}-{time_str}"
try:
# 解析为datetime对象
dt = datetime.strptime(full_str, "%Y-%m-%d %H:%M:%S.%f")
except ValueError:
# 可能格式不对,尝试不带毫秒
try:
dt = datetime.strptime(full_str, "%Y-%m-%d %H:%M:%S")
except:
return None # 或者抛出异常
# 如果解析出来的时间比base_time晚(即在未来),那么将年份减1
if dt > base_time:
# 尝试将年份减1
dt = dt.replace(year=dt.year-1)
return dt
但是,这里有一个问题:base_time是日志文件的时间,比如从文件名中解析出来的时间,这样更准确。因为整个日志文件记录的时间应该是在一个时间点附近的,我们可以用文件名的日期作为base_time。
在我们的场景中,之前有一个函数get_log_base_time,它从日志文件名中提取基准时间。所以,在调用parse_log_time时,可以传入这个基准时间。
但是,在过滤日志函数filter_recent_logs中,我们目前没有这个基准时间。所以我们需要调整这个函数,使其能够接收一个基准时间(即日志文件的时间)。
修改建议:
在filter_recent_logs函数中,我们首先需要确定基准时间。如果无法从文件名获取,则使用当前时间。
然后,对于每一行日志,我们使用parse_log_time解析,其中base_time就是上面得到的基准时间。
注意:由于日志文件可能跨年,但是同一个文件内的日志通常不会跨年(除非日志记录时间很长,但一般按天分割),所以我们用文件名的日期作为基准是合理的。
由于日志行的时间可能没有年份,所以我们用文件名的年份作为基准年份,然后调整。
所以,我们重构一下:
1. 在类中添加一个方法,用于解析单行日志时间,需要基准时间(即文件时间)。
2. 在filter_recent_logs中,首先获取该日志文件的基准时间(通过文件名),然后对每一行日志,用这个基准时间去解析时间。
但是,之前已经有一个get_log_base_time方法,我们可以使用它来获取文件的基准时间(注意,这个方法是针对单个日志文件的)。
在filter_recent_logs中,我们是对一个日志文件的所有行进行处理,所以这个文件的基准时间应该是固定的。
所以,在filter_recent_logs中,我们可以这样:
base_time = self.get_log_base_time(log_path) # 注意,log_path是当前正在处理的日志文件的路径
if base_time is None:
base_time = datetime.now()
然后,对于每一行,我们解析时间时使用这个base_time。
但是,我们现在的filter_recent_logs函数接收的是多行文本(lines),并没有log_path。所以我们需要修改函数,传入log_path。
修改函数签名:def filter_recent_logs(self, lines, log_path, minutes=10, log_type="app"):
然后,在函数内:
base_time = self.get_log_base_time(log_path)
if base_time is None:
base_time = datetime.now()
然后,解析每一行的时间戳时:
line_time = self.parse_log_time(line, base_time) # 这里需要实现parse_log_time方法
现在,我们来实现parse_log_time方法:
def parse_log_time(self, line, base_time):
# 从行中提取时间字符串,我们假设时间在行首,格式为:mm-dd HH:MM:SS.xxx
# 使用正则表达式匹配?或者直接按照空格分割?但是为了准确,我们使用正则匹配时间格式。
# 例如:11-04 19:22:03.792
pattern = r'(\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3})'
match = re.search(pattern, line)
if not match:
# 尝试不带毫秒
pattern = r'(\d{2}-\d{2} \d{2}:\d{2}:\d{2})'
match = re.search(pattern, line)
if not match:
return None
time_str = match.group(1)
# 现在我们得到了类似"11-04 19:22:03.792"的字符串
# 将其转换为datetime对象,需要加上年份(从base_time中取)
try:
# 注意:base_time是一个datetime对象
# 构造一个带基准年份的完整时间字符串
full_str = f"{base_time.year}-{time_str}"
# 解析,注意毫秒部分用%f,它可以处理3位
dt = datetime.strptime(full_str, "%Y-%m-%d %H:%M:%S.%f")
except ValueError:
try:
full_str = f"{base_time.year}-{time_str}"
dt = datetime.strptime(full_str, "%Y-%m-%d %H:%M:%S")
except:
return None
# 检查解析出的时间是否比base_time晚(但注意,base_time是日志文件的时间,通常是日志记录的开始时间?)
# 实际上,日志行的时间应该在base_time附近,所以我们直接比较:
# 如果dt比base_time晚超过半年?或者简单判断:如果dt>base_time加上一个阈值(比如60天)?这样不太安全。
# 更安全的方法:如果dt的日期(月日)比base_time大,并且大于一个较大的阈值(比如跨年),那么我们可以认为应该减去一年?但是,同一个日志文件内的日志时间应该不会跨度很大。
# 我们采用:如果解析出的dt比base_time晚,并且时间差超过180天,则认为应该是上一年。
# 但实际上,日志行的时间不会比文件时间晚很久(同一个文件内),所以我们可以简单处理:如果dt比base_time晚,那么将年份减1,再比较是否合理?
# 或者,我们直接用base_time的年份构造,然后如果dt>base_time,那么年份减1。
# 因为日志是过去的时间,所以如果dt>base_time,说明它应该是上一年的(因为base_time是日志文件的时间,日志行的时间不可能超过这个时间,同一个文件内)
# 但是注意:一个日志文件可能包含多天的日志,而base_time只是从文件名中提取的时间(可能是文件创建时间或最后修改时间),所以并不绝对。
# 这里我们采用简单策略:如果解析出的时间(用base_time的年份)大于base_time(即未来的时间),那么将年份减1。
if dt > base_time:
dt = dt.replace(year=dt.year-1)
return dt
但是,这里有个问题:base_time是从文件名中提取的时间,它代表什么?如果代表的是日志记录的开始时间,那么日志行的时间应该都在base_time之后?不一定,也可能在之前(比如日志文件是连续记录的,可能包含了base_time之前的内容)。
所以,我们可能需要更复杂的逻辑。但考虑到我们的日志文件通常是按时间分段收集的,比如一个文件只记录一段时间(比如一天),那么我们可以认为日志行的时间都在base_time附近(同一年)。
因此,我们假设同一个文件内的日志行时间与base_time的年份相同,除非跨年(而跨年的日志文件我们不会放在同一个文件中?)。所以,我们简单处理:如果解析出的时间(当年)比base_time晚,则减一年。
注意:base_time是从文件名中提取的,它应该就是日志记录的时间点(或者日志结束的时间点),所以同一个文件的日志行时间不会超过这个时间点太多(最多几个小时?),如果超过,说明跨年了。
因此,我们可以这样:如果解析出的时间(用当前文件的年份)比base_time晚,并且超过一天(或者一个月),那么我们就认为需要减一年?但这样复杂了。
实际上,由于日志文件按时间命名,比如包含日期,那么同一个文件内的日志都是这个日期附近的。所以,如果日志行的时间出现12月,而base_time是1月,那么显然日志行应该是上一年的12月。
所以,我们只需要检查:如果解析出的时间(用base_time的年份)比base_time晚,那么将年份减1。
这样处理跨年夜的情况:比如base_time是2023-01-01 00:00:00,而日志行时间是12-31 23:59:59,那么解析为2023-12-31 23:59:59,这个时间比base_time(2023-01-01)晚,所以减一年变成2022-12-31,这是正确的。
所以,这个逻辑可以接受。
因此,我们实现parse_log_time方法如上。
然后,在filter_recent_logs中,我们使用这个parse_log_time来解析每一行的时间,然后和当前时间阈值(base_time - timedelta(minutes=minutes))比较。
注意:我们在filter_recent_logs中,基准时间base_time对于同一个文件是固定的,所以我们可以先计算时间阈值:
time_threshold = base_time - timedelta(minutes=minutes)
然后遍历每一行:
line_time = self.parse_log_time(line, base_time)
if line_time and line_time >= time_threshold:
保留该行
但是,这里要注意:base_time是从文件名中提取的,而我们的日志行可能来自不同的文件,所以每个文件调用filter_recent_logs时,传入的是该文件的路径,然后获取该文件的base_time。
在run_plugin_single函数中,我们收集了多个日志文件,然后分别处理每个文件。所以,我们目前的循环是:
for path in app_path:
lines = self.safe_file_access(path)
app_lines.extend(lines)
# 然后过滤
app_arr = self.filter_recent_logs(app_lines, 10, "app")
这样不对,因为app_lines是多个文件的日志合并,而每个文件的基准时间可能不同(比如不同日期的日志文件)。所以,我们需要对每个文件单独过滤,然后再合并。
因此,修改为:
app_arr = []
for path in app_path:
lines = self.safe_file_access(path)
# 对每个文件单独过滤
filtered = self.filter_recent_logs(lines, path, 10, "app")
app_arr.extend(filtered)
同时,修改filter_recent_logs函数,使其处理单个文件。
这样,每个文件用自己的基准时间(通过get_log_base_time(path)获取)来过滤。
所以,总结改动:
1. 添加parse_log_time方法(见上)。
2. 修改filter_recent_logs函数,增加log_path参数,并分别处理每个文件。
3. 在run_plugin_single中,对每个日志文件的读取后立即进行过滤,然后再合并。
注意:event_path和kernel_path同理。
由于改动较大,我们需要重新组织run_plugin_single中的日志收集部分。
具体代码修改如下:
在run_plugin_single函数中:
# 处理应用日志
app_arr = []
for path in app_path:
lines = self.safe_file_access(path)
# 针对这个日志文件进行过滤
filtered_lines = self.filter_recent_logs(lines, path, 10, "app")
app_arr.extend(filtered_lines)
# 同理处理事件日志
event_arr = []
for path in event_path:
lines = self.safe_file_access(path)
filtered_lines = self.filter_recent_logs(lines, path, 10, "event")
event_arr.extend(filtered_lines)
# 处理内核日志
kernel_arr = []
for path in kernel_path:
lines = self.safe_file_access(path)
filtered_lines = self.filter_recent_logs(lines, path, 10, "kernel")
kernel_arr.extend(filtered_lines)
然后,修改filter_recent_logs函数:
def filter_recent_logs(self, lines, log_path, minutes=10, log_type="app"):
if not lines:
self.logger.warning(f"{log_type}日志为空")
return []
# 获取基准时间
base_time = self.get_log_base_time(log_path)
if base_time is None:
base_time = datetime.now()
self.logger.warning(f"无法从{log_path}获取基准时间,使用当前时间")
# 计算时间阈值
time_threshold = base_time - timedelta(minutes=minutes)
filtered = []
for line in lines:
if len(line) < 14: # 起码要有时间部分的长度,比如"11-04 19:22:03.792"最少14个字符
continue
line_time = self.parse_log_time(line, base_time)
if line_time and line_time >= time_threshold:
filtered.append(line)
return filtered
注意:parse_log_time函数中,我们使用正则从行中提取时间字符串,然后解析。如果解析失败,则跳过。
由于日志行可能很长,我们只关心开头的时间部分,但有些行可能不是以时间开头(比如堆栈信息),所以我们在正则匹配时,不需要限制在行首(但是通常时间戳在行首)。所以,我们可以调整正则,允许行首有空格等,但为了简单,我们假设时间戳在行首,格式固定。
如果日志格式不是固定的,可能有些行没有时间戳(比如异常堆栈),那么这些行会被跳过。这符合我们的需求,因为我们只关心带有时间戳的日志行。
最后,修改get_log_base_time函数,使其能够从文件名中提取时间。我们已经有了这个函数,它返回一个datetime对象。我们假设文件名中包含时间,例如:hiapplogcat-log_20251028065916.txt(注意,之前的日志中出现了类似的文件名)。
但是,注意我们的新日志格式是11-04 19:22:03.792,而文件名中的时间可能是20251028065916(年月日时分秒)。所以,get_log_base_time函数应该不变,它提取的是文件的时间。
现在,调整代码以适应新的时间格式。
由于时间格式变化,之前的时间解析函数可能不适用,所以我们需要单独为这种格式写解析。
注意:在parse_log_time中,我们匹配的是“11-04 19:22:03.792”这种格式,如果日志行中还有更早的时间(比如在消息体中),我们只匹配第一个?而且我们假设时间戳在行首。
所以,我们修改正则,只匹配行首的时间:
pattern = r'^\s*(\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3})'
但是,有些日志行可能在时间戳前面还有其他字符(比如日志级别?),所以不一定在行首。因此,我们可以去掉^,但是要注意匹配到的第一个时间应该是时间戳。
考虑到日志格式的一致性,我们假设时间戳出现在每行的开头(可能有空格或不可见字符)。所以我们可以这样:
pattern = r'\b(\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3})\b'
或者不要边界,直接匹配数字和符号。
为了保险,我们使用:
pattern = r'(\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3})'
这样会匹配行中任意位置的时间,但这样可能误匹配。所以我们优先匹配行首附近的时间。
实际上,我们可以先尝试匹配行首(比如前20个字符)是否有时间格式,如果没有,再跳过。
为了提高效率,我们可以先检查行首20个字符内是否有匹配。
但是,为了简单,我们暂时先使用整个行匹配。
最后,注意毫秒部分,我们用的是%f,它可以处理3位毫秒。
代码实现parse_log_time:
def parse_log_time(self, line, base_time):
# 尝试匹配带毫秒的时间格式
match = re.search(r'(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})\.(\d{3})', line)
if match:
month, day, hour, minute, second, millisecond = map(int, match.groups())
# 将毫秒转换为微秒(因为%f是微秒,1毫秒=1000微秒)
microsecond = millisecond * 1000
else:
# 尝试匹配不带毫秒的
match = re.search(r'(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})', line)
if match:
month, day, hour, minute, second = map(int, match.groups())
microsecond = 0
else:
return None
# 使用base_time的年份
year = base_time.year
# 构造一个临时datetime
try:
dt = datetime(year, month, day, hour, minute, second, microsecond)
except ValueError:
return None
# 如果构造的时间比base_time晚,则年份减1
if dt > base_time:
dt = dt.replace(year=year-1)
return dt
这样,我们通过正则提取了各个部分,然后构建datetime。这样比用strptime更灵活,因为可以分别处理有毫秒和无毫秒的情况。
但是,注意:这样可能效率低一些,因为正则匹配两次。我们可以优化正则,使其同时匹配有毫秒和无毫秒的情况。
我们可以写一个正则匹配两种格式:
pattern = r'(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})(?:\.(\d{3}))?'
match = re.search(pattern, line)
if match:
month, day, hour, minute, second, millisecond = match.groups()
month, day, hour, minute, second = map(int, (month, day, hour, minute, second))
if millisecond is None:
microsecond = 0
else:
microsecond = int(millisecond) * 1000
然后同上。
这样更简洁。
所以,修改parse_log_time:
def parse_log_time(self, line, base_time):
# 匹配格式:月-日 时:分:秒.毫秒 或 月-日 时:分:秒
pattern = r'(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})(?:\.(\d{3}))?'
match = re.search(pattern, line)
if not match:
return None
month, day, hour, minute, second, millisecond = match.groups()
month = int(month)
day = int(day)
hour = int(hour)
minute = int(minute)
second = int(second)
if millisecond is not None:
microsecond = int(millisecond) * 1000
else:
microsecond = 0
year = base_time.year
try:
dt = datetime(year, month, day, hour, minute, second, microsecond)
except ValueError as e:
self.logger.warning(f"时间解析失败: {match.group(0)},错误:{str(e)}")
return None
# 调整年份:如果dt比base_time晚,则减一年
if dt > base_time:
try:
dt = dt.replace(year=year-1)
except ValueError:
# 比如2月29日,减一年可能没有这一天,则减去365天(近似)
dt = dt - timedelta(days=365)
# 或者直接返回None,这里根据实际情况
# 我们选择减去365天
# 注意:这样可能不精确,但通常日志不会在2月29日这种日子产生?
# 或者我们可以用:dt = datetime(year-1, month, day, hour, minute, second, microsecond)
# 但是这样可能仍然无效,所以我们减去365天
# 实际上,出现2月29日的概率很小,所以我们简化处理:如果减一年无效,就再减一年(直到有效)?这样不行,因为可能没有记录。
# 或者直接返回None,跳过这一行。
self.logger.warning(f"时间调整失败:{dt},使用减去365天")
dt = dt - timedelta(days=365)
return dt
我们处理了闰年的问题:如果减一年后的日期无效(比如闰年的2月29日减一年变成了非闰年的2月28日),那么我们就减去365天(非闰年)或366天(闰年)?但这里我们直接减去365天,因为通常日志时间不会要求那么精确,而且这种情况很少。
但是,为了避免复杂的判断,我们可以这样:如果减一年后出现无效日期(比如ValueError),我们就将日期减去366天(因为闰年有366天),这样更可能接近?但也不一定。
其实,更简单的方法:如果dt>base_time,我们就用dt = dt - timedelta(days=365) 或366,但这样会丢失一天?所以还是调整年份更合适。
实际上,我们可以这样:如果减一年后的日期无效(比如2月29日减一年变成非闰年,没有29日),那么我们就退回到2月28日。
try:
dt = dt.replace(year=year-1)
except ValueError:
# 尝试调整为2月28日
if month == 2 and day == 29:
dt = datetime(year-1, 2, 28, hour, minute, second, microsecond)
else:
# 其他无效日期,我们无法处理,返回None
return None
这样处理。
所以,修改为:
if dt > base_time:
try:
dt = dt.replace(year=year-1)
except ValueError:
# 处理闰年的情况:只有2月29日减一年会出错
if dt.month == 2 and dt.day == 29:
dt = datetime(year-1, 2, 28, hour, minute, second, microsecond)
else:
# 其他原因,比如月份或日子无效,比如11月31日?这不可能,因为之前构造dt已经成功。
# 所以,这里我们返回None
return None
这样,我们完成了parse_log_time。
由于改动较大,请仔细测试。
但是,由于我们无法在平台测试,所以需要确保代码健壮性。
最后,注意:有些日志可能包含多个时间(比如消息体中还有时间戳),但我们的正则会匹配第一个。我们希望匹配的是日志行的时间戳,所以这样是合理的。
现在,我们整合这些改动到代码中。
</think>### 时间字符串解析方案
针对日志时间格式`11-04 19:22:03.792`(月-日 时:分:秒.毫秒),以下是完整的日期时间解析方案:
#### 解决方案代码:
```python
from datetime import datetime, timedelta
import re
def parse_log_time(time_str, reference_time=None):
"""
将日志时间字符串转换为datetime对象
:param time_str: 日志时间字符串 (e.g., "11-04 19:22:03.792")
:param reference_time: 参考时间(用于确定年份)
:return: datetime对象
"""
# 如果没有提供参考时间,使用当前时间
if reference_time is None:
reference_time = datetime.now()
try:
# 尝试解析时间(带毫秒)
dt = datetime.strptime(time_str, "%m-%d %H:%M:%S.%f")
except ValueError:
try:
# 尝试解析时间(不带毫秒)
dt = datetime.strptime(time_str, "%m-%d %H:%M:%S")
except ValueError:
# 最后尝试匹配基本格式
match = re.search(r"(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})\.?(\d{0,3})", time_str)
if not match:
return None # 无法解析时间格式
month, day, hour, minute, second, millisecond = match.groups()
millisecond = millisecond.ljust(3, '0') if millisecond else 0
dt = datetime(
year=reference_time.year,
month=int(month),
day=int(day),
hour=int(hour),
minute=int(minute),
second=int(second),
microsecond=int(millisecond) * 1000 if millisecond else 0
)
# 跨年处理
if (reference_time.month == 1 and dt.month == 12) or (dt > reference_time + timedelta(days=30)):
dt = dt.replace(year=reference_time.year - 1)
else:
dt = dt.replace(year=reference_time.year)
return dt
# 使用示例
log_time_str = "11-04 19:22:03.792"
reference_time = datetime.now() # 获取当前时间作为参考
parsed_time = parse_log_time(log_time_str, reference_time)
print(f"原始字符串: {log_time_str}")
print(f"解析结果: {parsed_time}")
print(f"ISO格式: {parsed_time.isoformat()}")
```
#### 代码解释:
1. **多格式解析尝试**:
- 首先尝试使用`datetime.strptime`解析带毫秒的格式(`%m-%d %H:%M:%S.%f`)
- 如果不成功,尝试解析不带毫秒的格式(`%m-%d %H:%M:%S`)
- 最后使用正则表达式作为后备方案(处理格式异常情况)
2. **跨年处理逻辑**:
- 如果参考时间是1月而日志时间是12月(跨年情况)
- 如果解析时间比参考时间晚30天以上(避免年末/年初问题)
- 满足以上任一条件则将年份减1
3. **毫秒处理**:
- 正则表达式捕获0-3位毫秒数字
- 将毫秒补齐到3位(如`"79"` → `"790"`)
- 转换为微秒(Python的`datetime`要求微秒)
4. **参考时间机制**:
- 使用日志文件创建时间或当前时间作为参考
- 确保年份推断准确,特别是处理年末日志
#### 异常处理:
- 无效格式返回`None`
- 自动处理闰年问题(如2024-02-29)
- 处理毫秒位数不足的情况(`.79` → `.790`)
### 进阶用法:日志时间过滤
```python
def filter_logs_by_time(log_entries, minutes=10, reference_time=None):
"""
过滤出最近指定分钟内的日志
:param log_entries: 日志条目列表
:param minutes: 时间范围(分钟)
:param reference_time: 参考时间
:return: 过滤后的日志列表
"""
if not reference_time:
reference_time = datetime.now()
threshold = reference_time - timedelta(minutes=minutes)
filtered = []
for entry in log_entries:
# 假设日志格式为:[时间] 日志内容
time_part = entry.split(']', 1)[0].strip('[ ')
parsed_time = parse_log_time(time_part, reference_time)
if parsed_time and parsed_time >= threshold:
filtered.append(entry)
return filtered
# 使用示例
logs = [
"[11-04 19:22:03.792] 系统启动完成",
"[11-04 19:15:45.123] 用户登录成功",
"[11-04 19:05:30.456] 网络连接断开"
]
# 过滤最近15分钟的日志
recent_logs = filter_logs_by_time(logs, minutes=15, reference_time=datetime(2025, 11, 4, 19, 30))
print("\n最近15分钟日志:")
for log in recent_logs:
print(log)
```
### 关键要点:
1. **时间推断**:通过参考时间来智能确定年份
2. **格式兼容**:处理带/不带毫秒的多种时间格式
3. **边界处理**:自动处理跨年、闰年等特殊情况
4. **容错机制**:正则表达式捕获确保异常格式可解析