複数のログエントリを含むLogstash解析XMLドキュメント


8

現在、logstashとelasticsearchが私たちのユースケースに役立つかどうかを評価しています。私が持っているのは、次の形式の複数のエントリを含むログファイルです

<root>
    <entry>
        <fieldx>...</fieldx>
        <fieldy>...</fieldy>
        <fieldz>...</fieldz>
        ...
        <fieldarray>
            <fielda>...</fielda>
            <fielda>...</fielda>
            ...
        </fieldarray>
    </entry>
    <entry>
    ...
    </entry>
    ...
<root>

entry要素には1つのログイベントが含まれます。(興味がある場合、ファイルは実際にはテンポタイムシート(Atlassian JIRAプラグイン)作業ログエクスポートです。)

独自のコーデックを作成せずに、そのようなファイルを複数のログイベントに変換することは可能ですか?

回答:


11

申し分なく、私のために働く解決策を見つけました。ソリューションの最大の問題は、XMLプラグインがかなり不安定ではないことです。

TLDR

Bashコマンドライン:

gzcat -d file.xml.gz | tr -d "\n\r" | xmllint --format - | logstash -f logstash-csv.conf

Logstash構成:

input {
    stdin {}
}

filter {
    # add all lines that have more indentation than double-space to the previous line
    multiline {
        pattern => "^\s\s(\s\s|\<\/entry\>)"
        what => previous
    }
    # multiline filter adds the tag "multiline" only to lines spanning multiple lines
    # We _only_ want those here.
    if "multiline" in [tags] {
        # Add the encoding line here. Could in theory extract this from the
        # first line with a clever filter. Not worth the effort at the moment.
        mutate {
            replace => ["message",'<?xml version="1.0" encoding="UTF-8" ?>%{message}']
        }
        # This filter exports the hierarchy into the field "entry". This will
        # create a very deep structure that elasticsearch does not really like.
        # Which is why I used add_field to flatten it.
        xml {
            target => entry
            source => message
            add_field => {
                fieldx         => "%{[entry][fieldx]}"
                fieldy         => "%{[entry][fieldy]}"
                fieldz         => "%{[entry][fieldz]}"
                # With deeper nested fields, the xml converter actually creates
                # an array containing hashes, which is why you need the [0]
                # -- took me ages to find out.
                fielda         => "%{[entry][fieldarray][0][fielda]}"
                fieldb         => "%{[entry][fieldarray][0][fieldb]}"
                fieldc         => "%{[entry][fieldarray][0][fieldc]}"
            }
        }
        # Remove the intermediate fields before output. "message" contains the
        # original message (XML). You may or may-not want to keep that.
        mutate {
            remove_field => ["message"]
            remove_field => ["entry"]
        }
    }
}

output {
    ...
}

詳細な

私のソリューションが機能するのは、少なくともentryレベルまでは、XML入力は非常に均一であり、何らかのパターンマッチングで処理できるためです。

エクスポートは基本的にXMLの非常に長い1行であり、logstash xmlプラグインは基本的にXMLデータを含むフィールド(読み取り:行の列)でのみ機能するため、データをより有用な形式に変更する必要がありました。

シェル:ファイルの準備

  • gzcat -d file.xml.gz |:データが多すぎました-明らかにそれをスキップできます
  • tr -d "\n\r" |:XML要素内の改行を削除:一部の要素には、文字データとして改行を含めることができます。次の手順で、これらを削除するか、何らかの方法でエンコードする必要があります。この時点ではすべてのXMLコードが1行に収まっていると想定していますが、このコマンドで要素間の空白を削除しても問題ありません。

  • xmllint --format - |:xmllintでXMLをフォーマットする(libxmlに付属)

    ここでは、XML(<root><entry><fieldx>...</fieldx></entry></root>)の単一の巨大なスパゲッティ行が適切にフォーマットされています。

    <root>
      <entry>
        <fieldx>...</fieldx>
        <fieldy>...</fieldy>
        <fieldz>...</fieldz>
        <fieldarray>
          <fielda>...</fielda>
          <fieldb>...</fieldb>
          ...
        </fieldarray>
      </entry>
      <entry>
        ...
      </entry>
      ...
    </root>
    

Logstash

logstash -f logstash-csv.conf

.confTL; DRセクションのファイルの完全な内容を参照してください。)

ここでは、multilineフィルターがうまく機能します。複数の行を1つのログメッセージにマージできます。そして、これがによるフォーマットxmllintが必要だった理由です:

filter {
    # add all lines that have more indentation than double-space to the previous line
    multiline {
        pattern => "^\s\s(\s\s|\<\/entry\>)"
        what => previous
    }
}

これは基本的に、インデントが2つ以上のスペースを持つすべての行(または</entry>/ xmllintがデフォルトで2つのスペースでインデントを行う)は、前の行に属していることを示しています。これは、文字データに改行(trシェルで取り除かれたもの)が含まれていてはならず、xmlが正規化されている必要があること(xmllint)も意味します。


こんにちは、あなたはこの仕事をすることに成功しましたか?同様のニーズがあり、分割に伴う複数行のソリューションがうまく機能しなかったので、私は興味があります。フィードバックをお
寄せ

@vizこれは機能しましたが、実稼働では使用していません。あなたは非常に規則的なXML構造を持っているし、(「ファイルの準備」の答えの項を参照してください)インデントと最初にそれをフォーマットした場合は複数行にのみ動作します
dualed

1

私にも似たような事件がありました。このxmlを解析するには:

<ROOT number="34">
  <EVENTLIST>
    <EVENT name="hey"/>
    <EVENT name="you"/>
  </EVENTLIST>
</ROOT>

この構成を使用してlogstashを実行します。

input {
  file {
    path => "/path/events.xml"
    start_position => "beginning"
    sincedb_path => "/dev/null"
    codec => multiline {
      pattern => "<ROOT"
      negate => "true"
      what => "previous"
      auto_flush_interval => 1
    }
  }
}
filter {
  xml {
    source => "message"
    target => "xml_content"
  }
  split {
    field => "xml_content[EVENTLIST]"
  }
  split {
    field => "xml_content[EVENTLIST][EVENT]"
  }
  mutate {
    add_field => { "number" => "%{xml_content[number]}" }
    add_field => { "name" => "%{xml_content[EVENTLIST][EVENT][name]}" }
    remove_field => ['xml_content', 'message', 'path']
  }
}
output {
  stdout {
    codec => rubydebug
  }
}

これが誰かの役に立つことを願っています。それを手に入れるのに長い時間がかかりました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.