<?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/kr/search-labs/author/dai-sugimori</link>
    </image>
    <link>https://www.elastic.co/kr/search-labs/author/dai-sugimori</link>
    <atom:link href="https://www.elastic.co/kr/search-labs/rss/author/dai-sugimori.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[kr]]></language>
    <lastBuildDate>Tue, 22 Sep 2026 04:43:19 GMT</lastBuildDate>
  <item>
    <title><![CDATA[Logstash의 루비 스크립팅]]></title>
    <description><![CDATA[Logstash 파이프라인에서 고급 데이터 변환을 위한 Logstash Ruby 필터 플러그인에 대해 알아보세요.]]></description>
    <content:encoded><![CDATA[<p>Logstash는 여러 소스에서 데이터를 수집하고 변환하여 선택한 대상으로 전송하는 데이터 처리 파이프라인입니다. 필터 플러그인은 이 프로세스의 핵심으로, 데이터가 파이프라인을 통과할 때 특정 작업을 수행합니다.</p><p>Logstash에는 데이터 구문 분석, 보강, 수정과 같은 일반적인 작업을 위한 몇 가지 기본 제공 필터가 포함되어 있습니다. 그러나 때로는 이러한 표준 필터가 제공할 수 있는 범위를 넘어서는 사용자 지정 로직이 필요한 시나리오가 발생할 수 있습니다. 바로 이 부분에서 <a href="https://www.elastic.co/docs/reference/logstash/plugins/plugins-filters-ruby">루비 필터 플러그인이</a> 등장합니다.</p><p><strong>Ruby 필터 플러그인을 사용하면 Logstash 파이프라인 내에서 직접 사용자 정의 Ruby 코드를 실행할 수 있습니다.</strong> 표준 필터로 충분하지 않은 경우, 루비 필터를 사용하면 복잡한 데이터 변환을 처리하고, 사용자 지정 비즈니스 로직을 구현하거나, 외부 시스템과 통합할 수 있습니다.</p><p>이 블로그에서는 기본 사용법부터 고급 사용법까지 루비 필터를 사용하는 방법을 살펴봅니다.</p><h2>루비 필터는 언제 사용해야 하나요?</h2><p>Elastic의 컨설팅 아키텍트로서, 요즘은 최신 데이터 처리 엔진이 아니지만 데이터 처리 파이프라인에 Logstash를 사용하는 고객들을 자주 보게 됩니다. 복잡한 데이터 조작이나 사용자 지정 로직을 처리할 때 표준 필터의 한계로 인해 어려움을 겪는 경우가 많습니다. 이러한 경우 루비 필터를 사용하면 이러한 문제를 극복하는 데 도움이 될 수 있습니다.</p><p>Ruby 필터는 표준 Logstash 필터로 특정 요구 사항을 충족할 수 없을 때 유용합니다. 다음은 몇 가지 일반적인 사용 사례입니다:</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>루비 필터의 작동 원리를 이해하기 위해 간단한 예제부터 살펴보겠습니다.</p><h3>루비 필터 구성하기</h3><p>Logstash 파이프라인을 생성할 때, 구성 파일을 <code>/etc/logstash/conf.d</code> 디렉터리에 배치해야 합니다. 또는 Logstash를 수동으로 부팅할 때 <code>-f</code> 옵션을 사용하여 구성 파일의 경로를 지정하면 파이프라인을 쉽게 실험할 수 있습니다.</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의 필터 단계에서 생성된 모든 추가 필드가 포함되어 있습니다. <code>event.get()</code> 및 <code>event.set()</code> 과 같은 Logstash 이벤트 API를 통해 이러한 필드에 액세스할 수 있습니다. 이 예제 코드에서 <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의 해시 객체가 아닙니다. 이벤트 API에 대해 자세히 알아보려면 <a href="https://www.elastic.co/docs/reference/logstash/event-api">이 공식 문서를</a> 확인하세요.</p><h3>루비 스크립트 외부화</h3><p>간단한 변환의 경우 인라인 루비 코드가 편리합니다. 그러나 복잡한 로직이나 재사용 가능한 함수의 경우 코드를 외부 Ruby 스크립트로 옮기는 것이 좋습니다. 이렇게 하면 유지보수성이 향상되고 Logstash 파이프라인 구성이 깔끔하게 유지됩니다.</p><p>먼저 루비 스크립트를 만들어 <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> 옵션을 사용하여 스크립트를 참조하도록 루비 필터 구성을 설정합니다. 이렇게 하면 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가 처리하는 핵심 데이터 구조입니다. 배열 및 해시 같은 중첩된 데이터 구조를 포함하여 다양한 필드를 포함할 수 있습니다. 루비 필터를 사용하면 이러한 중첩 구조를 쉽게 조작할 수 있습니다.</p><p>루비 필터는 해시 및 배열과 같은 중첩된 데이터 구조를 처리할 수 있으므로 이러한 구조 내에서 필드를 수정하거나 추가할 수 있습니다. 이는 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 객체가 포함되어 있습니다. 루비 필터는 새로운 키-값 쌍을 추가하여 중첩된 데이터를 수정합니다. 중첩된 데이터에 대한 이러한 유형의 조작은 표준 Logstash 필터로는 불가능하므로, 복잡한 데이터 구조에 편리한 옵션으로 Ruby 필터를 사용할 수 있습니다.</p><h3>단일 이벤트를 여러 이벤트로 분할</h3><p>루비 필터를 사용하여 단일 이벤트를 여러 개의 이벤트로 분할할 수도 있습니다. 여러 항목이 포함된 단일 이벤트가 있고 각 항목에 대해 별도의 이벤트를 만들려는 경우에 유용합니다.</p><p>Elasticsearch의 수집 파이프라인이나 Beats/Elastic Agent의 프로세서 모두 이벤트 분할을 지원하지 않습니다. 이것은 Logstash의 가장 강력한 사용 사례 중 하나입니다.</p><h4>분할 필터 사용</h4><p><code>split</code> 필터를 사용하여 지정된 필드를 기준으로 이벤트를 여러 개의 이벤트로 분할할 수 있습니다. 그러나 분할 중에 추가 변환이나 로직을 수행해야 하는 경우 분할 필터와 함께 루비 필터를 사용할 수 있습니다.</p><p>다음 예시에서는 한 줄의 XML 텍스트로 된 RSS 피드가 있습니다. 여기에는 여러 <code>&lt;item&gt;</code> 요소가 포함되어 있습니다. 루비 필터는 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> 필터는 불필요한 필드를 제거하는 데 사용할 수 있습니다. 그러나 분할 중에 추가 변환이나 로직을 수행해야 하는 경우 루비 필터를 사용할 수 있습니다.</p><h4>인라인 루비 스크립트 사용</h4><p>또한 인라인 루비 스크립트를 사용하여 <code>event.clone</code> 메서드와 <code>new_event_block variable</code>, 예: <code>new_event_block.call(new_event)</code> 를 사용하여 단일 이벤트를 여러 개의 이벤트로 분할할 수도 있습니다. 이렇게 하면 데이터를 보존하면서 원래 이벤트를 기반으로 새 이벤트를 만들 수 있습니다.</p><p>다음은 루비 필터를 사용하여 단일 이벤트를 여러 개의 이벤트로 분할하는 방법의 예시입니다. 입력 및 출력은 이전 예제와 동일합니다.</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>외부 루비 스크립트 사용</h4><p>외부 루비 스크립트를 사용하여 단일 이벤트를 여러 개의 이벤트로 분할할 수도 있습니다.</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>루비 스크립트는 <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 실행 입력 플러그인을 사용하면 외부 명령을 실행할 수 있으며, 그 출력은 Logstash의 이벤트가 됩니다. 명령의 출력은 이벤트의 <code>message</code> 필드에 저장됩니다.</p><p>일반적으로 시스템 명령의 출력은 사람이 읽을 수 있지만, Logstash가 쉽게 구문 분석할 수 있는 JSON이나 기타 형식으로 구조화되어 있지 않습니다. 이를 처리하기 위해 루비 필터를 사용하여 출력을 구문 분석하고 거기서 정보를 추출할 수 있습니다.</p><p>다음은 <code>exec</code> 입력 플러그인을 사용하여 유닉스 계열 시스템에서 실행 중인 모든 프로세스를 나열하는 <code>ps -ef</code> 명령을 실행하는 예제입니다. 출력은 루비 필터로 파싱되어 각 프로세스에 대한 관련 정보를 추출합니다.</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> 명령을 실행합니다. 루비 필터는 출력을 처리하여 UID, PID, PPID, CPU 사용량(C), 시작 시간(STIME), TTY, 총 CPU 시간(TIME), 실행된 명령(CMD) 등 관련 필드를 추출합니다. 제 macOS 환경에서는 잘 작동하지만 시스템의 <code>ps -ef</code> 명령의 출력 형식과 일치하도록 정규식 패턴을 조정해야 할 수도 있습니다.</p><h3>기본 제공 라이브러리 사용</h3><p>루비 필터 플러그인을 사용하면 내장된 루비 라이브러리를 사용할 수 있어 다양한 작업에 매우 유용할 수 있습니다. 예를 들어 <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 필터 스크립트의 시작 부분에 <code>require</code> 문을 사용할 수 있도록 Ruby 코드를 외부화해야 합니다. 이렇게 하면 라이브러리가 한 번 로드되고 스크립트에서 사용할 수 있게 됩니다.</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 파이프라인의 기능을 사용자 정의하고 확장할 수 있습니다. 이 글에서는 루비 필터 사용의 기본 사항을 다루고 고급 사용 예제를 제공했습니다.</p><p>루비 필터를 활용하면 사용자 지정 로직이나 고급 조작이 필요한 복잡한 데이터 처리 작업을 처리할 수 있습니다. 중첩된 데이터 구조로 작업하거나 이벤트를 분할하거나 복잡한/비구조화 텍스트를 구문 분석하여 구조화된 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>