2011-05-24 16 views
21

Czy istnieje narzędzie, które może wyodrębnić listę śladów stosu pojawiających się w pliku dziennika i prawdopodobnie policzyć unikalne?Narzędzie do wyodrębniania ścieżek stosu java z plików dziennika

EDYTOWANIE: Preferowałbym coś, co nie jest oparte na GUI i działa w tle i daje jakiś raport z powrotem. Mam wiele dzienników zebranych z kilku środowisk i po prostu chciałbym uzyskać szybki przegląd.

+0

Dlaczego w dzienniku znajduje się tyle śladów stosu? Czy rejestrujesz wyjątki w lewo i w prawo? Czy jesteś pewien, że to dobry pomysł? – sleske

+0

To jest dziennik z testu wydajności i niektóre części systemu zawodzą pod ciśnieniem. To, co chcę osiągnąć, to prosty raport o tym, gdzie i które wyjątki zdarzyły się podczas biegu. –

+0

Może to być jeden wyjątek w ciągu 1 dnia lub może to być 1000 wyjątków w ciągu jednej minuty. Ilość wyjątków nie jest określona przez ilość dzienników. –

Odpowiedz

2

Wymyśliłem następujący scenariusz Groovy. Jest to oczywiście bardzo dostosowane do moich potrzeb, ale mam nadzieję, że komuś to pomoże.

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

Używam Baretail.

+2

Wiem o tym narzędziu i właściwie też go używam :). Ale wolałbym coś, co nie jest oparte na GUI. Mamy dość dużo dzienników i otwarcie każdego z nich w GUI niestety nie jest opcją. –

13

Możesz napisać to dość łatwo. Oto wzór:

  1. Otwórz plik
  2. Szukaj ciąg "\n\tat " (to nowa linia, zakładka, at, puste) Jest to dość rzadkością ciąg poza ślady stosu.

Teraz wystarczy znaleźć pierwszą linię, która nie rozpoczyna się od \t, aby znaleźć koniec śladu stosu. Możesz pominąć 1-3 wiersze po tym, aby złapać przykręcone wyjątki.

Dodaj kilka linii (powiedzmy 10 lub 50) przed pierwszą linią śledzenia stosu, aby uzyskać kontekst.

+0

Dzięki, Aaron. Miałem ten sam pomysł i to właśnie powinienem zrobić, jeśli nie znajdę niczego, co już tam jest :). Jestem pewien, że ta funkcja jest obecna w niektórych komercyjnych analizatorach logów, ale miałem nadzieję, że coś takiego istnieje również jako darmowe narzędzie lub skrypt przykładowy. –

14

Oto szybki i-brudny wyrażenie grep ... jeśli używasz rejestratora takich jak log4j niż pierwszej linii wyjątkiem będzie na ogół zawierają WARN lub ERROR, następna linia będzie zawierać wyjątek nazwa i ewentualnie komunikat, a następnie kolejny ślad stosu rozpocznie się jednego z następujących:

  1. "\tat" (karta + w)
  2. "Caused by: "
  3. "\t... <some number> more" (są to linie, które wskazują liczbę klatek w stosie nie pokazano w „Spowodowane przez” wyjątku)
  4. nazwę Exception (i być może wiadomość) przed stos

Chcemy, aby wszystkie z powyższych linii, więc wyrażenie grep jest:

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

zakłada klasa Exception zawsze zawiera słowo Exception które mogą lub nie mogą być prawdziwe, ale to jest do szybkiego i brzydka po wszystkim.

Dostosuj w razie potrzeby do konkretnego przypadku.

9

Napisałem narzędzie w Pythonie. Udaje mu się podzielić dwa ślady stosu, nawet jeśli trafią one bezpośrednio w dzienniku.

#!/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

Twój skrypt działa świetnie. Jednak byłoby ładniej dodać datę wyjątku. – Mitchapp

+0

@Mitchapp masz przykładowy plik dziennika z datami? –