2011-05-24 15 views
21

¿Hay alguna herramienta que pueda extraer una lista de rastreos de pila que aparezca en el archivo de registro y probablemente cuente unos únicos?Herramienta para extraer rastros de la pila Java de los archivos de registro

EDIT: Preferiría algo que no esté basado en la GUI y se ejecute en segundo plano y le devuelva algún tipo de informe. Tengo muchos registros recopilados de varios entornos y me gustaría obtener una visión general rápida.

+0

¿Por qué tiene tantos rastros de pila en su registro? ¿Estás registrando excepciones a la izquierda y a la derecha? ¿Estás seguro de que es una buena idea? – sleske

+0

Eso es un registro de una prueba de rendimiento y ciertas partes del sistema fallan bajo presión. Lo que quiero lograr es un informe simpe sobre dónde y qué excepciones ocurrieron durante la ejecución. –

+0

Puede haber una excepción en 1 día o puede haber 1000 excepciones en un minuto. La cantidad de excepciones no está determinada por la cantidad de registros. –

Respuesta

2

He encontrado la siguiente secuencia de comandos de Groovy. Está, por supuesto, muy ajustado a mis necesidades, pero espero que ayude a alguien.

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

Yo uso Baretail.

+2

Conozco esa herramienta y realmente la uso también :). Pero preferiría algo que no esté basado en GUI. Tenemos muchos registros y abrir cada uno de ellos en la GUI no es, desafortunadamente, una opción. –

13

Puede escribir esto usted mismo con bastante facilidad. Aquí está el patrón:

  1. Abrir el archivo
  2. Busque la cadena "\n\tat " (que es la nueva línea, pestaña, at, en blanco) Esta es una cadena bastante raro fuera de la traza de la pila.

Ahora todo lo que necesita hacer es encontrar la primera línea que no comienza con \t para encontrar el final del seguimiento de la pila. Es posible que desee omitir 1-3 líneas después de eso para capturar excepciones encadenadas.

Además, agregue un par de líneas (digamos 10 o 50) antes de la primera línea del trazado de la pila para obtener un poco de contexto.

+0

Gracias, Aaron. Tenía la misma idea y eso es lo que haría eventualmente si no encuentro nada que ya esté allí :). Estoy bastante seguro de que esta característica está presente en algunos analizadores de registros comerciales, pero esperaba que algo así existiera también como una herramienta gratuita o script de ejemplo. –

14

Aquí es una expresión GREP rápido y sucio-... si está utilizando un registrador como log4j que la primera línea de la excepción contendrá generalmente WARN o ERROR, la siguiente línea contendrá la excepción nombre, y, opcionalmente, un mensaje, y después el posterior seguimiento de la pila se iniciará con una de las siguientes:

  1. "\tat" (pestaña + a)
  2. "Caused by: "
  3. "\t... <some number> more" (éstas son las líneas que indican el número de cuadros en la pila no se muestra en un "causado por" excepción)
  4. Un nombre de Excepción (y tal vez el mensaje) antes de la pila

Queremos conseguir todo de las líneas anteriores, por lo que la expresión gREP es:

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

asume una clase de excepción siempre contiene la palabra Exception que puede o no puede ser cierto, pero esto es rápido y sucio después de todo.

Ajuste según sea necesario para su caso específico.

9

Escribí una herramienta en Python. Se las arregla para dividir dos rastros de pila, incluso si se encuentran uno detrás del otro en el registro.

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

Su script funciona a la perfección. Sin embargo, sería más agradable agregar la fecha de la excepción. – Mitchapp

+0

@Mitchapp ¿tiene un ejemplo de archivo de registro con fechas? –

Cuestiones relacionadas