从日志文件中提取Java堆栈跟踪的工具

时间:2011-05-24 08:31:35

标签: java exception logging stack-trace

是否有任何工具可以提取日志文件中出现的堆栈跟踪列表,并且可能会计算唯一的堆栈跟踪列表?

编辑:我会做一些非基于GUI的事情,并在后台运行并提供某种报告。我从很多环境中收集了很多日志,只是想快速浏览一下。

6 个答案:

答案 0 :(得分:16)

这是一个快速而又脏的grep表达式...如果你使用log4j这样的记录器,那么异常的第一行通常会包含WARNERROR,下一行将包含Exception名称和可选的消息,然后后续堆栈跟踪将以下列之一开头:

  1. "\tat"(tab + at)
  2. "Caused by: "
  3. "\t... <some number> more"(这些行表示堆栈中的帧数未在“由...引起”异常中显示)
  4. 堆栈前的异常名称(可能是消息)
  5. 我们希望获得上述所有行,因此grep表达式为:

    grep -P "(WARN|ERROR|^\tat |Exception|^Caused by: |\t... \d+ more)"

    它假设一个Exception类总是包含单词Exception,这可能是也可能不是,但毕竟这是快速而又脏的。

    根据具体情况进行调整。

答案 1 :(得分:13)

你可以很容易地自己写这个。这是模式:

  1. 打开文件
  2. 搜索字符串"\n\tat "(即新行,制表符,at,空白)这是堆栈跟踪之外非常罕见的字符串。
  3. 现在您需要做的就是找到不以\t开头的第一行来查找堆栈跟踪的结尾。您可能希望在此之后跳过1-3行来捕获链式异常。

    在堆栈跟踪的第一行之前添加几行(比如说10或50)以获得一些上下文。

答案 2 :(得分:10)

我用Python编写了一个工具。它设法分割两个堆栈跟踪,即使它们在日志中相互靠近。

#!/usr/bin/env python
#
# Extracts exceptions from log files.
#

import sys
import re
from collections import defaultdict

REGEX = re.compile("(^\tat |^Caused by: |^\t... \\d+ more)")
# Usually, all inner lines of a stack trace will be "at" or "Caused by" lines.
# With one exception: the line following a "nested exception is" line does not
# follow that convention. Due to that, this line is handled separately.
CONT = re.compile("; nested exception is: *$")

exceptions = defaultdict(int)

def registerException(exc):
  exceptions[exc] += 1

def processFile(fileName):
  with open(fileName, "r") as fh:
    currentMatch = None
    lastLine = None
    addNextLine = False
    for line in fh.readlines():
      if addNextLine and currentMatch != None:
        addNextLine = False
        currentMatch += line
        continue
      match = REGEX.search(line) != None
      if match and currentMatch != None:
        currentMatch += line
      elif match:
        currentMatch = lastLine + line
      else:
        if currentMatch != None:
          registerException(currentMatch)
        currentMatch = None
      lastLine = line
      addNextLine = CONT.search(line) != None
    # If last line in file was a stack trace
    if currentMatch != None:
      registerException(currentMatch)

for f in sys.argv[1:]:
  processFile(f)

for item in sorted(exceptions.items(), key=lambda e: e[1], reverse=True):
  print item[1], ":", item[0]

答案 3 :(得分:2)

我提出了以下Groovy脚本。当然,它根据我的需求进行了很大调整,但我希望它可以帮到某些人。

def traceMap = [:]

// Number of lines to keep in buffer
def BUFFER_SIZE = 100

// Pattern for stack trace line
def TRACE_LINE_PATTERN = '^[\\s\\t]+at .*$'

// Log line pattern between which we try to capture full trace
def LOG_LINE_PATTERN = '^([<#][^/]|\\d\\d).*$'

// List of patterns to replace in final captured stack trace line 
// (e.g. replace date and transaction information that may make similar traces to look as different)
def REPLACE_PATTERNS = [
  '^\\d+-\\d+\\@.*?tksId: [^\\]]+\\]',
  '^<\\w+ \\d+, \\d+ [^>]*?> <[^>]*?> <[^>]*?> <[^>]*?> <',
  '^####<[^>]+?> <[^>]*?> <[^>]*?> <[^>]*?> <[^>]*?> <[^>]*?> <[^>]*?> <[^>]*?> <[^>]*?> <[^>]*?> <[^>]*?> <',
  '<([\\w:]+)?TransaktionsID>[^<]+?</([\\w:]+)?TransaktionsID>',
  '<([\\w:]+)?TransaktionsTid>[^<]+?</([\\w:]+)?TransaktionsTid>'
]

new File('.').eachFile { File file ->
  if (file.name.contains('.log') || file.name.contains('.out')) {
    def bufferLines = []
    file.withReader { Reader reader ->
      while (reader.ready()) {      
        def String line = reader.readLine()
        if (line.matches(TRACE_LINE_PATTERN)) {
          def trace = []
          for(def i = bufferLines.size() - 1; i >= 0; i--) {
            if (!bufferLines[i].matches(LOG_LINE_PATTERN)) {
              trace.add(0, bufferLines[i])
            } else {
              trace.add(0, bufferLines[i])
              break
            }
          }
          trace.add(line)
          if (reader.ready()) {
            line = reader.readLine()
            while (!line.matches(LOG_LINE_PATTERN)) {
              trace.add(line)
              if (reader.ready()) {
                line = reader.readLine()
              } else {
                break;
              }
            }
          }
          def traceString = trace.join("\n")
          REPLACE_PATTERNS.each { pattern ->
            traceString = traceString.replaceAll(pattern, '')
          }
          if (traceMap.containsKey(traceString)) {
            traceMap.put(traceString, traceMap.get(traceString) + 1)
          } else {
            traceMap.put(traceString, 1)
          }
        }
        // Keep the buffer of last lines.
        bufferLines.add(line)
        if (bufferLines.size() > BUFFER_SIZE) {
          bufferLines.remove(0)
        }
      }
    }
  }
}

traceMap = traceMap.sort { it.value }

traceMap.reverseEach { trace, number ->
  println "-- Occured $number times -----------------------------------------"
  println trace
}

答案 4 :(得分:1)

这是一个很好的代码 - http://www.techiedelight.com/java-program-search-exceptions-huge-log-file-on-server/

它基本上逐行读取日志文件,并在每一行中搜索关键字“Exception”。一旦找到,它将在单独的输出文件中打印接下来的10行(异常跟踪)。

答案 5 :(得分:0)

我使用Baretail