Đượ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 entry
mứ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 .conf
tệp trong phần TL; DR.)
Ở đây, multiline
bộ 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 xmllint
là 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 tr
vỏ) và xml phải được chuẩn hóa (xmllint)