좋아, 나에게 맞는 해결책을 찾았다. 솔루션의 가장 큰 문제는 XML 플러그인이 ... 불안정하지는 않지만 잘못 문서화되고 버그가 있거나 잘못 문서화된다는 것입니다.
TLDR
배쉬 커맨드 라인 :
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의 한 줄이며, logstash xml 플러그인은 본질적으로 XML 데이터가 포함 된 필드 (읽기 : 열의 열)에서만 작동하므로 데이터를보다 유용한 형식으로 변경해야했습니다.
셸 : 파일 준비
gzcat -d file.xml.gz |
: 너무 많은 데이터였습니다. 분명히 건너 뛸 수 있습니다.
tr -d "\n\r" |
: XML 요소 내에서 줄 바꿈 제거 : 일부 요소는 줄 바꿈을 문자 데이터로 포함 할 수 있습니다. 다음 단계 에서는 이를 제거하거나 어떤 방식으로 인코딩 해야 합니다. 이 시점에서 모든 XML 코드가 한 줄에 있다고 가정 하더라도이 명령으로 요소 사이의 공백을 제거하는지는 중요하지 않습니다.
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 -f logstash-csv.conf
( .conf
TL; DR 섹션에서 파일 의 전체 내용을 참조하십시오 .)
여기서 multiline
필터는 트릭을 수행합니다. 여러 줄을 단일 로그 메시지로 병합 할 수 있습니다. 그리고 이것이 포맷팅 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
}
}
이것은 기본적으로 들여 쓰기가 두 칸을 초과하는 모든 줄 (또는 </entry>
/ xmllint가 기본적으로 두 칸 들여 쓰기를하는 경우)이 이전 줄에 속 한다고 말합니다 . 이것은 또한 문자 데이터에 개행 문자가 포함되어서는 안되고 ( tr
쉘로 묶여) XML이 정규화되어야 함을 의미합니다 (xmllint)