현재 logstash와 elasticsearch가 사용 사례에 유용한 지 평가하고 있습니다. 내가 가진 것은 형식의 여러 항목을 포함 하는 로그 파일 입니다
<root>
<entry>
<fieldx>...</fieldx>
<fieldy>...</fieldy>
<fieldz>...</fieldz>
...
<fieldarray>
<fielda>...</fielda>
<fielda>...</fielda>
...
</fieldarray>
</entry>
<entry>
...
</entry>
...
<root>
각 entry
요소에는 하나의 로그 이벤트가 포함됩니다. (관심이 있다면 파일은 실제로 템포 작업 표 (아틀라스 식 JIRA 플러그인) 작업 로그 내보내기입니다.)
자체 코덱을 작성하지 않고도 이러한 파일을 여러 로그 이벤트로 변환 할 수 있습니까?
답변
좋아, 나에게 맞는 해결책을 찾았다. 솔루션의 가장 큰 문제는 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)
답변
나는 비슷한 경우가 있었다. 이 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
}
}
이것이 누군가를 도울 수 있기를 바랍니다. 그것을 얻기 위해 오랜 시간이 필요했습니다.