PHP/Python中的多行日志解析服务时间
在PHP和/或Python中,解析需要上下文知识的多行日志文件的最佳方法是什么?
例如:
Date Time ID Call
1/1/10 00:00:00 1234 Start
1/1/10 00:00:01 1234 ServiceCall A Starts
1/1/10 00:00:05 1234 ServiceCall B Starts
1/1/10 00:00:06 1234 ServiceCall A Finishes
1/1/10 00:00:09 1234 ServiceCall B Finishes
1/1/10 00:00:10 1234 Stop
每一行日志都有一个独特的ID,用来绑定到一个会话,但连续的几行不一定来自同一个会话。
最终的目标是找出每个交易花了多长时间,以及每个子交易花了多长时间。
如果有现成的库可以使用,那就太好了。
3 个回答
这是我之前写的一个日志解析器的变种,专门为你的日志格式定制的。(这个方法大致上和Jim Dennis的描述很相似,不过我用了一个默认字典来把每个会话的所有条目都放到一个列表里。)
from pyparsing import Suppress,Word,nums,restOfLine
from datetime import datetime
from collections import defaultdict
def convertToDateTime(tokens):
month,day,year,hh,mm,ss = tokens
return datetime(year+2000, month, day, hh,mm,ss)
# define building blocks for parsing and processing log file entries
SLASH,COLON = map(Suppress,"/:")
integer = Word(nums).setParseAction(lambda t:int(t[0]))
date = integer + (SLASH + integer)*2
time = integer + (COLON + integer)*2
timestamp = date + time
timestamp.setParseAction(convertToDateTime)
# define format of a single line in the log file
logEntry = timestamp("timestamp") + integer("sessionid") + restOfLine("descr")
# summarize calls into single data structure
calls = defaultdict(list)
for logline in log:
entry = logEntry.parseString(logline)
calls[entry.sessionid].append(entry)
# first pass to find start/end time for each call
for sessionid in sorted(calls):
calldata = calls[sessionid]
print sessionid, calldata[-1].timestamp - calldata[0].timestamp
对于你的数据,这段代码会输出:
1234 0:00:10
你可以用类似的方法来处理每个会话的条目列表,从中提取出子事务。
我首先想到的是,每当我的解析器遇到开始模式时,就创建一个新对象,并给它一个新的键。我假设在你的例子中,1234是一个键,这样所有需要关联在一起的日志行都可以映射到一个“东西”(对象)的状态上。
所以你会看到一个模式,开始跟踪其中一个对象,每次看到与它相关的日志条目时,你就调用对应事件类型的方法(状态变化),这些后续的日志行代表的就是这些事件。
根据你的例子,这些“日志状态”对象(暂时没有更合适的词)可能会包含一个列表或字典(或者其他容器),用于存储每个服务调用(我预计这会是另一类对象)。
所以整体设计就是一个解析器/调度器,它读取日志,如果日志项与某个现有对象(键)相关联,那么这个项就会被分发到该对象,接着它可以进一步创建自己的(服务调用或其他)对象,或者将事件分发给这些对象,或者抛出异常,或者调用回调,或者根据需要调用其他函数。
你可能还需要有一个集合或最终处理程序,当停止事件被分发给你的日志对象时,可以被调用。
我猜你还想支持某种状态报告方法,这样应用程序就可以列举所有活跃的(未收集的)对象,以响应某些信号或命令(也许是通过解析器/调度器执行的非阻塞检查)。
我想到两种不同的方法来处理这个问题。
1) 你可以使用一种叫做有限状态机的东西,逐行处理文件。当你遇到一个开始行时,记录下时间。当你遇到一个相同ID的结束行时,计算时间差并报告结果。
2) 你可以用PHP的兼容Perl的正则表达式,加上m修饰符,来匹配每一组开始和结束行之间的所有文本,然后只需要查看每个匹配结果的第一行和最后一行。
在这两种情况下,我都会确认ID是否匹配,以防止匹配到不同的组。