utilizar solo regulares regular rango probar numeros letras extraer expresiones expresion ejemplos con como cadenas java regex algorithm logging awk

solo - Filtrado de registros con expresiones regulares en java



extraer cadenas con expresiones regulares java (2)

La descripción es bastante larga, así que por favor tengan paciencia conmigo:
Tengo archivos de registro que van desde 300 MB a 1.5 Gb de tamaño, que deben filtrarse dada una clave de búsqueda.

El formato de los registros es algo como esto:

24 May 2017 17:00:06,827 [INFO] 123456 (Blah : Blah1) Service-name:: Single line content 24 May 2017 17:00:06,828 [INFO] 567890 (Blah : Blah1) Service-name:: Content( May span multiple lines) 24 May 2017 17:00:06,829 [INFO] 123456 (Blah : Blah2) Service-name: Multiple line content. Printing Object[ ID1=fac-adasd ID2=123231 ID3=123108 Status=Unknown Code=530007 Dest=CA ] 24 May 2017 17:00:06,830 [INFO] 123456 (Blah : Blah1) Service-name:: Single line content 4 May 2017 17:00:06,831 [INFO] 567890 (Blah : Blah2) Service-name:: Content( May span multiple lines)

Dada la clave de búsqueda 123456, necesito buscar lo siguiente:

24 May 2017 17:00:06,827 [INFO] 123456 (Blah : Blah1) Service-name:: Single line content 24 May 2017 17:00:06,829 [INFO] 123456 (Blah : Blah2) Service-name: Multiple line content. Printing Object[ ID1=fac-adasd ID2=123231 ID3=123108 Status=Unknown Code=530007 Dest=CA ] 24 May 2017 17:00:06,830 [INFO] 123456 (Blah : Blah1) Service-name:: Single line content

El siguiente script awk hace mi trabajo (muy lentamente):

gawk ''/([0-9]{1}|[0-9]{2})/s/w+/s[0-9]{4}/{n=0}/123456/{n=1} n''

Lleva alrededor de 8 minutos buscar un archivo de registro de 1 GB de tamaño. Y necesito hacer esto para muchos de esos archivos. Para colmo, tengo varias de estas claves de búsqueda, lo que hace que toda la tarea sea imposible.

Mi solución inicial es usar multihebra. He utilizado un FixThreadPoolExcutor fijo, envié una tarea para cada archivo que necesita ser filtrado. Dentro de la descripción de la tarea, he generado un nuevo proceso utilizando Runtime () de Java, que ejecutaría el script gawk usando bash y escribiría el resultado en un archivo y luego fusionaría todos los archivos.

A pesar de que podría parecer una mala forma de proceder, dado que el filtrado depende de la E / S en lugar de la CPU, me dio una aceleración en comparación con la ejecución secuencial de la secuencia de comandos en cada archivo.

Pero aún no es suficiente ya que todo lleva 2 horas, para una sola clave de búsqueda, con 27 gb de archivos de registro. En promedio, tengo 4 de estas claves de búsqueda y necesito buscar todos sus resultados y juntarlos.

Mi método no es eficiente porque:

A) Accede a cada archivo de registro varias veces cuando se proporcionan varias claves de búsqueda y causa aún más sobrecarga de E / S.
B) Se incurre en la sobrecarga de crear un proceso dentro de cada hilo.

Una solución simple para todo esto, es alejarse de awk y hacer todo el trabajo en Java, usando alguna biblioteca de expresiones regulares. La pregunta aquí es ¿qué es esa biblioteca de expresiones regulares que podría proporcionarme el resultado deseado?
Con awk tengo la propiedad /filter/{action} que me permite especificar un rango de múltiples líneas para ser capturado (como se ve arriba). ¿Cómo puedo hacer lo mismo dentro de Java?

Estoy abierto a todo tipo de sugerencias. Por ejemplo, una opción extrema sería almacenar los archivos de registro en un sistema de archivos compartido como S3 y procesar el resultado usando varias computadoras.

