我正在寻找可以以
的形式提供给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
中遗漏了什么?或者我没有正确使用正则表达式?
正则表达式的作用:
首先匹配时间戳,然后是日志类型(DEBUG
或INFO
或其他),然后是ID
(小写字母,数字和连字符的组合),后跟任何,直到找到下一个时间戳,或者直到找到输入结束与最后一个日志条目匹配为止。我还尝试在最后添加/m
,在这种情况下,生成的表具有所有NULL值。
答案 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})
(\[.+?\])
(.+?)
([\s\S]+?)
。 (?=\d{4}-\d{1,2}-\d{1,2}|\Z)
肯定前瞻 - 断言下面的正则表达式可以匹配。最后:\d{4}-\d{1,2}-\d{1,2}
。第二替代:\Z
断言字符串末尾的位置。
答案 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;
}
希望这有帮助。