Hello World 系列 - Logstash

示範環境: Ubuntu 16.04.4 LTS

首先, logstash 需要 java, 所以我們就先安裝java

apt-get install python-software-properties
apt-get install software-properties-common
add-apt-repository ppa:webupd8team/java
apt-get update
apt-get install oracle-java8-installer

echo "export JAVA_HOME=/usr/lib/jvm/java-8-oracle" >> ~/.bashrc
echo "export JRE_HOME=/usr/lib/jvm/java-8-oracle/jre" >> ~/.bashrc

然後, 下載安裝logstash
當然, 你也可以選擇自己想要的版本
這邊建議用6.0以上的 原因有二

  • 6.0 才有pipeline功能
  • 6.0 之前的 config reload 這個功能有點異常
wget https://artifacts.elastic.co/downloads/logstash/logstash-6.2.4.zip
unzip logstash-6.2.4.zip

pipeline, 就是讓logstash可以同時執行多個任務, 而彼此不受影響.
在這個功能推出以前, 如果你的設定有多個output; 其中一個output到達不了的時候, 會進而影響全部功能 .. 然而官方的回覆是說這是要對logstash質量的保證, 不允許只輸出一半之類的事..

Anyway, 後來太多人抱怨了, 就推出這個pipeline功能啦! 如果你們的架構, logstash要處理類型比較豐富, 就一定會需要用到這個功能哦~

config reload, 就是有更改設定檔的時候可以自動reload. 因為logstash啟動需要帶起jvm, 會需要一點時間, 所以有這個參數就很方便.


Logstash Hello World

cd logstash-6.2.4
bin/logstash -e 'input { stdin { } } output { stdout {} }'

這時候需要耐心等待下, 不久後就可以看到下圖
logstash_hello_world
有些朋友到了這一步就說, 我跑了沒有反應! 不working!

別誤會~其實已經正常開始運作了哦!
你會看到他說 The stdin plugin is now waiting for input:
然後現在試著在畫面輸入點東西, 就會看到他有回應了.
logstash_hello_world2


稍微實際一點的應用

wei~ 是有反應了, 但是好像沒什麼卵用啊? 說好的收集日誌呢? 把結果打印在畫面上也沒什麼用阿?
莫急、莫慌、莫害怕~ 先做一點基本的說明吧. 先看一下官方對於logstash的示意圖.
basic_logstash_pipeline

然後我自己的示意圖, 這一不小心就展現了我的繪畫天分 (:з」∠)
basic_logstash_pipeline2

你可以把他想像成是一個, 這個
air_filter

然後再做這件事
ANTI-BACTERIA-COATING


咳咳.. 進入正題

所以說, logstash 就是由三個部分組成, 每個部分都有各自的plugin

  • Input: 各式各樣的接收, 剛剛的示範是stdin, 畫面接收自terminal
  • Filter: 各式各樣的資料處理, 剛剛的示範沒有用這個, 不過這是他最厲害的地方之一
  • Output: 各式各樣的輸出, 剛剛的示範是stdout, 畫面直接打印在terminal

那我們就示範把日誌丟到elasticsearch吧! 還沒有安裝elasticsearch 的朋友, 請參考這裡哦~
Hello World 系列 - Elasticsearch

首先, 我們要編寫一個設定檔; 剛剛的示範屬於one-linner, 主要是官方想要表達: 嘿..把config直接寫在指令也是可以的唷~

不過正常情況下誰會把落落長的config塞在指令阿?!
總之, 編寫一個設定檔吧.

所以剛剛的示範, 寫成檔案會變成這樣

input {
  stdin {}
}


filter {}


output {
  stdout { }
}

主要這三個區塊就是他的骨架, 然後看到input下面塞了一個stdin ; 這邊就是上面說的plugin
可以來這邊看 logstash input stdin, 或是google 直接搜尋 logstash <input/filter/output> <plugin name>

那他就會跟你說, 這個plugin可以放那些參數, 哪些必填哪些選填之類.

沒興趣的同學請直接回頭~ 前方高能!


這邊示範, 蒐集laravel log 並且丟到 elasticsearch

mkdir conf.d
vim conf.d/laravel_log_to_es.conf
input {
  file {
    path => [ 
      "/var/log/laravel-*.log"
    ]
    start_position => "beginning"
    sincedb_path => "./logstash_sincedb"
    codec => multiline {
      pattern => "^\["
      negate => true
      what => "previous"
    }
  }
}


