Ghi chép về Logstash (P2): Logstash Configuration
Logstash
2
White

Nguyễn Tuấn Anh viết ngày 24/09/2019

1. Cấu trúc file config

Trong phần 1 của series, chúng ta có nhắc đến file logstash.conf. Đây là một file config của Logstash. File này sẽ gồm 3 phân đoạn input, filteroutput tương ứng với 3 stage của pipeline:

# This is a comment. You should use comments to describe
# parts of your configuration.
input {
    ...
}

filter {
    ...
}

output {
    ...
}

Mỗi một phân đoạn sẽ có cấu hình của một hoặc nhiều plugin.


2. Sử dụng biến môi trường

Có 2 cú pháp để sử dụng biến môi trường:

Cú pháp 1: ${var}

input {
    tcp {
        port => "${TCP_PORT}"
    }
}

Cú pháp 2: ${var:default value}

input {
    tcp {
        port => "${TCP_PORT:54321}"
    }
}

3. Input plugins

Một số input plugin hay dùng:

3.1. stdin

stdin cho phép chúng ta thu thập dữ liệu từ console của Logstash.

Ví dụ:

input {
    stdin { }
}

3.2. file

file cho phép chúng ta giám sát việc thay đổi các file và emit nội dung mới được append vào mỗi file. Plugin này không thích hợp cho việc đọc toàn bộ file từ đầu đến cuối và lưu trữ tất cả nội dung trong một event.

Mặc định, mỗi dòng trong file sẽ là một event. Nếu như muốn kết hợp nhiều dòng thành một event (như trường hợp xử lý stacktrace) thì chúng ta có thể sử dụng codec multiline.

Để theo dõi việc thay đổi các file, vị trí hiện tại trong file mà Logstash đã đọc sẽ được lưu bên trong một file có tên là sincedb.

Setting Kiểu dữ liệu Bắt buộc Mô tả
path string Đường dẫn trỏ tới file
start_position string Không Có 2 giá trị là: "end" (mặc định): đọc từ cuối file trở đi, "beginning": đọc từ đầu file trở đi
ignore_older number Không Logstash sẽ bỏ qua việc đọc một file nếu như thời gian chỉnh sửa lần cuối của file đó vượt quá khoảng thời gian được chỉ định (đơn vị là giây). Mặc định, nếu thời gian chỉnh sửa lần cuối vượt quá 24h (86400 giây), Logstash sẽ bỏ qua việc đọc file đó. Nếu giá trị này được set bằng 0 như ở ví dụ thì file sẽ không bị bỏ qua.
sincedb_path string Không Đường dẫn trỏ tới file sincedb

Ví dụ:

input {
    file {
        path => "/path/to/test.log"
        # path => "/path/to/logs/*.log" nếu muốn giám sát tất cả các file trong thư mục /path/to/logs
        start_position => "beginning"
        ignore_older => 0
    }
}

4. Filter plugins

Một số filter plugin hay dùng:

4.1. Common setting

Các filter plugin đều có chung các common setting sau:

Setting Kiểu dữ liệu Bắt buộc
add_field hash Không
add_tag array Không
enable_metric boolean Không
id string Không
periodic_flush boolean Không
remove_field array Không
remove_tag array Không

Ví dụ:

add_field

filter {
    grok {
        add_field => {
            "foo" => "This is foo field"
            "bar" => "This is bar field"
        }
    }
}

add_tag

filter {
    grok {
        add_tag => ["foo", "bar", "baz"]
    }
}

remove_field

filter {
    grok {
        remove_field => ["bar"]
    }
}

remove_tag

filter {
    grok {
        remove_tag => ["foo", "baz"]
    }
}

4.2. grok

Trong Logstash, grok là cách tốt nhất để phân tích một dữ liệu log chưa được cấu trúc thành có cấu trúc và có thể truy vấn được.

grok hoạt động theo cơ chế kết hợp các pattern để kiểm tra có match với dữ liệu log hay không. Logstash đã cung cấp sẵn 120 pattern để người dùng có thể áp dụng trong grok.

