Develop-Study / ElasticSearch-study

0 stars 0 forks source link

6장. 로그스태시 #9

Open seryang opened 2 years ago

seryang commented 2 years ago

6.1 로그스태시 소개

6.2 로그스태시 실행

6.3 파이프라인

filter { { 필터 플러그인 } }

output { { 출력 플러그인 } }

- 파이프라인 구성요소인 입력, 필터, 출력의 내부에 플러그인을 지정할 수 있음
- 용도나 형태에 맞춰 이미 만들어진 수많은 플러그인이 있기 때문에 필요한 기능을 지원하는 플러그인을 검색하여 템플릿에 추가하면 됨

## 6.3.1 입력
- 파이프라인의 가장 앞부분에 위치하며 소스 원본으로부터 데이터를 입력받은 단계
- 직접 대상에 접근해 읽어 들이는 경우도 있지만 서버를 열어놓고 받아들이는 형태의 구성도 가능
- 로그스태시 입력이 받아들일 수 있는 데이터 소스
   - 파일, 통계, 웹, 데이터베이스, 스트림
- 다양한 형태의 데이터를 인식할 수 있고 이를 쉽게 처리하기 위해 다양한 입력 플러그인들이 존재
- ex) 특정 파일은 파일 플러그인을, 실시간 트윗은 트위터 플러그인을 통해 가져올 수 있음

> 자주 사용하는 입력 플러그인

입력 플러그인 | 설명
--| --
file | 리눅스의 tail -f 명령처럼 파일을 스트리밍하여 이벤트를 읽어 들임
syslog | 네트워크를 통해 전달되는 시스로그를 수신
kafka | 카프카의 토픽에서 데이터를 읽어 들임
jdbc | jdbc 드라이버로 지정한 일정마다 쿼리를 실행해 결과를 읽어 들임

- 로그스태시의 플러그인은 대부분 사용법이나 형태가 비슷하므로 파일 플러그인 사용법을 배워두면 다른 입력 플러그인 사용도 크게 어렵지 않음

