Hive RegexSerDe多行日志匹配

时间:2013-07-29 22:20:13

标签: regex hive multiline

我正在寻找可以以

的形式提供给Hive QL的“创建外部表”声明的正则表达式
"input.regex"="the regex goes here"

条件是RegexSerDe必须读取的文件中的日志具有以下形式:

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.

我正在使用以下创建外部表代码:

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';

这就是事情:

它能够拉出每个日志的所有第一个线路。但不是具有多行的其他日志行。我尝试了所有链接,最后将\z替换为\Z,将\A替换为^,将\Z\z替换为$没什么用的。我在output.format.string的%4$s中遗漏了什么?或者我没有正确使用正则表达式?

正则表达式的作用:

首先匹配时间戳,然后是日志类型(DEBUGINFO或其他),然后是ID(小写字母,数字和连字符的组合),后跟任何,直到找到下一个时间戳,或者直到找到输入结束与最后一个日志条目匹配为止。我还尝试在最后添加/m,在这种情况下,生成的表具有所有NULL值。

3 个答案:

答案 0 :(得分:1)

您的正则表达式似乎存在许多问题。

首先,删除双方括号。

其次,\A\Z / \z将匹配输入的开头和结尾,而不仅仅是一行。将\A更改为^以匹配行首,但不要\z更改为$,因为您确实希望在这种情况下匹配输入结束。

第三,您要匹配(.*?),而不是(.*)?。第一种模式是不合适的,而第二种模式是贪婪的但是可选的。它应该将您的整个输入与最终匹配,因为您允许其后跟输入结束。

第四,.与换行符不匹配。您可以使用(\s|\S)代替,或([x]|[^x])等任何一对免费匹配。

第五,如果它为您提供与\A\Z / \z的单行匹配,那么输入也是单行也是在您锚定整个字符串时。

我建议尝试只匹配\n,如果没有匹配,则不包括换行符。

由于正则表达式不包含分隔符,因此无法将/m添加到结尾。它将尝试匹配文字字符/m,这就是为什么你没有匹配。

如果它能够正常工作你想要的正则表达式是:

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

故障:

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

匹配换行符的开头,以及数字为19个字符:-加上逗号,三位数字和空格。捕获除最终空间(时间戳)之外的所有空间。

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

匹配文字[,任意数量的大写字母,甚至没有,文字]和空格。捕获除最终空间之外的所有空间(错误级别)。

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

匹配任意数量的数字,小写字母或-和空格。捕获除最终空间之外的所有空间(消息ID)。

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

匹配任何空格或非空白字符(任何字符),但匹配不合理*?。当新记录或输入结束\Z)立即提前时停止匹配。在这种情况下,您不希望再次匹配行尾,您只会在输出中获得一行。捕获除最终(消息文本)之外的所有内容。 \r?\n将跳过邮件末尾的最终换行符,\r?\Z也是如此。您也可以写\r?\n\z注意:如果有输入,则\Z包括输入末尾的最终换行符。小写\z仅在输入结束时匹配,而不是在输入结束前的换行符。我已经添加了\z?,以防您必须处理Windows行结尾,但是,我不相信这是必要的。

但是,我怀疑除非您可以立即提供整个文件而不是逐行提供,否则这也无法正常工作。

您可以尝试的另一个简单测试是:

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

如果它有效,它将匹配任何实线,然后是下一行的行数(时间戳的第一个数字)。

答案 1 :(得分:1)

遵循Java正则表达式可能会有所帮助:

(\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)

故障:

  • 第一个捕获小组(\d{4}-\d{1,2}-\d{1,2}\s+\d{1,2}:\d{1,2}:\d{1,2},\d{1,3})
  • 第二捕获小组(\[.+?\])
  • 第三捕获小组(.+?)
  • 第4个捕获组([\s\S]+?)

(?=\d{4}-\d{1,2}-\d{1,2}|\Z)肯定前瞻 - 断言下面的正则表达式可以匹配。最后:\d{4}-\d{1,2}-\d{1,2}。第二替代:\Z断言字符串末尾的位置。

参考http://regex101.com/

答案 2 :(得分:0)

我对Hive知之甚少,但是下面的正则表达式或者为Java字符串格式化的变体可能会有效:

(\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]+)*)

这可以在这里与您的样本数据匹配:

http://rubular.com/r/tQp9iBp4JI

细分:

  • (\d{4}-\d\d-\d\d \d\d:\d\d:\d\d,\d+)日期和时间(捕获组1)
  • \[([a-zA-Z_-]+)\]日志级别(捕获组2)
  • ([\w-]+)请求ID(捕获组3)
  • ((?:[^\n\r]+)(?:[\n\r]{1,2}\s[^\n\r]+)*)潜在的多行消息(捕获组4)

前三个捕获组非常简单。

最后一个可能有点奇怪,但它正在使用rubular。细分:

(                       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
)

我使用[^\n\r]代替.,因为看起来RegexSerDe.匹配新行(link):

// 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;
}

希望这有帮助。