filter {
  grok {
  # Laravel log format
    match => { "message" => "\[%{TIMESTAMP_ISO8601:logdate}\] %{DATA:env}\.%{DATA:severity}: %{GREEDYDATA:logmessage}" }
    }
  # Parse the date
    date {
      match => [ "logdate", "yyyy-MM-dd HH:mm:ss" ]
      target => [ "logdate" ]
    }
  # Remove empty lines
    if [message] =~ /^\s*$/ {
      drop { }
    }
  # Remove origin message
    mutate {
      remove_field => [ "message" ]
    }
}


output {
  elasticsearch {
    hosts => ["127.0.0.1:9200"]
    index => "laravel-log-%{+YYYY.MM.dd}"
  }
}

Capture5

只是想蒐集點log, 至於嗎?

別這樣~ 這都是為了更有利後續資料分析做的處理哦~
假設你的log位置在/var/log/laravel-2018.log

內容如下

[2018-05-02 15:05:49] production.INFO: server is now running  
[2018-05-02 15:05:50] production.INFO: worker init    
[2018-05-02 15:05:50] production.ALERT: proccess not found  
[2018-05-02 15:06:22] production.ALERT: tux not found
[2018-05-02 15:15:54] production.ERROR: ErrorException: Trying to get property of non-object in /home/ubuntu/UserRegisterLinkService.php
Stack trace:
#0 /home/ubuntu/UserRegisterLinkService.php(62): Illuminate\Foundation\Bootstrap\HandleExceptions->handleError(8, 'Trying to get p...', '/home/ubuntu/...', 62, Array)
#1 /home/ubuntu/UserRegisterLinkController.php(69): App\Services\UserRegisterLinkService->create(Object(App\Models\User), Array)
#2 [internal function]: App\Http\Controllers\API\Front\UserRegisterLinkController->create(Object(Illuminate\Http\Request))
#3 /home/ubuntu/src/Illuminate/Routing/Controller.php(55): 
[2018-05-02 15:20:49] production.INFO: server is now running  
[2018-05-02 15:20:50] production.INFO: worker init  

上面每個區塊的puglin可以幫我們做這些事, 如果沒有這樣的話, 資料進去elasticsearch裡面就是一坨文字而已, 是不能做分析的哦!


我挑幾個比較重要的說, 剩下的會在其他篇做解釋
codec => multiline: 這是多行日誌的解析插件, 沒有這個的話, 每一行trace都會被判定是獨立事件
grok: 就是把我們的日誌訊息, 用正則的方式, 變成field: value
date: 日誌時間的插件, 如果沒有使用他的話, logstash丟出去的@timestamp欄位, 是日誌接收的時間哦. 如果今天要匯入舊日誌的話就麻煩了.
if [message] =~ /^\s*$/: 如果今天日誌是一個空行的話, 通常不會發生啦~
mutate: 就是變異, 可以任意改變我們的資料


運行結果

畫面上好像不會特別顯示什麼.. 不過這時候看一下自己的elasticsearch

[email protected]:~# curl 127.0.0.1:9200/_cat/indices
yellow open laravel-log-2018.05.06 46sxRKN8SU2si1yfH8sNkQ 5 1 6 0 44kb 44kb

然後來做個搜尋

[email protected]:~# curl -s 127.0.0.1:9200/laravel-log-2018.05.06/_search?q=severity:ALERT | jq .
{
  "took": 3,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 2,
    "max_score": 0.9808292,
    "hits": [
      {
        "_index": "laravel-log-2018.05.06",
        "_type": "doc",
        "_id": "ljh5NGMBkXU3O_9hlAIl",
        "_score": 0.9808292,
        "_source": {
          "host": "ubuntu",
          "logmessage": "tux not found",
          "@version": "1",
          "logdate": "2018-05-02T07:06:22.000Z",
          "env": "production",
          "severity": "ALERT",
          "@timestamp": "2018-05-06T08:02:52.142Z",
          "path": "/var/log/laravel-2018.log"
        }
      },
      {
        "_index": "laravel-log-2018.05.06",
        "_type": "doc",
        "_id": "mDh5NGMBkXU3O_9hlAIp",
        "_score": 0.2876821,
        "_source": {
          "host": "ubuntu",
          "logmessage": "proccess not found  ",
          "@version": "1",
          "logdate": "2018-05-02T07:05:50.000Z",
          "env": "production",
          "severity": "ALERT",
          "@timestamp": "2018-05-06T08:02:52.141Z",
          "path": "/var/log/laravel-2018.log"
        }
      }
    ]
  }
}

成功啦~ 可以看到ALERT的就是有兩筆資料; 資料的欄位是剛剛grok的功勞哦
現在你的資料已經很漂亮, 可以讓kibana 或是其他軟體去做視覺化分析啦~

Err.. 一不小心講太多了, 咱下次見~