2013-07-29 4 views
9

ich für einen regulären Ausdruck suchen, die eine eingespeist werden kann „erstellen externe Tabelle“ statement of Hive QL in Form vonHive RegexSerDe mehrzeilige Log passende

"input.regex"="the regex goes here" 

Die Bedingung ist, dass die Protokolle in den Dateien, die die RegexSerDe muss Lesung ist in der folgenden Form sein:

2013-02-12 12:03:22,323 [DEBUG] 2636hd3e-432g-dfg3-dwq3-y4dsfq3ew91b Some message that can contain any special character, including linebreaks. This one does not have a linebreak. It just has spaces on the same line. 
2013-02-12 12:03:24,527 [DEBUG] 265y7d3e-432g-dfg3-dwq3-y4dsfq3ew91b Some other message that can contain any special character, including linebreaks. This one does not have one either. It just has spaces on the same line. 
2013-02-12 12:03:24,946 [ERROR] 261rtd3e-432g-dfg3-dwq3-y4dsfq3ew91b Some message that can contain any special character, including linebreaks. 
This is a special one. 
This has a message that is multi-lined. 
This is line number 4 of the same log. 
Line 5. 
2013-02-12 12:03:24,988 [INFO] 2632323e-432g-dfg3-dwq3-y4dsfq3ew91b Another 1-line log 
2013-02-12 12:03:25,121 [DEBUG] 263tgd3e-432g-dfg3-dwq3-y4dsfq3ew91b Yet another one line log. 

ich den folgenden erstellen externe Tabelle Code verwenden:

CREATE EXTERNAL TABLE applogs (logdatetime STRING, logtype STRING, requestid STRING, verbosedata STRING) 
ROW FORMAT SERDE 'org.apache.hadoop.hive.contrib.serde2.RegexSerDe' 
WITH SERDEPROPERTIES 
(
"input.regex" = "(\\A[[0-9:-] ]{19},[0-9]{3}) (\\[[A-Z]*\\]) ([0-9a-z-]*) (.*)?(?=(?:\\A[[0-9:-] ]{19},[0-9]|\\z))", 
"output.format.string" = "%1$s \\[%2$s\\] %3$s %4$s" 
) 
STORED AS TEXTFILE 
LOCATION 'hdfs:///logs-application'; 

Hier ist die Sache:

Es ist in der Lage, alle ersten Zeilen jedes Protokolls zu ziehen. Aber nicht die anderen Zeilen von Protokollen, die mehr als eine Zeile haben. Ich habe versucht, alle Links, ersetzt \z mit \Z am Ende, ersetzt \A mit ^ und \Z oder \z mit $, nichts hat funktioniert. Fehle ich etwas in der %4$s output.format.string? oder verwende ich die Regex nicht richtig?

Was die Regex tut:

Es entspricht den Zeitstempel zuerst, gefolgt von dem Protokolltyp (DEBUG oder INFO oder was auch immer), dann der ID (Mischung aus Kleinbuchstabe Buchstaben, Zahlen und Bindestrichen) von ANYTHING gefolgt, bis der nächste Zeitstempel gefunden wird oder bis das Ende der Eingabe mit dem letzten Protokolleintrag übereinstimmt. Ich habe auch versucht, die /m am Ende hinzufügen, in diesem Fall hat die generierte Tabelle alle NULL-Werte.

+0

warum ordnen Sie das Baby nicht an? (lol das ist nicht einmal ein Verb, aber stil ... könntest du nicht jeden auf ein Array setzen? dann ist die erste Zeile der Schlüssel 0, der zweite mehrzeilige Gegenstand wäre in 1, die anderen zwei in 2 und 3) und du kannst sie anrufen wie du willst) – user1576978

Antwort

1

Es scheint eine Reihe von Problemen mit Ihrer Regex zu geben.

Entfernen Sie zuerst Ihre doppelten eckigen Klammern.

Zweitens \A und \Z/\z sind den Anfang und das Ende des Eingang, nicht nur eine Linie entsprechen. Ändern Sie \A in ^, um den Zeilenanfang anzupassen, aber nicht ändern Sie \z zu $, da Sie in diesem Fall tatsächlich das Ende der Eingabe anpassen möchten.

Drittens möchten Sie (.*?), nicht (.*)? entsprechen. Das erste Muster ist nicht gierig, während das zweite Muster gierig, aber optional ist. Es sollte Ihre gesamte Eingabe an das Ende angepasst haben, da Sie nach dem Ende der Eingabe folgen konnten.

Viertens, . stimmt nicht mit Zeilenvorschüben überein. Sie können stattdessen (\s|\S) oder ([x]|[^x]), usw., jedes Paar von kostenlosen Übereinstimmungen verwenden.

Fünftens: Wenn es Ihnen einzelne Zeilen gibt übereinstimmt mit \A und \Z/\z dann der Eingabe auch einzelne Linien waren, wie Sie die gesamte Zeichenfolge wurden verankern.

Ich würde vorschlagen, nur \n übereinstimmen, wenn nichts übereinstimmt, dann Zeilenumbrüche sind nicht enthalten.

Sie können /m nicht am Ende hinzufügen, da die Regex keine Trennzeichen enthält.Es wird versuchen, die wörtlichen Zeichen /m stattdessen zu entsprechen, weshalb Sie keine Übereinstimmung haben.

Wenn es würde die Regex Sie würde arbeiten wollen:

"^([0-9:- ]{19},[0-9]{3}) (\\[[A-Z]*\\]) ([0-9a-z-]*) ([\\s\\S]*?)(?=\\r?\\n([0-9:-]){19},[0-9]|\\r?\\z)" 

Aufteilung:

^([0-9:- ]{19},[0-9]{3}) 

