2011-05-24 2 views
21

Есть ли какой-нибудь инструмент, который может извлечь список следов стека, появляющихся в файле журнала, и, возможно, считать уникальными?Инструмент для извлечения трасс java стека из файлов журналов

EDIT: Я бы предпочел что-то, что не основано на графическом интерфейсе, и будет работать на фоне и дать какой-то отчет. У меня довольно много журналов, собранных из нескольких сред, и просто хочу получить быстрый обзор.

+0

Почему у вас столько следов стека в вашем журнале? Вы регистрируете исключения слева и справа? Вы уверены, что это хорошая идея? – sleske

+0

Это журнал теста производительности, и некоторые части системы терпят неудачу под давлением. То, что я хочу достичь, - это отчет о симе, где и какие исключения произошли во время работы. –

+0

Это может быть одно исключение, произошедшее за 1 день, или это может быть 1000 исключений, произошедших за одну минуту. Количество исключений не определяется количеством журналов. –

ответ

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 
} 
0

Я использую Baretail.

+2

Я знаю об этом инструменте, и на самом деле я его тоже использую :). Но я бы предпочел что-то, что не основано на графическом интерфейсе. У нас довольно много журналов, и открытие каждого из них в графическом интерфейсе, к сожалению, не является вариантом. –

13

Вы можете написать это сами довольно легко. Вот картина:

  1. Открыть файл
  2. Поиск строки "\n\tat " (это новая линия, закладка, at, пустой) Это довольно редко строка за пределами трассировки стека.

Теперь все, что вам нужно сделать, это найти первую строку, которая не начинается с \t, чтобы найти конец трассировки стека. Вы можете пропустить 1-3 строки после этого, чтобы поймать прикованные исключения.

Плюс добавить пару строк (например, 10 или 50) перед первой строкой трассировки стека, чтобы получить некоторый контекст.

+0

Спасибо, Аарон. У меня была такая же идея, и именно это я и сделаю, если не найду ничего, что уже есть :). Я уверен, что эта функция присутствует в некоторых коммерческих анализаторах журналов, но я надеялся, что что-то подобное существует также как бесплатный инструмент или сценарий примера. –

14

Вот быстрый и грязный выражение Grep ... если вы используете регистратор, таких как log4j, чем в первой строке, за исключением, как правило, содержат WARN или ERROR, следующая строка будет содержать Exception имя, и, возможно, сообщение, а затем последующие трассировки стека будет начинаться с одной из следующих функций:

  1. "\tat" (табл + в)
  2. "Caused by: "
  3. "\t... <some number> more" (это линии, которые указывают на количество кадров в стеке не показано в «Вызванный» исключением)
  4. имя Exception (и, возможно, сообщение) до стека

Мы хотим, чтобы все из приведенных выше строк, поэтому выражение Grep является:

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

это предполагает класс Exception всегда содержит слово Exception, которое может или не может быть правдой, но это быстро и грязный в конце концов.

При необходимости отрегулируйте свой конкретный случай.

9

Я написал инструмент в 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] 
+0

Ваш скрипт отлично работает. Однако было бы лучше добавить дату исключения. – Mitchapp

+0

@ Mitchapp У вас есть пример файла журнала с датами? –

1

Вот хороший код, который делает то же самое - http://www.techiedelight.com/java-program-search-exceptions-huge-log-file-on-server/

Это в основном читает лог-файл построчно и поиск ключевого слова «Exception» в каждой строке. После этого он будет печатать следующие 10 строк (трассировка исключений) в отдельном файле вывода.

Смежные вопросы