Soy nuevo en stackoverflow y no sé si puedo publicar esto aquí. Pero he estado trabajando en esto durante la última semana y necesito a alguien con experiencia que me guíe en esto. Gracias por adelantado.


Tienes pocas opciones.

El mejor uno sería usar un diccionario invertido. Esto significa que para cada palabra clave x presente en al menos uno de los registros, almacena una referencia a todos los registros que lo contienen. Pero como ya pasaste una semana en esta tarea, te aconsejo usar algo que ya está allí y que hace exactamente eso: Elasticsearch . En realidad, puede usar la pila ELK completa (elasticsearch, logstash, kibana, diseñada principalmente para registros) incluso para analizar los registros, ya que puede poner una expresión regular en el archivo de configuración. Solo necesitará indexar los archivos una vez y obtendrá búsquedas tan rápido como unos pocos milisegundos.

Si realmente quiere gastar energía y no buscar la mejor solución, puede usar map-reduce en hadoop para filtrar el registro. Pero esa no es una tarea en la que map-reduce sea óptimo y sería más como un hack.


Cambiar a Java podría no ser la mejor opción si buscas acelerar el tiempo de ejecución, pero si lo estás considerando, escribí una clase de Java que podría ser útil.

Puede usarlo para buscar una o más claves en un archivo simultáneamente. Como está leyendo un archivo de registro, es seguro suponer que todas las líneas siguen el formato correcto sin errores. Por lo tanto, en lugar de revisar el formato de la expresión regular de toda la línea, simplemente se salta al lugar donde debería estar la clave (los dígitos después del primero) y la compara con el valor requerido (suponiendo que siempre sea un número).

Úselo de esta manera:

Set<Integer> keys = new HashSet(); keys.add(123456); keys.add(314159); /* synchronously (omitting 3rd argument prints to stdout) */ new KeySearch(''path/to/file.log'', keys).run(); /* asynchronously!!! (to use PrintStream, create the output file first) */ PrintStream ps1 = new PrintStream(''lines-found1.log''); PrintStream ps2 = new PrintStream(''lines-found2.log''); new Thread(new KeySearch(''path/to/1.log'', keys, ps1::println)).start(); new Thread(new KeySearch(''path/to/2.log'', keys, ps2::println)).start();

El tercer argumento es una interfaz personalizada KeySearch.Callback que recibe líneas a medida que se encuentran. Utilizo una referencia de método como ejemplo, pero puede ser cualquier cosa que desee. Aquí está la clase (requiere Java 8 al menos).

import java.io.*; import java.util.*; public class KeySearch implements Runnable { public interface Callback { void lineFound(String line); } private final Set<Integer> keys; private final Callback callback; private final String name; public KeySearch(String fileName, Collection<Integer> keys) { this(fileName, keys, System.out::println); } public KeySearch(String fileName, Collection<Integer> keys, Callback call) { this.keys = new HashSet<>(keys); this.name = fileName; this.callback = call; } @Override public void run() { String s; try(FileReader fr = new FileReader(name); BufferedReader br = new BufferedReader(fr)) { while ((s = readLine(br)) != null) if (matches(s)) callback.lineFound(s); } catch (IOException e) { System.err.println("Error reading " + name); throw new RuntimeException(e); } } private boolean matches(String line) { return keys.contains(getKeyOf(line)); } private String readLine(BufferedReader reader) throws IOException { StringBuilder line = new StringBuilder(); String next; do { next = reader.readLine(); if (next == null) return null; line.append(next).append(System.lineSeparator()); } while (next.lastIndexOf(''['') > next.lastIndexOf('']'')); return line.toString(); } private boolean isDigit(CharSequence s, int i) { char c = s.charAt(i); return c >= ''0'' && c <= ''9''; } private int getKeyOf(String line) { // find the first ] (e.g. at the end of [INFO]) // and read the first number after it int start = line.indexOf('']''); while (!isDigit(line, start)) start++; int end = start; while (isDigit(line, end)) end++; return Integer.parseInt(line.substring(start, end)); } }