Cú pháp của một grok pattern là %{SYNTAX:SEMANTIC}. Trong đó:

  • SYNTAX là tên của pattern sẽ match với một phần văn bản trong dữ liệu log. Ví dụ, 3.44 sẽ match với pattern NUMBER, 55.3.244.1 sẽ match với pattern IP.
  • SEMANTIC là định danh cho phần văn bản đã match. Sau khi thực hiện grok, event sẽ có thêm field có name là SEMANTIC và value là phần văn bản đã match.

Nếu dữ liệu log không match với các pattern, field tags của event sẽ có thêm item "_grokparsefailure".

Setting Kiểu dữ liệu Bắt buộc Mô tả
match hash Không Liệt kê các field của event mà sẽ thực hiện grok, cùng những pattern sẽ được áp dụng. Giá trị mặc định là {}

Ví dụ:

Với dữ liệu log sau:

55.3.244.1 GET /index.html 15824 0.043

Chúng ta sẽ cấu hình grok như sau:

input {
    file {
        path => "/var/log/http.log"
    }
}

filter {
    grok {
        match => { "message" => "%{IP:client} %{WORD:method} %{URIPATHPARAM:request} %{NUMBER:bytes} %{NUMBER:duration}" }
    }
}

Sau khi thực thi grok, nếu dữ liệu log match với các pattern, event sẽ có thêm các field sau:

  • client: 55.3.244.1
  • method: GET
  • request: /index.html
  • bytes: 15824
  • duration: 0.043

Ngoài ra, đôi khi chúng ta cần phải xây dựng riêng pattern cho mình. Dưới đây là cú pháp của custom pattern:

(?<field_name>the pattern here)

4.3. mutate

mutate cho phép chúng ta đổi tên, xóa, thay thế và chỉnh sửa các field của event.

Ví dụ:

input { stdin { } }

filter {
    mutate {
        add_field => { "newMessage" => "%{message}" }
    }

    mutate {
        rename => [ "message", "oldMessage" ]
    }

    mutate {
        uppercase => [ "newMessage" ]
    }
}

output {
    stdout { codec => rubydebug }
}

Console output:

test log
{
      "@version" => "1",
          "host" => "example",
    "newMessage" => "TEST LOG",
    "oldMessage" => "test log",
    "@timestamp" => 2019-09-17T02:21:20.988Z
}

4.3. date

date được dùng để format value của một field có dạng date/time và lưu value sau khi format vào một field khác.

Các setting của date là:

  • match: chỉ định date/time format của field đầu vào: match => [field, formats...].
  • target: chỉ định field sẽ lưu value sau khi format. Mặc định là field @timestamp.

Ví dụ 1:

input { stdin { } }

filter {
    date { match => ["message", "yyyy-MM-dd HH:mm:ss"] }
}

output {
    stdout { codec => rubydebug }
}

Console output:

2019-09-17 08:00:00
{
      "@version" => "1",
       "message" => "2019-09-17 08:00:00",
    "@timestamp" => 2019-09-17T08:00:00.000Z,
          "host" => "example"
}

Ví dụ 2:

input { stdin { } }

filter {
    date {
        match => ["message", "yyyy-MM-dd HH:mm:ss"]
        target => "temp"
    }
}

output {
    stdout { codec => rubydebug }
}

Console output:

2019-09-17 08:00:00
{
    "@timestamp" => 2019-09-17T01:48:37.064Z,
          "host" => "example",
          "temp" => 2019-09-17T08:00:00.000Z,
       "message" => "2019-09-17 08:00:00",
      "@version" => "1"
}

5. Output plugins

Một số output plugin hay dùng:

5.1. stdout

stdout được dùng để xuất dữ liệu ra console của Logstash.

Setting Kiểu dữ liệu Bắt buộc Mô tả
codec codec Không Codec của console output. Mặc định là "rubydebug"

Ví dụ:

output {
    stdout { codec => rubydebug }
}

5.2. http

http được dùng để gửi dữ liệu thông qua các HTTP request.

