我正在尝试找到一种以功能方式读取大型日志文件并将其拆分为事件的方法。我有一种必要的方式(例如,使用可变状态,没有可组合)。我看了Best way to read lines in groups in a flat file - 遗憾的是,我的文件没有像END这样的定义分隔符。该解决方案也消耗END行。
我的文件看起来像这样
Nov 28, 2015 2:30:47 PM CST Info Security BEA-090905 Disabling CryptoJ JC ...
Nov 28, 2015 2:30:47 PM CST Info Security BEA-090906 Changing the default .....
2015-11-28 14:33:08,320:ERROR:[ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.Default (self-tuning)': [1448742788318]<?xml version="1.0" encoding="UTF-8"?>
<Errors>
<Error ErrorCode="INVERR01"
ErrorDescription="SKU information missing" ErrorUniqueExceptionId="10.7.44.4914487427882870000000000001">
<Attrib
...
有些事件是一行,有些是堆栈跟踪等。在上面的示例中,我想获得3个事件。我有工作命令代码
var wip = false
var uow = ""
var sb: StringBuilder = new StringBuilder
Source.
fromFile(f).
getLines.
toStream.
zipWithIndex.
foreach {
case (l, index) => {
l match {
case ln if ln.trim == "" =>
case ln if ue.isBeginLine(ln) && wip =>
processEvent(sb.toString, ue)
sb.setLength(0)
sb.append(ln)
case ln if ue.isBeginLine(ln) && !wip =>
wip = true
sb.append("\n").append(ln)
case ln if wip => sb.append("\n").append(ln)
case ln => log.info(">> Worker: Rejecting: %s".format(ln))
} // match
}} // foreach
我可以使用以下方法 ue.isBeginLine 来识别事件的开始。以下是示例代码(针对每种不同的日志格式进行了自定义) - 稍后我将使isBeginLine更通用。
def isBeginLine(s: String): Boolean =
s.startsWith("2015") |
s.startsWith("<Nov 28") |
s.startsWith("WebLogic") |
s.startsWith("INFO:") |
s.startsWith("WARNING:") |
s.startsWith("Parsing") |
s.startsWith("Nov 28")
如上所述,我尝试了以下内容(来自Best way to read lines in groups in a flat file)。不幸的是,下面的方法需要为每个事件定义一个终结符或分隔符
val i = Source.
fromFile(f).
getLines
def groupIterator(xs: Iterator[String]) = new Iterator[String] {
var tmp = new StringBuffer
def hasNext = xs.hasNext
def next = xs.takeWhile(!_.startsWith("2015")).mkString("\n")
}
for (g <- groupIterator(i)) println("=======\n" + g + "\n==========")
因此,出于好奇,是否有更好的,功能性的方法将日志文件解析为事件?理想情况下,我想要像以下一样来聚合事件。
Source.
fromFile(f).
getLines.
"""splitEvents""".
foldLeft( HashMap[String, Event]() )( .... ) )
答案 0 :(得分:1)
使用相同类型的方法,但使用BufferedIterator
,以便您可以访问head
作为预测。它的功能稍差,但你可以自己包装,让它在外面再次起作用。核心例程看起来像
def getNextChunk(i: BufferedIterator[String]): Option[Array[String]] =
if (!i.hasNext) None
else {
var ab = Array.newBuilder[String]
ab += i.next
while (i.hasNext && !isRecordStart(i.head)) ab += i.next
Some(ab.result)
}
然后你只是反复调用它直到你点击None
。例如,你可以
Iterator.continually(getNextChunk(i)).
takeWhile(_.isDefined).
map(_.get)
获取块的迭代器。
或者你可以从GroupedIterator
开出自己的BufferedIterator
来实现同样的事情;它可能会更有效率。