如何用Ruby+Logstash修改Item标签外的XML objectMessage内容
需求与解决方案
问题背景
我是Ruby新手,现有如下格式的XML日志文件:
<QuerySiteInformation> xmlns="http://www.abc-dd-cfff.com/aws/elastic/azure" <Site> <id>abc-cde-fvvvv</id> <objectMessage>Internal> message shown here in multiple lines</objectMessage> <Item> <id>e5753ead-d202-451e-92cc-ea49d0a6bdf5</id> <code>67448833344443</code> <objectMessage>Internal> message shown here in multiple lines</objectMessage> <reference>/</reference> </Item> </Site> <SiteInteraction> <InteractionItem> <Location> <id>8496940--2842047577555</id> <objectMessage>Internal> message shown here in multiple lines</objectMessage> </Location> </InteractionItem> <objectMessage>Internal> message shown here in multiple lines</objectMessage> </SiteInteraction> </QuerySiteInformation>
注:Internal> message shown here in multiple lines 可出现在日志任意位置。
需求:仅当<objectMessage>标签位于<Item>标签外部时,将其内容替换为“MESSAGE HAS BEEN REMOVED”,预期效果如下:
<QuerySiteInformation> xmlns="http://www.abc-dd-cfff.com/aws/elastic/azure" <Site> <id>abc-cde-fvvvv</id> <objectMessage>Internal> MESSAGE HAS BEEN REMOVED</objectMessage> <Item> <id>e5753ead-d202-451e-92cc-ea49d0a6bdf5</id> <code>67448833344443</code> <objectMessage>Internal> message shown here in multiple lines</objectMessage> <reference>/</reference> </Item> </Site> <SiteInteraction> <InteractionItem> <Location> <id>8496940--2842047577555</id> <objectMessage>Internal> MESSAGE HAS BEEN REMOVED</objectMessage> </Location> </InteractionItem> <objectMessage>Internal> MESSAGE HAS BEEN REMOVED</objectMessage> </SiteInteraction> </QuerySiteInformation>
之前的Logstash配置会修改<Item>节点内的标签内容,现在需要实现反向逻辑:
filter { xml { source => "message" target => "xml" store_xml => true remove_field => ["message"] } } filter { ruby { code => ' event.get("[xml][Site][0][Item]").each_with_index do |item, index| event.set("[xml][Site][0][Item][#{index}]", "MESSAGE HAS BEEN REMOVED") end ' } } output { stdout { codec => rubydebug } }
修改后的Logstash配置
要实现只替换<Item>外部的<objectMessage>内容,我们需要遍历XML解析后的结构,跳过<Item>内部的节点,替换其他所有<objectMessage>的值。以下是调整后的配置:
filter { xml { source => "message" target => "xml" store_xml => true remove_field => ["message"] force_array => false # 避免单节点自动转为数组,可根据实际日志调整 } } filter { ruby { code => ' # 递归处理节点,跳过Item内部的objectMessage def process_node(node) return unless node.is_a?(Hash) node.each do |key, value| if key == "Item" # 遇到Item节点直接跳过,不处理内部内容 next elsif key == "objectMessage" # 替换Item外部的objectMessage内容 node[key] = "Internal> MESSAGE HAS BEEN REMOVED" else # 递归处理子哈希节点 process_node(value) if value.is_a?(Hash) # 遍历处理数组类型的子节点 value.each { |item| process_node(item) } if value.is_a?(Array) end end end # 从XML根节点开始处理 process_node(event.get("xml")) ' } } output { stdout { codec => rubydebug } # 若需要输出回XML格式,可启用以下配置 # xml { # source => "xml" # target => "message" # root_element => "QuerySiteInformation" # } }
代码说明
- 递归遍历逻辑:通过
process_node函数遍历整个XML解析后的哈希结构,遇到<Item>节点直接跳过,确保其内部的<objectMessage>不被修改;遇到目标节点则替换内容。 - 数组兼容处理:考虑到日志中可能存在多个同层级节点(比如多个
<Site>),代码会判断值是否为数组,遍历每个元素进行递归处理。 - force_array配置:
xml插件的force_array设为false,避免单个节点被自动转为数组,简化处理逻辑;如果日志存在多个同类型节点,可调整为true适配。
内容的提问来源于stack exchange,提问作者CA-CB-TellMe
相关产品推荐
相关产品推荐

