본문 바로가기
제품/ELK

[Logstash] 멀티라인 이벤트 처리하기

by 헬로웬디 2024. 8. 12.

안녕하세요. 웬디입니다.

오늘은 로그스태시에서 멀티라인 이벤트를 처리하는 방법에 대해 알아보겠습니다.

 

Java stack traces는 로그는 여러 줄로 구성됩니다. 예를 들어, 다음과 같은 형식으로 나타날 수 있습니다:

Exception in thread "main" java.lang.NullPointerException
        at com.example.myproject.Book.getTitle(Book.java:16)
        at com.example.myproject.Author.getBookTitles(Author.java:25)
        at com.example.myproject.Bootstrap.main(Bootstrap.java:14)

 

또는 다음과 같은 JSON 형식의 이벤트도 있습니다.

{
  "source": "tsdb",
  "metric": "proc.stat.cpu_rate",
  "mount": null,
  "type": "user",
  "value": 28.15821636325676
},
{
  "source": "tsdb",
  "metric": "proc.stat.cpu_rate",
  "mount": null,
  "type": "system",
  "value": 18.6231479517115
},
{
  "source": "tsdb",
  "metric": "proc.stat.cpu_rate",
  "mount": null,
  "type": "nice",
  "value": 23.333945326513415
}

 

이렇게 여러 줄로 작성된 로그나 이벤트를 처리하려면, 모든 줄을 하나의 이벤트로 병합하는 과정이 필요합니다. 로그스태시는 이런 멀티라인 이벤트를 처리하기 위해 multiline codec plugin을 지원하는데, 그 사용법에 대해 알아봅니다.

 

우선 multiline codec은 input plugin에서 호출할 수 있으며, 다음과 같은 형식을 가집니다.

    input {
      stdin {
        codec => multiline {
          pattern => "pattern, a regexp"
          negate => "true" or "false"
          what => "previous" or "next"
        }
      }
    }

 

  • pattern : 정규 표현식을 사용하여 패턴을 정의합니다.
  • negate : 패턴 일치를 반전시킬지 여부를 결정합니다.
  • what : 패턴에 일치할 경우, 해당 이벤트를 이전 (previous) 이벤트에 병합할지 또는 다음 (next) 이벤트에 병합할지를 결정합니다. 

 

그럼 위에서 예로 든 JSON 이벤트를 로그스태시로 구문 분석해 보겠습니다. 각 JSON 이벤트는 '{'로 시작하므로, '{'로 시작하지 않는 라인은 이전 라인과 병합하여 하나의 이벤트로 생성합니다.

  codec => multiline {
    pattern => "^\{"
    negate => true
    what => "previous"
  }

 

먼저 JSON 이벤트를 blog.json으로 저장해주세요. 그리고 위에서 구현한 multiline codec을 input 플러그인에 작성합니다. 

input {
  file {
    path => "/usr/share/logstash/blog.json"
    start_position => "beginning"
    sincedb_path => "/dev/null"  # For testing purposes; ensures re-reading of the file
    codec => multiline {
      pattern => "^\{"
      negate => true
      what => "previous"
    }
  }
}

filter {
  json {
    source => "message"  # The field containing the JSON data
  }
}

output {
  stdout { codec => rubydebug }
}

 

결과

이벤트가 성공적으로 구문 분석이 되었습니다. 

{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 3,
      "relation": "eq"
    },
    "max_score": 1,
    "hits": [
      {
        "_index": ".ds-logs-endpoint.skybeam-default-2024.08.10-000001",
        "_id": "YDppO5EBCbUdfJ_uinJU",
        "_score": 1,
        "_source": {
          "source": "tsdb",
          "type": "user",
          "mount": null,
          "metric": "proc.stat.cpu_rate",
          "value": 28.15821636325676
        }
      },
      {
        "_index": ".ds-logs-endpoint.skybeam-default-2024.08.10-000001",
        "_id": "XjppO5EBCbUdfJ_uinJU",
        "_score": 1,
        "_source": {
          "source": "tsdb",
          "type": "system",
          "mount": null,
          "metric": "proc.stat.cpu_rate",
          "value": 18.6231479517115
        }
      },
      {
        "_index": ".ds-logs-endpoint.skybeam-default-2024.08.10-000001",
        "_id": "XzppO5EBCbUdfJ_uinJU",
        "_score": 1,
        "_source": {
          "source": "tsdb",
          "type": "nice",
          "mount": null,
          "metric": "proc.stat.cpu_rate",
          "value": 23.333945326513415
        }
      }
    ]
  }
}