Spiel Beginn der Newline und 19 Zeichen, die Ziffern sind, :, - oder plus ein Komma, drei Ziffern und ein Leerzeichen. Erfassen Sie alle bis auf das letzte Leerzeichen (den Zeitstempel).

(\\[[A-Z]*\\]) 

Spiel eine wörtliche [, eine beliebige Anzahl von Großbuchstaben, auch keine, eine wörtliche ] und ein Raum. Erfassen Sie alle außer dem letzten Leerzeichen (die Fehlerstufe).

([0-9a-z-]*) 

Spiel eine beliebige Anzahl von Ziffern, Kleinbuchstaben oder - und einem Raum. Erfassen Sie alle bis auf das letzte Leerzeichen (die Nachrichten-ID).

([\\s\\S]*?)(?=\\r?\\n([0-9:-]){19},[0-9]|\\r?\\Z) 

Spiel beliebige Leerzeichen oder Nicht-Leerzeichen (ein beliebiges Zeichen), aber ungreedy *? entsprechen. Stoppen Sie den Abgleich, wenn ein neuer Datensatz oder Ende des Eingangs (\Z) unmittelbar voraus ist. In diesem Fall möchten Sie das Ende der Zeile nicht erneut zuordnen, Sie erhalten nur eine Zeile in Ihrer Ausgabe. Erfassen Sie alle bis auf das letzte (den Nachrichtentext). Die \r?\n Überspringt den letzten Zeilenumbruch am Ende Ihrer Nachricht, ebenso wie der \r?\Z. Sie könnten auch schreiben \r?\n\z Hinweis: Hauptstadt \Z enthält die letzte Zeilenumbruch am Ende der Eingabe, wenn es eine gibt. Kleinbuchstaben \z passt nur am Ende der Eingabe, nicht vor dem Ende der Eingabe. Ich habe hinzugefügt \z? nur für den Fall, dass Sie mit Windows-Zeilenenden beschäftigen, aber ich glaube nicht, dass dies notwendig sein sollte.

Ich vermute jedoch, dass, wenn Sie nicht die gesamte Datei auf einmal statt Zeile für Zeile einspeisen können, dass dies auch nicht funktioniert.

Ein weiterer einfacher Test können Sie versuchen, ist:

"^([\\s\\S]+)^\\d" 

Wenn es funktioniert wird es jede volle Linie durch eine Linie Ziffer in der nächsten Zeile (die erste Ziffer des Zeitstempels), gefolgt entsprechen.

0

Ich weiß nicht viel über Hive, aber die folgende regex, oder eine für Java-Strings formatiert Variation, funktionieren könnte:

(\d{4}-\d\d-\d\d \d\d:\d\d:\d\d,\d+) \[([a-zA-Z_-]+)\] ([\w-]+) ((?:[^\n\r]+)(?:[\n\r]{1,2}\s[^\n\r]+)*) 

Diese Ihre Beispieldaten zu sehen ist hier passend:

http://rubular.com/r/tQp9iBp4JI

Eine Aufgliederung:

  • (\d{4}-\d\d-\d\d \d\d:\d\d:\d\d,\d+) Die da te und die Zeit (capture Gruppe 1)
  • \[([a-zA-Z_-]+)\] Die Protokollebene (capture Gruppe 2)
  • ([\w-]+) die Anforderungs-ID (capture Gruppe 3)
  • ((?:[^\n\r]+)(?:[\n\r]{1,2}\s[^\n\r]+)*) Die potentiell mehrzeiligen Nachricht (capture Gruppe 4)

Die ersten drei Capture-Gruppen sind ziemlich einfach.

Die letzte ist vielleicht ein wenig seltsam, aber es arbeitet auf Rubular. Eine Aufschlüsselung:

(      Capture it as one group 
    (?:[^\n\r]+)  Match to the end of the line, dont capture 
    (?:     Match line by line, after the first, but dont capture 
     [\n\r]{1,2}  Match the new-line 
     \s    Only lines starting with a space (this prevents new log-entries from matching) 
     [^\n\r]+  Match to the end of the line    
    )*     Match zero or more of these extra lines 
) 

ich [^\n\r] anstelle des . verwendet, weil es wie RegexSerDe lässt die . Spiel neue Linien (link) aussieht:

// Excerpt from https://github.com/apache/hive/blob/trunk/contrib/src/java/org/apache/hadoop/hive/contrib/serde2/RegexSerDe.java#L101 
if (inputRegex != null) { 
    inputPattern = Pattern.compile(inputRegex, Pattern.DOTALL 
     + (inputRegexIgnoreCase ? Pattern.CASE_INSENSITIVE : 0)); 
} else { 
    inputPattern = null; 
} 

Hoffnung, das hilft.

1

Nach Java regex helfen können:

(\d{4}-\d{1,2}-\d{1,2}\s+\d{1,2}:\d{1,2}:\d{1,2},\d{1,3})\s+(\[.+?\])\s+(.+?)\s+([\s\S\s]+?)(?=\d{4}-\d{1,2}-\d{1,2}|\Z) 

Aufteilung:

  • 1. Capturing Gruppe (\d{4}-\d{1,2}-\d{1,2}\s+\d{1,2}:\d{1,2}:\d{1,2},\d{1,3})
  • 2. Capturing Gruppe (\[.+?\])
  • 3. Capturing Gruppe (.+?)
  • 4. Capturing Gruppe ([\s\S]+?).

(?=\d{4}-\d{1,2}-\d{1,2}|\Z) Positive Lookahead - Behaupten, dass der Regex unten matched.1st werden kann Alternative: \d{4}-\d{1,2}-\d{1,2} .2nd Alternative: \Z assert Position am Ende des Strings.

Referenz http://regex101.com/