## 실습 
- 로그스태시가 설치된 config 폴더에 logstash-test.conf라는 파이프라인 설정 파일을 만들어 보자
- 확장자가 꼭 conf일 필요는 없음
- 파이프라인 설정 파일의 위치 역시 크게 중요하지는 않지만 가능하면 프로젝트끼리 묶어두는 것이 소스 관리 차원에서 좋음
- 이제 logstash-test.conf 파일에 파일 입력 플러그인을 적용해보자.
![image](https://user-images.githubusercontent.com/7028121/173599383-9e26c61d-5faa-4601-9d55-dade08df0c77.png)
- 필터 로그인을 사용하지 않음
- 입력으로는 파일 플러그인, 출력으로는 표준 출력을 사용
- 파일 플러그인에는 여러 옵션이 있는데 그중 path는 읽어들일 파일 위치를 결정
- elasticsearch.log 파일을 읽는 것으로 설정했는데, 파일에 로그가 쌓이면 실시간으로 elasticsearch.log 파일의 변경을 감지해 읽어 들임
- start_position은 최초 파일을 발견했을 때 파일을 읽을 위치로, 파일의 시작 부분부터 읽어들일지 끝부분부터 새로운 라인만 읽어들일지 정할 수 있음
- 출력은 표준 출력 플러그인을 사용했기 때문에 입력이 발생하면 모니터에 출력
- 실시간으로 출력되는 elasticsearch.log를 수집하기 위해 먼저 엘라스틱서치를 실행하고 로그스태시를 실행해보자.
`user@AL01978864 logstash-8.2.2 % ./bin/logstash -f ./conf/logstash-text.conf`
![image](https://user-images.githubusercontent.com/7028121/173616515-57c3cf2e-4cdc-4363-bd17-30da0f5c7072.png)
- -f 옵션은 설정 파일이나 폴더를 지정하는 옵션으로 방금 만들었던 logstash-test.conf를 파이프라인 설정에 사용하게 됨
- 입력 플러그인 경로에 적힌 elasticsearch.log 파일이 업데이트될 떄마다 출력 플러그인의 stdout(표준 출력)에 의해 모니터에 위 이미지 같은 로그들이 보이게 됨
- message에 적힌 구문을 분석해 의미 있는 데이터로 변환하는 역할을 필터 플러그인이 해야 하며 이는 로그스태시의 가장 중요한 업무라고 할 수 있음

## 6.3.2 필터
- 입력 플러그인이 받은 데이터를 의미 있는 데이터로 구조화하는 역할을 함
- 로그스태시 필터는 비정형 데이터를 정형화하고 데이터 분석을 위한 구조를 잡아줌
- 필터 역시 플러그인 형태이며 입력과 비슷하게 다양한 필터 플러그인이 존재함

> 자주 사용되는 필터 플러그인

필터 플러그인 | 설명
--| --
grok | grok 패턴을 사용해 메시지를 구조화된 형태로 분석. grok 패턴은 일반적인 정규식과 유사하나, 추가적으로 미리 정의된 패턴이나 필드 이름 설정, 데이터 타입 정의 등을 도와줌
dissect | 간단한 패턴을 사용해 메시지를 구조화된 형태로 분석한다. 정규식을 사용하지 않아 grok에 비해 자유도는 조금 떨어지지만 더 빠른 처리가 가능
mutate | 필드명을 변경하거나 문자열 처리 등 일반적인 가공 함수들을 제공
date | 문자열을 지정한 패턴의 날짜형으로 분석

## 실습

>filter-example.log
```text
user@AL01978864 log % cat filter-example.log
[2020-01-02 14:17] [ID1] 192.10.2.6 9500 [INFO] - connected.
[2020/01/02 14:19:25] [ID2] 218.25.32.70 1070 [warn] - busy server.

logstash-text.conf


input {
file {
path => "/Users/user/ELK/logstash/log/filter-example.log"
start_position => "beginning"
sincedb_path => "nul"
}
}

output { stdout { } }

- filter-example.log 파일을 입력으로 받는 파이프라인 설정
- 파일 플러그인에 sincedb_path라는 옵션이 추가됨
- start_position은 로그스태시가 새로운 파일을 인식했을 때 파일ㅇ르 어디서부터 읽을 것인지에 대한 옵션으로 beginning과 end 중 하나를 선택할 수 있음
- beginning은 파일의 처음, end는 파일의 끝을 가리킴
- sincedb 데이터베이스 파일은 파일을 어디까지 읽었는지 기록하는 파일. nul로 지정하면 sincedb 파일을 만들지 않기 때문에 이전 파일을 읽었던 기록이 없어서 매번 로그스태시를 실행할 떄마다 파일을 처음부터 읽게 됨

```text
user@AL01978864 logstash % ./bin/logstash -f ./config/logstash-test.conf --log.level error
{
       "message" => "[2020-01-02 14:17] [ID1] 192.10.2.6 9500 [INFO] - connected.",
    "@timestamp" => 2022-06-14T15:41:43.452187Z,
      "@version" => "1",
           "log" => {
        "file" => {
            "path" => "/Users/user/ELK/logstash/log/filter-example.log"
        }
    },
         "event" => {
        "original" => "[2020-01-02 14:17] [ID1] 192.10.2.6 9500 [INFO] - connected."
    },
          "host" => {
        "name" => "AL01978864.local"
    }
}
{
       "message" => "[2020/01/02 14:19:25] [ID2] 218.25.32.70 1070 [warn] - busy server.",
    "@timestamp" => 2022-06-14T15:41:43.480294Z,
      "@version" => "1",
           "log" => {
        "file" => {
            "path" => "/Users/user/ELK/logstash/log/filter-example.log"
        }
    },
         "event" => {
        "original" => "[2020/01/02 14:19:25] [ID2] 218.25.32.70 1070 [warn] - busy server."
    },
          "host" => {
        "name" => "AL01978864.local"
    }
}
{
       "message" => "",
    "@timestamp" => 2022-06-14T15:41:43.481134Z,
      "@version" => "1",
           "log" => {
        "file" => {
            "path" => "/Users/user/ELK/logstash/log/filter-example.log"
        }
    },
         "event" => {
        "original" => ""
    },
          "host" => {
        "name" => "AL01978864.local"
    }
}

6.3.2.1 문자열 자르기

user@AL01978864 logstash % cat config/logstash-test.conf
input {
    file {
    path => "/Users/user/ELK/logstash/log/filter-example.log"
    start_position => "beginning"
    sincedb_path => "nul"
    }
}

filter {
    mutate {
    split => { "message" => " " }
   }
}

output {
    stdout { }
}

mutate 옵션

mutate 옵션 설명
split 쉼표(,) 같은 구분 문자를 기준으로 문자열을 배열로 나눔
rename 필드 이름을 바꿈
replace 해당 필드값을 특정 값으로 바꿈
uppercase 문자열을대문자로
lowercase 문자열을 소문자로
join 배열을 쉼표(,) 같은 구분 문자로 연결해 하나의 문자열로 합침
gsub 정규식이 일치하는 항목을 다른 문자열로 대체
merge 특정 필드를 다른 필드에 포함시킴
coerce null인 필드값에 기본값을 넣어줌
strip 필드값의 좌우 공백을 제거

filter 를 적용한 결과

{
"host" => {
"name" => "AL01978864.local"
},
"message" => [
[0] "[2020-01-02",
[1] "14:17]",
[2] "[ID1]",
[3] "192.10.2.6",
[4] "9500",
[5] "[INFO]",
[6] "-",
[7] "connected."
],
"@version" => "1",
"event" => {
"original" => "[2020-01-02 14:17] [ID1] 192.10.2.6 9500 [INFO] - connected."
},
"log" => {
"file" => {
"path" => "/Users/user/ELK/logstash/log/filter-example.log"
}
},
"@timestamp" => 2022-06-14T15:59:14.113534Z
}
{
"host" => {
"name" => "AL01978864.local"
},
"message" => [
[0] "[2020/01/02",
[1] "14:19:25]",
[2] "[ID2]",
[3] "218.25.32.70",
[4] "1070",
[5] "[warn]",
[6] "-",
[7] "busy",
[8] "server."
],
"@version" => "1",
"event" => {
"original" => "[2020/01/02 14:19:25] [ID2] 218.25.32.70 1070 [warn] - busy server."
},
"log" => {
"file" => {
"path" => "/Users/user/ELK/logstash/log/filter-example.log"
}
},
"@timestamp" => 2022-06-14T15:59:14.140492Z
}
  • message 필드 문자열이 공백을 기준으로 구분되어 배열 형태의 데이터가 되었음
  • 구분된 문자들은 '필드명[숫자['와 같이 접근할 수 있음
  • 예를 들어 ID를 가리키는 필드는 message[2]와 같음

logstash-test.conf 를 다음과 같이 수정


user@AL01978864 config % cat logstash-test.conf
input {
file {
path => "/Users/user/ELK/logstash/log/filter-example.log"
start_position => "beginning"
sincedb_path => "/dev/null"
}
}

filter { mutate { split => { "message" => " " } add_field => { "id" => "%{[message][2]}" } remove_field => "message" } }

output { stdout { } }

- add_field는 필드를 새로 만들어서 추가하는 옵션
- id라는 필드를 새로 생성하고 앞에서 split으로 분리되어 있는 message 필드의 배열 중에서 message[2] 의 데이터를 넣음
- remove_field 는 특정 필드를 삭제하는 옵션
- add_field와 remove_field는 mutate의 특별한 옵션은 아니고 모든 필터 플러그인에서 사용할 수 있는 옵션

> 필터 플러그인 공통 옵션

공통옵션 | 설명
--| --
add_field | 새로운 필드 추가
add_tag | 성공한 이벤트에 태그를 추가
enable_metric | 메트릭 로깅을 활성화하거나 비활성화할 수 있음. 기본적으로 활성화되어 있으며, 수집된 데이터는 로그스태시 모니터링에서 해당 필터의 성능을 분석할 때 사용
id | 플러그인의 아이디를 설정. 모니터링 시 아이디를 이용해 특정 플러그인을 쉽게 찾을 수 있음
remove_field | 필드 삭제
remove_tage | 성공한 이벤트에 붙은 태그를 제거

> mutate 플러그인의 split, add_field, remove_field 옵션 사용 결과
```text
{
            "id" => "[ID1]",
          "host" => {
        "name" => "AL01978864.local"
    },
    "@timestamp" => 2022-06-14T16:32:11.702740Z,
           "log" => {
        "file" => {
            "path" => "/Users/user/ELK/logstash/log/filter-example.log"
        }
    },
         "event" => {
        "original" => "[2020-01-02 14:17] [ID1] 192.10.2.6 9500 [INFO] - connected."
    },
      "@version" => "1"
}
{
            "id" => "[ID2]",
          "host" => {
        "name" => "AL01978864.local"
    },
    "@timestamp" => 2022-06-14T16:32:11.776895Z,
           "log" => {
        "file" => {
            "path" => "/Users/user/ELK/logstash/log/filter-example.log"
        }
    },
         "event" => {
        "original" => "[2020/01/02 14:19:25] [ID2] 218.25.32.70 1070 [warn] - busy server.."
    },
      "@version" => "1"
}

6.3.2.2 dissect를 이용한 문자열 파싱

filter { dissect { mapping => {"message" => "[%{timestamp}] [%{id}] %{ip} %{port} [%{level}] - %{message}."} } }

output { stdout { } }

- dissect 플러그인의 mapping 옵션에 구분자 형태를 정의하고 필드를 구분함
- %{필드명}으로 작성하면 중괄호 안의 필드명으로 새로운 필드가 만들어짐
- %{} 외의 문자들은 모두 구분자 역할을 함
- 필드를 구분하는 문자로는 ' ' (공백) , '-' , '[', ']' 가 있음
- mesaage 필드의 문자열이 "[timestamp] [id] ip port [level] - msg" 형식으로 구성되어 있다면 구분자에 맞춰 문자열을 자르고 생성된 형태에 맞춰 필드를 구분

```text
{
    "@timestamp" => 2022-06-14T17:07:37.484121Z,
       "message" => "[2020/01/02 14:19:25]   [ID2] 218.25.32.70 1070 [warn] - busy server.",
          "host" => {
        "name" => "AL01978864.local"
    },
           "log" => {
        "file" => {
            "path" => "/Users/user/ELK/logstash/log/filter-example.log"
        }
    },
      "@version" => "1",
         "event" => {
        "original" => "[2020/01/02 14:19:25]   [ID2] 218.25.32.70 1070 [warn] - busy server."
    },
          "tags" => [
        [0] "_dissectfailure"
    ]
}

filter { dissect { mapping => {"message" => "[%{timestamp}]%{?->} [%{id}] %{ip} %{+ip} [%{?level}] - %{}."} } }

output { stdout { } }

- -> 기호는 공백을 무시
- %{필드명->}을 입력하면 공백이 몇 칸이든 하나의 공백으로 인식
- %{?필드명} 혹은 %{}를 입력하면 그 필드명은 결과에 포함하지 않음
- 그래서 %{?->}라고 입력하면 공백들을 하나의 필드로 만든 다음 무시하게 됨
- 기존 level이나 message 필드로 모두 무시하게 됨
- %{+필드명}을 작성하면 여러 개의 필드를 하나의 필드로 합쳐서 표현 (기존 port 필드가 ip 필드에 합쳐짐)
- [timestamp]와 [id] 사이에 있던 공백을 필드로 만든 후 공백 처리를 했음
```text
{
      "@version" => "1",
       "message" => "[2020/01/02 14:19:25]   [ID2] 218.25.32.70 1070 [warn] - busy server.",
            "id" => "ID2",
            "ip" => "218.25.32.70 1070",
     "timestamp" => "2020/01/02 14:19:25",
    "@timestamp" => 2022-06-14T17:14:19.413067Z,
           "log" => {
        "file" => {
            "path" => "/Users/user/ELK/logstash/log/filter-example.log"
        }
    },
          "host" => {
        "name" => "AL01978864.local"
    },
         "event" => {
        "original" => "[2020/01/02 14:19:25]   [ID2] 218.25.32.70 1070 [warn] - busy server."
    }
}
{
      "@version" => "1",
       "message" => "[2020-01-02 14:17] [ID1] 192.10.2.6 9500 [INFO] - connected.",
            "id" => "ID1",
            "ip" => "192.10.2.6 9500",
     "timestamp" => "2020-01-02 14:17",
    "@timestamp" => 2022-06-14T17:14:19.382382Z,
           "log" => {
        "file" => {
            "path" => "/Users/user/ELK/logstash/log/filter-example.log"
        }
    },
          "host" => {
        "name" => "AL01978864.local"
    },
         "event" => {
        "original" => "[2020-01-02 14:17] [ID1] 192.10.2.6 9500 [INFO] - connected."
    }
}

grok을 이용한 문자열 파싱

grok에서 지원하는 패턴

패턴명 설명
NUMBER 십진수를 인식. 부호와 소수점을 포함할 수 있음
SPACE 스페이스, 탭 등 하나 이상의 공백을 인식
URI URI를 인식. 프로토콜, 인증 정보, 호스트, 경로, 파라미터를 포함할 수 있음
IP IP 주소를 인식. IPV4나 IPV6를 모두 인식할 수 있음
SYSLOGBASE 시스로그의 일반적인 포맷에서 타임스탬프, 중요도, 호스트, 프로세스 정보까지 메시지 외의 헤더 부분을 인식
TIMESTAMP_ISO8601 ISO8601 포맷의 타임스탬프를 인식. 2020-01-01T12:00:00+09:00과 같은 형태이며, 타임존까지 정확한 정보를 기록하고 로그에선 많이 쓰이는 날짜 포맷이기에 grok표현식을 작성할 때도 유용
DATA 이 패턴의 직전 패턴부터 다음 패턴 사이를 모두 인식. 특별히 인식하고자 하는 값의 유형을 신경 쓸 필요가 없으므로 특별히 값이 검증될 필요가 없다면 가장 많이 쓰이는 패턴 중 하나임
GREEDYDATA DATA 타입과 동일하나, 표현식의 가장 뒤에 위치시킬 경우 해당 위치부터 이벤트의 끝까지를 값으로 인식

자주 쓰는 로그스태시 패턴

input {
    file {
    path => "/Users/user/ELK/logstash/log/filter-example.log"
    start_position => "beginning"
    sincedb_path => "/dev/null"
    }
}

filter {
    grok {
    match => { "message" => "\[%{TIMESTAMP_ISO8601:timestamp}\] [ ]*\[%{DATA:id}] %{IP:ip} %{NUMBER:port:int} \[%{LOGLEVEL:level}\] \- %{DATA.msg}\."}
    }
}

output {
    stdout { }
}
{
       "message" => "[2020-01-02 14:17] [ID1] 192.10.2.6 9500 [INFO] - connected.",
          "port" => 9500,
            "ip" => "192.10.2.6",
     "timestamp" => "2020-01-02 14:17",
         "level" => "INFO",
    "@timestamp" => 2022-06-14T17:29:53.303814Z,
            "id" => "ID1",
      "@version" => "1",
         "event" => {
        "original" => "[2020-01-02 14:17] [ID1] 192.10.2.6 9500 [INFO] - connected."
    },
          "host" => {
        "name" => "AL01978864.local"
    },
           "log" => {
        "file" => {
            "path" => "/Users/user/ELK/logstash/log/filter-example.log"
        }
    }
}
{
    "@timestamp" => 2022-06-14T17:29:53.331826Z,
       "message" => "[2020/01/02 14:19:25]   [ID2] 218.25.32.70 1070 [warn] - busy server.",
      "@version" => "1",
         "event" => {
        "original" => "[2020/01/02 14:19:25]   [ID2] 218.25.32.70 1070 [warn] - busy server."
    },
          "host" => {
        "name" => "AL01978864.local"
    },
           "log" => {
        "file" => {
            "path" => "/Users/user/ELK/logstash/log/filter-example.log"
        }
    },
          "tags" => [
        [0] "_grokparsefailure"
    ]
}

filter { grok { pattern_definitions => { "MY_TIMESTAMP" => "%{YEAR}[/-]%{MONTHNUM}[/-]%{MONTHDAY}[/-][T ]%{HOUR}:?%{MINUTE}(?::?%{SECOND})?%{ISO8601_TIMEZONE}?" } match => { "message" => "[%{MY_TIMESTAMP:timestamp}] [ ]*[%{DATA:id}] %{IP:ip} %{NUMBER:port:int} [%{LOGLEVEL:level}] - %{DATA.msg}."} } }

output { stdout { } }

- grok 플러그인에서 pattern_definitions 옵션을 추가하고 원하는 형식의 정규 표현식을 작성하면 된다. 
- 기존의 TIMESTAMP_ISO8601 포맷은 연-월-일 형태만 지원했는데, 이 패턴을 복사해와서 연/월/일 형태까지 지원하도록 추가함
- 정규 표현식에서는 [/-]는 '/', '-' 기호 모두 패턴에 맞는다고 판단

```text
{
       "message" => "[2020/01/02 14:19:25]   [ID2] 218.25.32.70 1070 [warn] - busy server.",
          "tags" => [
        [0] "_grokparsefailure"
    ],
          "host" => {
        "name" => "AL01978864.local"
    },
    "@timestamp" => 2022-06-14T17:37:07.595255Z,
      "@version" => "1",
         "event" => {
        "original" => "[2020/01/02 14:19:25]   [ID2] 218.25.32.70 1070 [warn] - busy server."
    },
           "log" => {
        "file" => {
            "path" => "/Users/user/ELK/logstash/log/filter-example.log"
        }
    }
}
{
       "message" => "[2020-01-02 14:17] [ID1] 192.10.2.6 9500 [INFO] - connected.",
          "tags" => [
        [0] "_grokparsefailure"
    ],
          "host" => {
        "name" => "AL01978864.local"
    },
    "@timestamp" => 2022-06-14T17:37:07.567033Z,
      "@version" => "1",
         "event" => {
        "original" => "[2020-01-02 14:17] [ID1] 192.10.2.6 9500 [INFO] - connected."
    },
           "log" => {
        "file" => {
            "path" => "/Users/user/ELK/logstash/log/filter-example.log"
        }
    }
}

6.3.2.4 대소문자 변경

input {
    file {
    path => "/Users/user/ELK/logstash/log/filter-example.log"
    start_position => "beginning"
    sincedb_path => "/dev/null"
    }
}

filter {
    dissect {
    mapping => {"message" => "[%{timestamp}]%{?->}[%{?id}] %{?ip} %{?port} [%{level}] - %{?msg}."}
    }
    mutate {
    uppercase => ["level"]
    }
}

output {
    stdout { }
}

6.3.2.5 날짜/시간 문자열 분석

input {
    file {
    path => "/Users/user/ELK/logstash/log/filter-example.log"
    start_position => "beginning"
    sincedb_path => "/dev/null"
    }
}

filter {
    dissect {
    mapping => {"message" => "[%{timestamp}]%{?->}[%{?id}] %{?ip} %{?port} [%{level}] - %{?msg}."}
    }
    mutate {
    strip => "timestamp"
    }
    date {
    match => [ "timestamp", "YYYY-MM-dd HH:mm", "yyyy/MM/dd HH:mm:ss" ]
    target => "new_timestamp"
    timezone => "UTC"
    }
}

output {
    stdout { }
}

6.3.2.6 조건문

filter { dissect { mapping => {"message" => "[%{timestamp}]%{?->}[%{id}] %{ip} %{port} [%{level}] - %{msg}."} } if [level] == "INFO" { drop { } } else if [level] == "warn" { mutate { remove_field => [ "ip", "port", "timestamp", "level" ] } } }

output { stdout { } }

- 필터 내부에서 조건문을 사용할 수 있음
- dissect 플러그인을 통해 먼저 timestamp, id, ip, port, level, msg 필드를 생성
- 다음으로 if 조건문을 통해 필드명이 특정 조건과 일치하는지 확인하는데, level은 앞서 dissect에서 만들어진 필드
- 파이프라인 순서대로 동작하기 때문에 앞에서 level 필드가 만들어져야만 사용할 수 있음
- drop는 데이터를 삭제하는 플러그인
- 조건문과 결합하면 특정 조건을 만족하는 로그를 버릴 수 있음
- 여기서는 level 필드의 값에 따라 로그를 삭제하거나 특정 필드를 제거하고 있음

```text
{
          "host" => {
        "name" => "AL01978864.local"
    },
       "message" => "[2020/01/02 14:19:25]   [ID2] 218.25.32.70 1070 [warn] - busy server.",
            "id" => "ID2",
         "event" => {
        "original" => "[2020/01/02 14:19:25]   [ID2] 218.25.32.70 1070 [warn] - busy server."
    },
    "@timestamp" => 2022-06-14T17:58:39.898744Z,
           "log" => {
        "file" => {
            "path" => "/Users/user/ELK/logstash/log/filter-example.log"
        }
    },
      "@version" => "1",
           "msg" => "busy server"
}

6.3.3 출력

자주 사용하는 출력 플러그인

출력 플러그인 설명
elasticsearch 가장 많이 사용되는 출력 플러그인으로, bulk API를 사용해 엘라스틱서치에 인덱싱을 수행
file 지정한 파일의 새로운 줄에 데이터를 기록
kafka 카프카 토픽에 데이터를 기록

전체 출력 플러그인

input {
    file {
    path => "/Users/user/ELK/logstash/log/filter-example.log"
    start_position => "beginning"
    sincedb_path => "/dev/null"
    }
}

output {
    file {
    path => "/Users/user/ELK/logstash/config/output.json"
    }
    elasticsearch {
    index => "output"
    }
}

엘라스틱서치 플러그인 옵션

옵션 설명
hosts 이벤트를 전송할 엘라스틱서치의 주소
index 이벤트를 인덱싱할 대상 인덱스
docuemnt_id 인덱싱될 문서의 아이디를 직접 지정할 수 있는 옵션
user/passwd 엘라스틱서치에 보안 기능이 활성화되어 있을 때 인증을 위한 사용자 이름과 비밀번호
pipeline 엘라스틱서치에 등록된 인제스트 파이프라인을 활용하기 위한 옵션
template, template_name 로그스태시에서 기본 제공되는 인덱스 템플릿 외에 커스텀 템플릿을 사용하기 위한 옵션. template에는 정의한 인덱스 템플릿 파일의 경로를, template_name에는 엘라스틱서치에 어떤 이름으로 등록할지를 설정할 수 있음

파일 형식 결과

[2022-06-15T17:16:48,264][INFO ][logstash.outputs.file    ][main][c03c39f9bcc9f39d2683d41c02f954ef3eeb1df6ef6979220e2a6be96fce260e] Opening file {:path=>"/Users/user/ELK/logstash/config/output.json"}
user@AL01978864 logstash % ./bin/logstash -f ./config/logstash-test.conf
[2022-06-15T12:24:07,522][INFO ][filewatch.observingtail  ][main][7cbdaeecb67301ede1ebed72c52e15e1618aadfe66ca0cdd7b96915b358f606c] START, creating Discoverer, Watch with file and sincedb collections

user@AL01978864 config % cat output.json
{"@timestamp":"2022-06-15T03:24:07.819056Z","message":"[2020-01-02 14:17] [ID1] 192.10.2.6 9500 [INFO] - connected.","log":{"file":{"path":"/Users/user/ELK/logstash/log/filter-example.log"}},"host":{"name":"AL01978864.local"},"@version":"1","event":{"original":"[2020-01-02 14:17] [ID1] 192.10.2.6 9500 [INFO] - connected."}}
{"@timestamp":"2022-06-15T03:24:07.844124Z","message":"[2020/01/02 14:19:25]   [ID2] 218.25.32.70 1070 [warn] - busy server.","log":{"file":{"path":"/Users/user/ELK/logstash/log/filter-example.log"}},"host":{"name":"AL01978864.local"},"@version":"1","event":{"original":"[2020/01/02 14:19:25]   [ID2] 218.25.32.70 1070 [warn] - busy server."}}

6.3.4 코덱

자주 사용하는 플러그인

플러그인 설명
josn 입력 시 JSON 형태의 메시지를 객체로 읽어 들임. 출력 시에는 이벤트 객체를 다시금 JSON 형태로 변환
palin 메시지를 단순 문자열로 읽어 들임. 출력 시에는 원하는 포맷을 지정할 수 있음.
rubydebug 로그스태시의 설정을 테스트하거나 예기치 못한 파이프라인 설정 오류를 디버깅하기 위한 목적으로 주로 사용되며, 출력 시 루비(Ruby) 언어의 해시 형태로 이벤트를 기록. 입력 시에는 사용되지 않음
input {
    file {
    path => "/Users/user/ELK/logstash/log/filter-example.log"
    start_position => "beginning"
    sincedb_path => "/dev/null"
    codec => "json"
    }
}

output {
     stdout {}
}
{
       "message" => "[2020-01-02 14:17] [ID1] 192.10.2.6 9500 [INFO] - connected.",
          "host" => {
        "name" => "AL01978864.local"
    },
          "tags" => [
        [0] "_jsonparsefailure"
    ],
           "log" => {
        "file" => {
            "path" => "/Users/user/ELK/logstash/log/filter-example.log"
        }
    },
      "@version" => "1",
    "@timestamp" => 2022-06-15T08:43:26.187946Z
}
{
       "message" => "[2020/01/02 14:19:25]   [ID2] 218.25.32.70 1070 [warn] - busy server.",
          "host" => {
        "name" => "AL01978864.local"
    },
          "tags" => [
        [0] "_jsonparsefailure"
    ],
           "log" => {
        "file" => {
            "path" => "/Users/user/ELK/logstash/log/filter-example.log"
        }
    },
      "@version" => "1",
    "@timestamp" => 2022-06-15T08:43:26.248952Z
}

output { stdout {} }

- csv 파일의 경우 코덱 플러그인으로 plain을 사용하면 제대로 동작하는데, plain 코덱의 경우 굳이 명시하지 않아도 기본 적용됨
```text
{
      "@version" => "1",
       "message" => "[2020-01-02 14:17] [ID1] 192.10.2.6 9500 [INFO] - connected.",
    "@timestamp" => 2022-06-15T08:46:57.883293Z,
           "log" => {
        "file" => {
            "path" => "/Users/user/ELK/logstash/log/filter-example.log"
        }
    },
         "event" => {
        "original" => "[2020-01-02 14:17] [ID1] 192.10.2.6 9500 [INFO] - connected."
    },
          "host" => {
        "name" => "AL01978864.local"
    }
}
{
      "@version" => "1",
       "message" => "[2020/01/02 14:19:25]   [ID2] 218.25.32.70 1070 [warn] - busy server.",
    "@timestamp" => 2022-06-15T08:46:57.911414Z,
           "log" => {
        "file" => {
            "path" => "/Users/user/ELK/logstash/log/filter-example.log"
        }
    },
         "event" => {
        "original" => "[2020/01/02 14:19:25]   [ID2] 218.25.32.70 1070 [warn] - busy server."
    },
          "host" => {
        "name" => "AL01978864.local"
    }
}
input {
    file {
    path => "/Users/user/ELK/logstash/log/filter-example.log"
    start_position => "beginning"
    sincedb_path => "/dev/null"
    }
}

output {
     stdout {
    #codec => "line"
    #codec => "json"
    #codec => "rubydebug"
     }
}

line 코덱

2022-06-15T08:52:36.860921Z {name=AL01978864.local} [2020-01-02 14:17] [ID1] 192.10.2.6 9500 [INFO] - connected.
2022-06-15T08:52:36.883979Z {name=AL01978864.local} [2020/01/02 14:19:25]   [ID2] 218.25.32.70 1070 [warn] - busy server.

json 코덱

{"event":{"original":"[2020-01-02 14:17] [ID1] 192.10.2.6 9500 [INFO] - connected."},"message":"[2020-01-02 14:17] [ID1] 192.10.2.6 9500 [INFO] - connected.","log":{"file":{"path":"/Users/user/ELK/logstash/log/filter-example.log"}},"@timestamp":"2022-06-15T08:53:34.239319Z","host":{"name":"AL01978864.local"},"@version":"1"}{"event":{"original":"[2020/01/02 14:19:25]   [ID2] 218.25.32.70 1070 [warn] - busy server."},"message":"[2020/01/02 14:19:25]   [ID2] 218.25.32.70 1070 [warn] - busy server.","log":{"file":{"path":"/Users/user/ELK/logstash/log/filter-example.log"}},"@timestamp":"2022-06-15T08:53:34.267980Z","host":{"name":"AL01978864.local"},"@version":"1"}

rubydebug 코덱

{
"@version" => "1",
"message" => "[2020-01-02 14:17] [ID1] 192.10.2.6 9500 [INFO] - connected.",
"event" => {
"original" => "[2020-01-02 14:17] [ID1] 192.10.2.6 9500 [INFO] - connected."
},
"log" => {
"file" => {
"path" => "/Users/user/ELK/logstash/log/filter-example.log"
}
},
"host" => {
"name" => "AL01978864.local"
},
"@timestamp" => 2022-06-15T08:54:53.208102Z
}
{
"@version" => "1",
"message" => "[2020/01/02 14:19:25]   [ID2] 218.25.32.70 1070 [warn] - busy server.",
"event" => {
"original" => "[2020/01/02 14:19:25]   [ID2] 218.25.32.70 1070 [warn] - busy server."
},
"log" => {
"file" => {
"path" => "/Users/user/ELK/logstash/log/filter-example.log"
}
},
"host" => {
"name" => "AL01978864.local"
},
"@timestamp" => 2022-06-15T08:54:53.241008Z
}
  • 표준 출력 플러그인에서 코덱을 따로명시하지 않으면 기본으로 rubydebug 코덱이 기본으로 사용됨

6.4 다중 파이프라인

6.4.1 다중 파이프라인 작성

파이프라인 설정

설정 설명
pipeline.id 파이프라인의 고유한 아이디
path.config 파이프라인 설정 파일의 위치
pipeline.workers 필터와 출력을 병렬로 처리하기 위한 워커 수. 기본적으로 호스트의 CPU 코어 수와 동일하게 설정
pipeline.batch.size 입력 시 하나의 워커당 최대 몇 개까지의 이벤트를 동시에 처리할지를 결정. 배치 처리된 이벤트들은 엘라스틱서치 출력에서 하나의 벌크 요청으로 묶이기 때문에, 이 수치가 클수록 요청 수행 횟수가 줄어들어 인덱싱 성능 개선 효과가 있지만, 그만큼 단일 요청이 커지므로 1000, 2000과 같이 적당히 조절해가며 튜닝할 필요가 있음
queue.type 파이프라인에서 사용할 큐의 종류를 정할 수 있음. 기본적으로 memory 타입이 사용되나, persisted 타입을 선택해 이벤트의 유실을 좀 더 최소화할 수도 있음
input {
  file {
    path => "~/ELK/elasticsearch/logs/gc.log"
    start_position => "beginning"
    sincedb_path => "/dev/null"
  }
}

output {
  elasticsearch {
    index => "multipipe_pipe2"
  }
}

6.5 모니터링

user@AL01978864 config % curl -XGET "localhost:9600?pretty"
{
  "host" : "AL01978864.local",
  "version" : "8.2.2",
  "http_address" : "127.0.0.1:9600",
  "id" : "d6f760c0-c1d2-4b31-a43a-25049bddf8c6",
  "name" : "AL01978864.local",
  "ephemeral_id" : "e51b6d8d-3c92-4159-a18b-e088d969c46e",
  "status" : "green",
  "snapshot" : false,
  "pipeline" : {
    "workers" : 10,
    "batch_size" : 125,
    "batch_delay" : 50
  },
  "build_date" : "2022-05-25T15:50:17Z",
  "build_sha" : "4371da83af22cc411a7b1edbbe55698d312c9310",
  "build_snapshot" : false
}

로그스태시 API 리스트

정보 사용법
노드 curl -XGET 'localhost:9600/_node?pretty'
플러그인 curl -XGET 'localhost:9600/_node/plugins?pretty'
노드 통계 curl -XGET 'localhost:9600/_node/stats?pretty'
핫 스레드 curl -XGET 'localhost:9600/_node/hot_threads?pretty'
user@AL01978864 config % curl -XGET "localhost:9600/_node/pipelines?pretty"
{
  "host" : "AL01978864.local",
  "version" : "8.2.2",
  "http_address" : "127.0.0.1:9600",
  "id" : "d6f760c0-c1d2-4b31-a43a-25049bddf8c6",
  "name" : "AL01978864.local",
  "ephemeral_id" : "e51b6d8d-3c92-4159-a18b-e088d969c46e",
  "status" : "green",
  "snapshot" : false,
  "pipeline" : {
    "workers" : 10,
    "batch_size" : 125,
    "batch_delay" : 50
  },
  "pipelines" : {
    "mypipe2" : {
      "ephemeral_id" : "b2d09a4b-e08c-4993-8d6d-f34b6d861bde",
      "hash" : "cd38db589ab63a8ce1a1b6b783dce4068f93268544ab60bed2e99179b9038c64",
      "workers" : 10,
      "batch_size" : 125,
      "batch_delay" : 50,
      "config_reload_automatic" : false,
      "config_reload_interval" : 3000000000,
      "dead_letter_queue_enabled" : false
    }
  }
}

타입별 모니터링 노드 API

타입 설명
pipelines 실행중인 파이프라인의 종류와 각 파이프라인에 할당된 배치 크기, 워커 수 등의 정보를 얻음
os 로그스태시가 실행되는 노드의 OS의 종류와 버전 등의 정보를 얻음
jvm 로그스태시가 실행되는 노드의 자바 가상 머신의 버전과 GC 방식, 힙 메모리 등의 정보를 얻음
user@AL01978864 config % curl -XGET 'localhost:9600/_node/stats/pipelines?pretty'

{
  "host" : "AL01978864.local",
  "version" : "8.2.2",
  "http_address" : "127.0.0.1:9600",
  "id" : "d6f760c0-c1d2-4b31-a43a-25049bddf8c6",
  "name" : "AL01978864.local",
  "ephemeral_id" : "e51b6d8d-3c92-4159-a18b-e088d969c46e",
  "status" : "green",
  "snapshot" : false,
  "pipeline" : {
    "workers" : 10,
    "batch_size" : 125,
    "batch_delay" : 50
  },
  "pipelines" : {
    "mypipe2" : {
      "events" : {
        "filtered" : 0,
        "out" : 0,
        "duration_in_millis" : 0
      },
      "plugins" : {
        "inputs" : [ {
          "id" : "b0d3d73e57849a445db9b50139a6182ddd309c2ab59bb2b62af20e8cb9b53414",
          "name" : "file"
        } ],
        "codecs" : [ {
          "id" : "plain_78f9c440-20a8-447d-a724-becbc32f1200",
          "name" : "plain",
          "encode" : {
            "writes_in" : 0,
            "duration_in_millis" : 0
          },
          "decode" : {
            "writes_in" : 0,
            "out" : 0,
            "duration_in_millis" : 0
          }
        }, {
          "id" : "plain_4135e538-4ddd-4745-a839-2bbf7244434e",
          "name" : "plain",
          "encode" : {
            "writes_in" : 0,
            "duration_in_millis" : 0
          },
          "decode" : {
            "writes_in" : 0,
            "out" : 0,
            "duration_in_millis" : 0
          }
        } ],
        "filters" : [ ],
        "outputs" : [ {
          "id" : "a9042e4642e415126368f589d66834074934561ed5eaa59ab40d425e893f98cd",
          "name" : "elasticsearch",
          "events" : {
            "out" : 0,
            "in" : 0,
            "duration_in_millis" : 0
          }
        } ]
      },
      "reloads" : null,
      "queue" : {
        "type" : "memory"
      }
    }
  }
}

타입별 모니터링 노드 통계 API

타입 설명
jvm 스레드, 메모리 사용량, GC 자바 등 자바 가상 머신의 사용 통계
process 사용중인 파일 디스크립터 수, 즉 열어둔 파일 수와 메모리, CPU 사용량 등 프로세스의 사용 통계
events 파이프라인별이 아닌, 실행중인 로그스태시에 인입된, 필터링된, 출력된 총 이벤트의 수와 이를 처리하기 위해 소요된 시간 등의 통계
pipelines 파이프라인과 그 하위에 구성된 플러그인별 이벤트 통계
reloads 로그스태시에서 자동/수동으로 설정을 리로드했을 때 성공/실패 수 통계
os 로그스태시가 도커와 같은 컨테이너에서 실행될 때 cgroup에 대한 통계

6.5.2 모니터링 기능 활성화

6.6 정리

seryang commented 2 years ago

질문1

230페이지 파이프라인 설정을 보면 입력의 파일 플러그인 옵션에 sincedb_path => "nul"


질문2