Logstash phân tích tài liệu xml chứa nhiều mục nhật ký


8

Tôi hiện đang đánh giá xem logstash và elaticsearch có hữu ích cho trường hợp sử dụng của chúng tôi hay không. Những gì tôi có là một tệp nhật ký chứa nhiều mục có dạng

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

Mỗi entryphần tử sẽ chứa một sự kiện đăng nhập. (Nếu bạn quan tâm, tệp thực sự là một Timesheets Tempo (Trình cắm nhật ký công việc của Atlassian JIRA).)

Có thể chuyển đổi một tệp như vậy thành nhiều sự kiện nhật ký mà không cần viết codec của riêng tôi không?

Câu trả lời:


11

Được rồi, tôi tìm thấy một giải pháp phù hợp với tôi. Vấn đề lớn nhất với giải pháp là plugin XML ... không hoàn toàn không ổn định, nhưng là tài liệu kém và lỗi hoặc tài liệu kém và không chính xác.

TLD

Dòng lệnh Bash:

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

Cấu hình 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 {
    ...
}

Chi tiết

Giải pháp của tôi hoạt động vì ít nhất là cho đến entrymức, đầu vào XML của tôi rất đồng nhất và do đó có thể được xử lý bằng một số loại khớp mẫu.

Do xuất khẩu về cơ bản là một dòng XML thực sự dài và plugin xml logstash về cơ bản chỉ hoạt động với các trường (đọc: cột trong dòng) có chứa dữ liệu XML, tôi đã phải thay đổi dữ liệu thành định dạng hữu ích hơn.

Shell: Chuẩn bị tập tin

  • gzcat -d file.xml.gz |: Chỉ là quá nhiều dữ liệu - rõ ràng bạn có thể bỏ qua
  • tr -d "\n\r" |: Xóa ngắt dòng trong các phần tử XML: Một số phần tử có thể chứa ngắt dòng dưới dạng dữ liệu ký tự. Bước tiếp theo yêu cầu những thứ này được loại bỏ, hoặc được mã hóa theo một cách nào đó. Mặc dù nó giả định rằng tại thời điểm này bạn có tất cả mã XML trong một dòng lớn, không có vấn đề gì nếu lệnh này xóa bất kỳ khoảng trắng nào giữa các phần tử

  • xmllint --format - |: Định dạng XML bằng xmllint (đi kèm với libxml)

    Ở đây, dòng spaghetti khổng lồ của XML ( <root><entry><fieldx>...</fieldx></entry></root>) được định dạng chính xác:

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

Đăng nhập

logstash -f logstash-csv.conf

(Xem nội dung đầy đủ của .conftệp trong phần TL; DR.)

Ở đây, multilinebộ lọc thực hiện các mẹo. Nó có thể hợp nhất nhiều dòng vào một thông điệp tường trình. Và đây là lý do tại sao định dạng với xmllintlà cần thiết:

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

Về cơ bản, điều này nói rằng mọi dòng có thụt đầu dòng nhiều hơn hai khoảng trắng (hoặc là </entry>/ xmllint không thụt lề với hai khoảng trắng theo mặc định) thuộc về một dòng trước đó. Điều này cũng có nghĩa là dữ liệu ký tự không được chứa dòng mới (tước bằng trvỏ) và xml phải được chuẩn hóa (xmllint)


Xin chào, bạn đã quản lý để làm cho công việc này? Tôi tò mò vì tôi có nhu cầu tương tự và giải pháp đa dòng cùng với sự phân chia không có tác dụng với tôi. Cảm ơn phản hồi của bạn
viz

@viz Điều này đã làm việc, nhưng chúng tôi không bao giờ sử dụng nó trong sản xuất. Multiline chỉ hoạt động nếu bạn có cấu trúc XML rất đều đặn và đã định dạng nó trước bằng cách thụt lề (xem câu trả lời, phần "chuẩn bị tệp")
đôi vào

1

Tôi đã có một trường hợp tương tự. Để phân tích xml này:

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

Tôi sử dụng cấu hình này để đăng nhập:

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

Tôi hy vọng điều này có thể giúp đỡ một ai đó. Tôi đã cần một thời gian dài để có được nó.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.