【问题标题】:Hive RegexSerDe Multiline Log matchingHive RegexSerDe 多行日志匹配
【发布时间】:2013-07-29 21:38:48
【问题描述】:

我正在寻找一个正则表达式,它可以以

的形式提供给 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';

事情是这样的:

它能够提取每个日志的所有 FIRST LINES。但不是其他多行的日志行。我尝试了所有链接,最后将\z 替换为\Z,将\A 替换为^\Z\z$,没有任何效果。我在 output.format.string 的%4$s 中遗漏了什么吗?还是我没有正确使用正则表达式?

正则表达式的作用:

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

【问题讨论】:

  • 你为什么不把那个宝贝排列起来呢? (哈哈,这甚至不是一个动词,但仍然......你不能将它们中的每一个都设置为一个数组吗?那么第一行将是键 0,第二个多行项目将在 1 中,另外两个在 2 和 3并且您可以随意调用它们)

标签: regex hive multiline


【解决方案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) Positive Lookahead - 断言下面的正则表达式可以匹配。第一个替代方案:\d{4}-\d{1,2}-\d{1,2}.2nd 替代方案:\Z 在字符串末尾断言位置。

参考http://regex101.com/

【讨论】:

    【解决方案2】:

    我对 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;
    }
    

    希望这会有所帮助。

    【讨论】:

      【解决方案3】:

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

      首先,删除你的双方括号。

      其次,\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"
      

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-02-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多