Setting Kiểu dữ liệu Bắt buộc Mô tả
url string URL
http_method string Một trong các giá trị "put", "post", "patch", "delete", "get", "head"
headers hash Không Danh sách header
format string Không Một trong các giá trị "json", "json_batch", "form", "message"
mapping hash Không Body data cho các format "json", "json_batch", "form"

Ví dụ:

output {
    http {
        url => "http://localhost:8080/callback"
        http_method => "post"
        format => "json"
        mapping => ["payload", "%{message}"]
    }
}

6. Các ví dụ tham khảo

6.1. Ví dụ 1

Trong ví dụ này, chúng ta sẽ đọc dữ liệu của một file log và ghi ra console.

logstash.conf

input {
    file {
        path => "/tmp/test.log"
    }
}

output {
    stdout { codec => rubydebug }
}

Console output khi append một dòng log mới:

{
      "@version" => "1",
       "message" => "2019-09-17 10:54:43.019  INFO 15705 --- [main] com.example.demo.DemoApplication         : Started DemoApplication in 2.363 seconds (JVM running for 3.205)",
    "@timestamp" => 2019-09-17T01:54:52.160Z,
          "path" => "/tmp/test.log",
          "host" => "example"
}

6.2. Ví dụ 2

Đọc dữ liệu của một file log. Thêm field xác định log level. Nếu log level là ERROR thì sẽ gửi log message lên một RESTful API.

logstash.conf

input {
    file {
        path => "/tmp/test.log"
    }
}

filter {
    grok {
        match => {
            "message" => "%{LOGLEVEL:level}"
        }
    }
}

output {
    stdout { codec => rubydebug }

    if [level] == "ERROR" {
        http {
            url => "http://localhost:8080/callback"
            http_method => "post"
            format => "json"
            mapping => { 
                "payload" => "%{message}" 
            }
        }
    }
}

Console output:

...
{
      "@version" => "1",
          "path" => "/tmp/test.log",
    "@timestamp" => 2019-09-17T01:57:13.314Z,
       "message" => "2019-09-17 10:57:07.293  INFO 16042 --- [main] com.example.demo.DemoApplication         : Started DemoApplication in 2.273 seconds (JVM running for 2.815)",
          "host" => "example",
         "level" => "INFO"
}
{
      "@version" => "1",
          "path" => "/tmp/test.log",
    "@timestamp" => 2019-09-17T01:57:20.389Z,
       "message" => "2019-09-17 10:57:20.001 ERROR 16042 --- [scheduling-1] com.example.demo.DemoApplication         : This is error log",
          "host" => "example",
         "level" => "ERROR"
}

6.3. Ví dụ 3

Gộp các dòng của stacktrace thành 1 event.

input {
  file {
    path => "/tmp/test.log"
    codec => multiline {
      pattern => "^%{YEAR}-%{MONTHNUM}-%{MONTHDAY} %{TIME}.*"
      negate => true
      what => "previous"
      auto_flush_interval => 5
    }
  }
}

output {
    stdout { codec => rubydebug }
}

Ở đây, chúng ta sẽ sử dụng codec plugin là multiline để gộp nhiều dòng log thành một event.

Ý nghĩa của các setting:

  • pattern: pattern của các dòng log chúng ta muốn gộp. Chúng ta có thể thấy các dòng của stacktrace không bắt đầu bằng timestamp như các dòng log thông thường.
  • negate: có 2 giá trị là true hoặc false (mặc định). Nếu bằng true, nếu một dòng không match với pattern sẽ được xem như là dòng đầu tiên của một event. Dòng đó sẽ được gộp với dòng trên (nếu whatprevious) hoặc dòng dưới (nếu whatnext) thành một event.
  • what: có 2 giá trị là previouse hoặc next.

Các bạn có thể tham khảo thêm về xử lý stacktrace trong Logstash trong bài viết.

Ví dụ:

Chúng ta có đoạn log sau:

...
2019-09-17 11:06:25.455  INFO 17256 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 2.349 seconds (JVM running for 2.855)
2019-09-17 11:06:30.010 ERROR 17256 --- [   scheduling-1] com.example.demo.DemoApplication         : Thrown exception: 

