<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title><![CDATA[Dai Sugimori - Elasticsearch Labs]]></title>
    <description><![CDATA[Articles and tutorials from the Search team at Elastic]]></description>
    <copyright><![CDATA[© 2026. Elasticsearch B.V. All Rights Reserved]]></copyright>
    <image>
      <title><![CDATA[Dai Sugimori - Elasticsearch Labs]]></title>
      <url>https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1121c0bf0e8a6e65/6a88da6340a1841030ef456f/search-labs-thumbnail.png</url>
      <link>https://www.elastic.co/cn/search-labs/author/dai-sugimori</link>
    </image>
    <link>https://www.elastic.co/cn/search-labs/author/dai-sugimori</link>
    <atom:link href="https://www.elastic.co/cn/search-labs/rss/author/dai-sugimori.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[cn]]></language>
    <lastBuildDate>Tue, 29 Sep 2026 10:30:44 GMT</lastBuildDate>
  <item>
    <title><![CDATA[在 Logstash 中使用 Ruby 脚本]]></title>
    <description><![CDATA[了解 Logstash Ruby 过滤器插件，以便在 Logstash 管道中进行高级数据转换。]]></description>
    <content:encoded><![CDATA[<p>Logstash 是一个数据处理管道，可从多个来源摄取数据，对其进行转换，然后发送到您选择的目的地。过滤器插件是这一过程的关键；当数据通过管道时，它们会对数据执行特定操作。</p><p>Logstash 包含多个内置过滤器，可用于解析、丰富和修改数据等常见任务。但有时，您会遇到需要自定义逻辑的情况，这超出了这些标准过滤器所能提供的范围。这就是<a href="https://www.elastic.co/docs/reference/logstash/plugins/plugins-filters-ruby">Ruby 过滤器插件的</a>用武之地。</p><p><strong>Ruby 过滤器插件允许您在 Logstash 管道中直接执行自定义 Ruby 代码。</strong>如果标准过滤器还不够用，Ruby 过滤器还能让您处理复杂的数据转换、实施自定义业务逻辑或与外部系统集成。</p><p>在本博客中，我们将探讨如何从基础到高级使用 Ruby 过滤器。</p><h2>何时使用 Ruby 过滤器？</h2><p>作为 Elastic 的咨询架构师，我经常看到客户在数据处理管道中使用 Logstash，尽管现在它还不是最先进的数据处理引擎。当涉及到复杂的数据操作或自定义逻辑时，他们往往会因为标准过滤器的局限性而苦恼。在这种情况下，Ruby 过滤器可以帮助克服这些挑战。</p><p>当标准 Logstash 过滤器无法满足您的特定要求时，Ruby 过滤器就会派上用场。下面是一些常见的使用案例：</p><ul><li><p><strong>深度嵌套数据操作</strong>：修改复杂的 JSON 结构、数组中的数组或根据内容动态重组数据</p></li><li><p><strong>高级字符串处理</strong>：从非结构化文本中解析和提取结构化数据</p></li><li><p><strong>实施复杂的业务逻辑</strong>：创建需要条件逻辑、循环或复杂计算的自定义转换</p></li></ul><h2>基本用法</h2><p>让我们从一个简单的例子开始，了解 Ruby 过滤器是如何工作的。</p><h3>配置 Ruby 过滤器</h3><p>创建 Logstash 管道时，应将配置文件放在<code>/etc/logstash/conf.d</code> 目录中。或者，你也可以使用<code>-f</code> 选项，在手动启动 Logstash 时指定配置文件的路径，这样就可以轻松试验你的管道了。</p>$ ./bin/logstash -f /path/to/your_pipeline.conf<p>配置文件的扩展名应为<code>.conf</code> 。</p><p>要使用 Ruby 过滤器，请在 Logstash 管道配置 (*.conf) 文件的过滤器部分定义<code>ruby</code> 过滤器。下面是一个基本例子：</p>filter {
  ruby {
    code =&gt; "
      event.set('new_field', 'Hello from Ruby!')
    "
  }
}<p>此内联 Ruby 过滤器在 Logstash 配置中定义了一个 Ruby 过滤器实例。<code>code</code> 参数提供了 Logstash 将为该过滤器处理的每个事件执行的内联 Ruby 脚本。在该脚本中，有一个<code>event</code> 变量可用于表示事件本身。事件对象包含发送到 Logstash 的原始数据以及在 Logstash 过滤阶段创建的任何附加字段。您可以通过 Logstash 事件 API 访问这些字段，如<code>event.get()</code> 和<code>event.set()</code> 。在此示例代码中，<code>event.set('new_field', 'Hello from Ruby!')</code> 将名为<code>new_field</code> 的新字段设置为字符串值<code>Hello from Ruby!</code> 。您可以根据需要在<code>code</code> 块中添加任何其他代码。</p><p>请注意，这个<code>event</code> 对象虽然是键值类型的数据容器，但它并不是通常的 Ruby 哈希对象。查看<a href="https://www.elastic.co/docs/reference/logstash/event-api">此官方文档</a>，了解有关事件 API 的更多信息。</p><h3>外部化 Ruby 脚本</h3><p>对于简单的转换，内联 Ruby 代码非常方便。但是，对于复杂的逻辑或可重复使用的功能，建议将代码移到外部 Ruby 脚本中。这样可以提高可维护性，并保持 Logstash 管道配置的整洁。</p><p>首先，创建 Ruby 脚本并将其保存为<code>my_ruby_script.rb</code> 。脚本必须定义一个处理事件的<code>filter</code> 方法。它将一个事件对象作为参数，该对象代表正在处理的当前事件。<code>filter</code> 方法需要返回一个要发射的事件数组。要删除事件，返回空数组。</p><p>例如，下面的脚本读取<code>message</code> 字段，计算其长度，并将结果存储到名为<code>message_length</code> 的新字段中。</p>def register(params)
  # This method is called when the plugin is loaded.
  # You can use it to initialize any instance variables or perform setup tasks.
end

def filter(event)
  message = event.get('message')

  if message
    event.set('message_length', message.length)
  end

  return [event]
end<p>接下来，使用<code>path</code> 选项设置 Ruby 过滤器配置，以引用脚本。这会告诉 Logstash 加载并执行外部脚本。使用外部脚本时，请确保文件存在并具有正确的权限。</p>filter {
  ruby {
    path =&gt; "/path/to/my_ruby_script.rb"
  }
}<p>现在，每个事件都会被传递到<code>my_ruby_script.rb</code> 中的<code>filter</code> 方法，并由它进行处理。</p><p>这种方法能帮助你更有效地管理复杂的逻辑，使测试、调试和重用 Ruby 代码变得更容易。</p><h2>高级用法</h2><p>在本节中，我们将探讨在 Logstash 中使用 Ruby 过滤器的一些高级示例。这些示例将演示如何使用 Ruby 执行数据转换、丰富事件和实现自定义逻辑。</p><h3>操作嵌套数据结构</h3><p>Logstash 事件是 Logstash 处理的核心数据结构。它可以包含各种字段，包括嵌套数据结构，如数组和哈希值。通过 Ruby 过滤器，您可以轻松处理这些嵌套结构。</p><p>Ruby 过滤器可以处理嵌套数据结构，如哈希值和数组，允许你修改或添加这些结构中的字段。这在处理 JSON 等复杂数据格式时非常有用。</p>input {
  generator {
    lines =&gt; [
      '{"nested": {"key1": "value1", "key2": "value2"}}'
    ]
    count =&gt; 1
    codec =&gt; "json"
    ecs_compatibility =&gt; "disabled"
  }
}

filter {
  ruby {
    code =&gt; "
      nested_data = event.get('nested')

      if nested_data.is_a?(Hash)
        nested_data['key3'] = 'value3'
        event.set('nested', nested_data)
      end
    "
  }
}

output {
  stdout { codec =&gt; rubydebug }
}<p>此示例在输入数据中包含一个嵌套 JSON 对象。Ruby 过滤器通过添加新的键值对来修改嵌套数据。标准 Logstash 过滤器无法对嵌套数据进行此类操作，因此 Ruby 过滤器成为复杂数据结构的便捷选择。</p><h3>将单个事件拆分为多个事件</h3><p>Ruby 过滤器还可用于将单个事件分割成多个事件。当您有一个包含项目数组的单个事件，并希望为每个项目创建单独的事件时，这个功能就非常有用。</p><p>请注意，Elasticsearch 的摄取管道和 Beats/Elastic Agent 的处理器都不支持拆分事件。这是 Logstash 最强大的用例之一。</p><h4>带分体式过滤器</h4><p>您可以使用<code>split</code> 过滤器，根据指定字段将一个事件拆分成多个事件。不过，如果需要在分割过程中执行额外的转换或逻辑，可以将 Ruby 过滤器与分割过滤器结合使用。</p><p>在下面的示例中，我们将 RSS 订阅作为一行 XML 文本。它包含多个<code>&lt;item&gt;</code> 元素。Ruby 过滤器用于从 XML 中提取<code>&lt;item&gt;</code> 元素，并将其存储在名为<code>items</code> 的新字段中。然后，根据<code>items</code> 字段，使用拆分过滤器将事件拆分成多个事件。</p>input {
  generator {
    lines =&gt; [
      '&lt;rss version="2.0"&gt;&lt;channel&gt;&lt;title&gt;Sample RSS&lt;/title&gt;&lt;item&gt;&lt;title&gt;Article 1&lt;/title&gt;&lt;link&gt;http://example.com/1&lt;/link&gt;&lt;description&gt;Desc 1&lt;/description&gt;&lt;/item&gt;&lt;item&gt;&lt;title&gt;Article 2&lt;/title&gt;&lt;link&gt;http://example.com/2&lt;/link&gt;&lt;description&gt;Desc 2&lt;/description&gt;&lt;/item&gt;&lt;/channel&gt;&lt;/rss&gt;'
    ]
    count =&gt; 1
    codec =&gt; "plain"
    ecs_compatibility =&gt; "disabled"
  }
}

filter {
  xml {
    source =&gt; "message"
    target =&gt; "rss"
    store_xml =&gt; true
    force_array =&gt; false
  }
  ruby {
    code =&gt; "event.set('items', event.get('[rss][channel][item]')) if event.get('[rss][channel][item]')"
  }
  split {
    field =&gt; "items"
  }
  ruby {
    code =&gt; "
      item = event.get('items')
      event.set('title', item['title']) if item['title']
      event.set('link', item['link']) if item['link']
      event.set('description', item['description']) if item['description']
    "
  }
  mutate {
    remove_field =&gt; ["@timestamp", "@version", "sequence", "host", "event", "message", "rss", "items"]
  }
}

output {
  stdout { codec =&gt; rubydebug }
}<p>输出结果如下</p>{
          "title" =&gt; "Article 1",
           "link" =&gt; "http://example.com/1",
    "description" =&gt; "Desc 1"
}
{
          "title" =&gt; "Article 2",
           "link" =&gt; "http://example.com/2",
    "description" =&gt; "Desc 2"
}<p>您可能已经注意到，在这种情况下，<code>ruby</code> 过滤器并不是必不可少的。<code>split</code> 过滤器可用于根据<code>items</code> 字段将事件拆分成多个事件，而<code>mutate</code> 过滤器可用于删除不必要的字段。不过，如果需要在分割过程中执行额外的转换或逻辑，可以使用 Ruby 过滤器。</p><h4>使用内联 Ruby 脚本</h4><p>您还可以使用内联 Ruby 脚本，通过<code>event.clone</code> 方法和<code>new_event_block variable</code> ，将单个事件拆分成多个事件，如<code>new_event_block.call(new_event)</code> 。这样，您就可以根据原始事件创建新事件，同时保留其数据。</p><p>下面是一个如何使用 Ruby 过滤器将单个事件分割成多个事件的示例。输入和输出与上一个示例相同。</p>filter {
  xml {
    source =&gt; "message"
    target =&gt; "rss"
    store_xml =&gt; true
    force_array =&gt; false
  }
  ruby {
    code =&gt; "
      items = event.get('[rss][channel][item]')
      if items.is_a?(Array)
        items.each do |item|
          new_event = event.clone
          new_event.set('title', item['title'])
          new_event.set('link', item['link'])
          new_event.set('description', item['description'])
          new_event_block.call new_event
        end
        event.cancel
      elsif items.is_a?(Hash)
        event.set('title', items['title'])
        event.set('link', items['link'])
        event.set('description', items['description'])
      end
    "
  }
  mutate {
    remove_field =&gt; ["@timestamp", "@version", "sequence", "host", "event", "message", "rss", "items"]
  }
}<h4>使用外部 Ruby 脚本</h4><p>您还可以使用外部 Ruby 脚本将单个事件分割成多个事件。</p><p>配置文件：</p>filter {
  xml {
    source =&gt; "message"
    target =&gt; "rss"
    store_xml =&gt; true
    force_array =&gt; false
  }
  ruby {
    path =&gt; "path/to/ruby/split_event.rb"
  }
  mutate {
    remove_field =&gt; ["@timestamp", "@version", "sequence", "host", "event", "message", "rss", "items"]
  }
}<p>Ruby 脚本需要外部化为<code>split_event.rb</code> ：</p>def filter(event)
  items = event.get('[rss][channel][item]')
  events = []
  if items.is_a?(Array)
    items.each do |item|
      new_event = event.clone
      new_event.set('title', item['title'])
      new_event.set('link', item['link'])
      new_event.set('description', item['description'])
      events &lt;&lt; new_event
    end
    return events
  elsif items.is_a?(Hash)
    event.set('title', items['title'])
    event.set('link', items['link'])
    event.set('description', items['description'])
    return [event]
  else
    return []
  end
end<p>请记住，<code>filter</code> 方法必须返回一个事件数组。您可以通过克隆传入的事件对象并将其添加到数组中来返回多个事件，也可以将单个事件作为包含一个元素的数组来返回。</p>return events
# or
# return [event]<p>这样就可以将单个事件拆分成多个事件。</p><h3>执行外部命令并解析其输出结果</h3><p>Logstash exec 输入插件允许您执行外部命令，其输出将成为 Logstash 的事件。命令的输出将存储在事件的<code>message</code> 字段中。</p><p>通常情况下，系统命令的输出是人类可读的，但并不是 Logstash 可以轻松解析的 JSON 或其他格式的结构。要处理这种情况，可以使用 Ruby 过滤器解析输出并从中提取信息。</p><p>下面是一个使用<code>exec</code> 输入插件执行<code>ps -ef</code> 命令的示例，该命令可列出类 Unix 系统上所有正在运行的进程。Ruby 过滤器将对输出进行解析，以提取每个进程的相关信息。</p>input {
  exec {
    command =&gt; "ps -ef"
    interval =&gt; 60
  }
}

filter {
  ruby {
    code =&gt; '
      processes = []
      lines = event.get("message").split("\n")  
      lines.each_with_index do |line, index|
        # Skip header line and empty lines
        next if index == 0 || line.strip.empty?
        entry = nil
        
        # Use regex to match the ps -ef output format more flexibly
        # This pattern accounts for variable spacing and different time formats
        if line =~ /^\s*(\S+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\S+)\s+(\S+)\s+([\d:]+\.?\d*)\s+(.+)$/
          uid, pid, ppid, c, stime, tty, time, cmd = $1, $2, $3, $4, $5, $6, $7, $8
          
          entry = {
            "UID" =&gt; uid,
            "PID" =&gt; pid,
            "PPID" =&gt; ppid,
            "C" =&gt; c,
            "STIME" =&gt; stime,
            "TTY" =&gt; tty,
            "TIME" =&gt; time,
            "CMD" =&gt; cmd.strip
          }
        elsif line =~ /^\s*(\S+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(.+)$/
          # Fallback pattern for lines that might not match the exact format
          # Split the remaining part more carefully
          uid, pid, ppid, c, remainder = $1, $2, $3, $4, $5
          
          # Split remainder into STIME, TTY, TIME, CMD
          parts = remainder.strip.split(/\s+/, 4)
          if parts.length &gt;= 4
            stime, tty, time, cmd = parts[0], parts[1], parts[2], parts[3]
            
            entry = {
              "UID" =&gt; uid,
              "PID" =&gt; pid,
              "PPID" =&gt; ppid,
              "C" =&gt; c,
              "STIME" =&gt; stime,
              "TTY" =&gt; tty,
              "TIME" =&gt; time,
              "CMD" =&gt; cmd
            }
          end
        end
        if entry &amp;&amp; entry["UID"] == "0"
          original_line = line.strip
          entry["original_line"] = original_line if original_line.length &gt; 0
          processes.push(entry)
        end
      end
      event.set("processes", processes)
      event.remove("message")
      event.remove("event")
    '
  }
}

output {
  stdout { codec =&gt; rubydebug }
}<p>本例使用<code>exec</code> 输入插件，每 60 秒运行一次<code>ps -ef</code> 命令。Ruby 过滤器处理输出，提取相关字段，如 UID、PID、PPID、CPU 使用率 (C)、开始时间 (STIME)、TTY、CPU 总时间 (TIME) 和执行的命令 (CMD)。它在我的 macOS 环境中运行良好，但你可能需要调整 regex 模式，以匹配你系统中<code>ps -ef</code> 命令的输出格式。</p><h3>使用内置库</h3><p>Ruby 过滤器插件允许您使用内置的 Ruby 库，这对各种任务都非常有用。例如，您可以使用<code>json</code> 库解析 JSON 字符串，或使用<code>date</code> 库处理日期。</p><p>下面是一个使用<code>json</code> 库解析存储在字段中的 JSON 字符串的示例：</p>require 'json'

def filter(event)
  json_string = event.get('message')
  parsed_json = JSON.parse(json_string)
  event.set('parsed_json', parsed_json)
  return [event]
end<p>为了避免每次都需要使用库，您应该将 Ruby 代码外部化，以便在 Ruby 过滤器脚本的开头使用<code>require</code> 语句。这将加载一次库，使其可在脚本中使用。</p><p>要检查环境中哪些库可用，可以在 Ruby 过滤器中运行以下代码，列出内置库：</p>Gem.loaded_specs.sort_by { |name, _| name }.each do |name, spec|
  puts "#{name}: #{spec.version}"
end<p><strong>注意： </strong>Logstash 官方不支持内置库，它们的行为可能会发生变化，或者在未来版本中不可用。使用风险自负。</p><h2>结论</h2><p>Logstash Ruby 过滤器允许您自定义和扩展 Logstash 管道的功能。在本篇文章中，我们介绍了使用 Ruby 过滤器的基础知识，并提供了高级用法示例。</p><p>利用 Ruby 过滤器，您可以处理需要自定义逻辑或高级操作的复杂数据处理任务。无论您是处理嵌套数据结构、拆分事件，还是解析复杂/非结构化文本并将其转换为结构化 JSON，Ruby 过滤器都能灵活满足您的特定需求。</p><p>希望本指南能为您提供探索 Logstash Ruby 过滤器全部潜力的知识和灵感。祝您编写脚本愉快！</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/ruby-scripting-logstash</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/ruby-scripting-logstash</guid>
    <category><![CDATA[索引数据]]></category>
    <category><![CDATA[Ruby]]></category>
    <dc:creator><![CDATA[Dai Sugimori]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt503d18396642e73e/6a17f62aaf47b68527cde121/b1bcd63c033ccbde102c20ba3085f165f9289a71-1600x1000.png" length="0" type="image/png"/>
    <pubDate>Tue, 24 Jun 2025 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>