Logstash Ruby Filter

broccolism·2022년 9월 13일
1

ELK Stack

목록 보기
1/1
post-thumbnail

https://www.elastic.co/guide/en/logstash/current/introduction.html

로그스태시는 오픈소스 데이터 수집 엔진이다. 실시간으로 많은 양의 데이터를 수집할 수 있으며, 수집한 데이터를 원하는 형태로 가공해서 다른 곳으로 내보낼 수 있다. ELK 스택에서는 로그스태시가 수집한 데이터를 가공하여 ElasticSearch 저장소로 내보낸다.

Logstash Filters

데이터를 특정 형태로 가공하기 위해서 로그스태시의 필터를 사용할 수 있다. 예를 들어 로그스태시의 timestamp 타입으로 데이터 타입을 변환하는 date 필터, 외부 웹 서비스나 REST API를 연동할 수 있는 http 필터 등이 있다. 필터는 플러그인 형태로 사용한다. 즉, 필요한 필터만 다운로드받아 사용하게 된다. 글의 제목인 ruby 필터는 루비로 적은 코드를 실행하게 해주는 플러그인이다.

사용할 필터에 대한 정보는 로그스태시의 input, output을 정의하는 설정 파일(예: logstash.conf)에서 함께 정의한다. 루비 코드 역시 로그스태시 설정 파일에 함께 적어주면 된다. 물론 설정 파일이 아닌 다른 파일에 있는 루비 코드를 실행하는 방법도 있다.

Ruby Filter

https://www.elastic.co/guide/en/logstash/current/plugins-filters-ruby.html

inline 루비 코드나 루비 파일을 받아서 루비 코드를 실행하는 필터. 이 둘은 mutually exclusive하고 사용법도 조금씩 다르다.

별도 파일에 루비 코드를 적는 방식

별도 파일에 루비 코드를 적은 다음, 아래와 같이 파일의 경로 및 파라미터를 적어주면 된다.

filter {
  ruby {
    path => "/etc/logstash/drop_percentage.rb"
    script_params => { "percentage" => 0.9 }
  }
}

Inline 루비 코드를 사용하는 방식

어디에 inline 되는걸까? 바로 로그스태시 설정 파일이다. '루비 필터를 사용할거임!' 이라고 선언하면서 루비 코드도 같이 적는다.

filter {
  ruby {
    code => "event.cancel if rand <= 0.90"
  }
}

filter, ruby 말고도 code라는 옵션을 추가했다. 이 옵션에 들어간 루비 코드는 로그스태시가 input 하나를 받을 때마다 매번 실행된다. 로그스태시 시작 시 단 1번만 실행하고 싶은 코드가 있다면 init 옵션에 적으면 된다. 해당 코드는 plugin register 단계에 실행된다.

함수를 정의하기 적당한 곳: init

code option 에서 함수를 정의하면 처리율(throughput)이 눈에 띄게 떨어집니다. 대신 init option 을 사용하세요. (https://www.elastic.co/guide/en/logstash/7.17/plugins-filters-ruby.html#plugins-filters-ruby-using-inline-script)

코드를 적다보니 뭔가 복잡한 일을 해야 하는 함수가 생겼다고 하자. 그러면 해당 함수가 로그 1개가 들어올 때마다 매번 새롭게 선언 및 정의되는 비효율적인 일이 생긴다. 그런 함수가 있다면 init 옵션에 적는게 좋다. 최초 1번만 실행되기 때문이다.

필요한 루비 모듈 import, 함수 및 클래스 정의, 상수 정의 등을 모두 init에서 하는게 좋다. 아래처럼 init에서 만든 함수와 클래스를 code에서 바로 사용할 수 있다. 상수는 루비의 전역 변수($로 시작하며, 대소문자 관계 없음)로 정의하면 된다. 이렇게 하면 코드도 깔끔해진다.

filter {
  ruby {
    init => "
      require 'json'
      
      class Animal
        attr_accessor :name, :age
        # 이게 없으면 JSON 변환할 때나 `==` 사용 시 멤버 변수에 접근을 할 수 없어서 에러가 난다.
        # (에러 메세지가 꽤 불친절해서 삽질을 조금 했다.)
        
        def initialize(name, age)
          @name = name
          @age = age
        end
        
        def ==(other)
          return name == other.name && age == other.age
        end
      end
      
      def compare_animal(animal1, animal2)
        ...
      end
      
      $SPECIAL_CHAR = '*'
	  ...
    "
    code => "
      animal1 = Animal.new(event.get('[name]'), event.get('[age]'))
      ...
      compare_animal(animal1, animal2)
      event.set('[special]', $SPECIAL_CHAR)
    "
    
  }
}

급하게 루비 필터를 사용할 일이 생겼다면, Ruby Syntax를 보자. 루비 문법 자체가 크게 어렵거나 복잡하지 않아서 잠깐 각 잡고 읽으면 바로 써먹을 수 있다.

profile
코드도 적고 그림도 그리고 글도 씁니다. 넓고 얕은 경험을 쌓고 있습니다.

0개의 댓글