java.io.FileNotFoundException: /tmp/non-exists.log (No such file or directory)
    at java.io.FileInputStream.open0(Native Method) ~[na:1.8.0_201]
    at java.io.FileInputStream.open(FileInputStream.java:195) ~[na:1.8.0_201]
    at java.io.FileInputStream.<init>(FileInputStream.java:138) ~[na:1.8.0_201]
    at com.example.demo.DemoApplication.log(DemoApplication.java:32) ~[classes/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_201]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_201]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_201]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_201]
    at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:84) [spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
    at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) [spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
    at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:93) [spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [na:1.8.0_201]
    at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266) [na:1.8.0_201]
    at java.util.concurrent.FutureTask.run(FutureTask.java) [na:1.8.0_201]
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) [na:1.8.0_201]
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) [na:1.8.0_201]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_201]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_201]
    at java.lang.Thread.run(Thread.java:748) [na:1.8.0_201]

Console output:

...
{
      "@version" => "1",
       "message" => "2019-09-17 11:06:25.455  INFO 17256 --- [main] com.example.demo.DemoApplication         : Started DemoApplication in 2.349 seconds (JVM running for 2.855)",
          "host" => "example",
    "@timestamp" => 2019-09-17T02:06:30.594Z,
          "path" => "/tmp/test.log"
}
{
      "@version" => "1",
       "message" => "2019-09-17 11:06:30.010 ERROR 17256 --- [scheduling-1] com.example.demo.DemoApplication         : Thrown exception: \njava.io.FileNotFoundException: /tmp/non-exists.log (No such file or directory)\n\tat java.io.FileInputStream.open0(Native Method) ~[na:1.8.0_201]\n\tat java.io.FileInputStream.open(FileInputStream.java:195) ~[na:1.8.0_201]\n\tat java.io.FileInputStream.<init>(FileInputStream.java:138) ~[na:1.8.0_201]\n\tat com.example.demo.DemoApplication.log(DemoApplication.java:32) ~[classes/:na]\n\tat sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_201]\n\tat sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_201]\n\tat sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_201]\n\tat java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_201]\n\tat org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:84) [spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]\n\tat org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) [spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]\n\tat org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:93) [spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]\n\tat java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [na:1.8.0_201]\n\tat java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266) [na:1.8.0_201]\n\tat java.util.concurrent.FutureTask.run(FutureTask.java) [na:1.8.0_201]\n\tat java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) [na:1.8.0_201]\n\tat java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) [na:1.8.0_201]\n\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_201]\n\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_201]\n\tat java.lang.Thread.run(Thread.java:748) [na:1.8.0_201]",
    "@timestamp" => 2019-09-17T02:06:35.696Z,
          "tags" => [
        [0] "multiline"
    ],
          "path" => "/tmp/test.log",
          "host" => "example"
}
Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Nguyễn Tuấn Anh

26 bài viết.
185 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
48 41
MyContact là một ứng dụng mà mình thường viết mỗi khi học một ngôn ngữ hay công nghệ mới. MyContact chỉ là một ứng dụng CRUD đơn giản, cho phép ngư...
Nguyễn Tuấn Anh viết gần 3 năm trước
48 41
White
27 14
Hướng dẫn lập trình Spring Security Trong bài viết lần này, mình sẽ giúp các bạn bước đầu tìm hiểu (Link) thông qua xây dựng các chức năng: Đăng ...
Nguyễn Tuấn Anh viết gần 3 năm trước
27 14
White
16 1
Giới thiệu Spring Framework Trong bài viết này, mình sẽ giới thiệu cho các bạn về một trong những Java EE framework rất nổi bật và phổ biến hiện n...
Nguyễn Tuấn Anh viết gần 3 năm trước
16 1
Bài viết liên quan
White
7 2
1. Logstash là gì? Logstash là một engine mã nguồn mở cho phép thu thập dữ liệu theo thời gian thực. Logstash đóng vai trò "data shipper" trạm tru...
Nguyễn Tuấn Anh viết 1 tháng trước
7 2
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
26 bài viết.
185 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!