一、概述
1.1 logstash簡介
logstash是一個數據分析軟件,主要目的是分析log日誌。整一套軟件可以當作一個MVC模型,logstash是controller層,Elasticsearch是一個model層,kibana是view層。首先將數據傳給logstash,它將數據進行過濾和格式化(轉成JSON格式),然後傳給Elasticsearch進行存儲、建搜索的索引,kibana提供前端的頁面再進行搜索和圖表可視化,它是調用Elasticsearch的接口返回的數據進行可視化。logstash和Elasticsearch是用Java寫的,kibana使用node.js框架。
1.2 logstash架構
圖1.1 logstash架構
logstash工作時,主要設置3個部分的工作屬性。
input:設置數據來源
filter:可以對數據進行一定的加工處理過濾,但是不建議做複雜的處理邏輯。這個步驟不是必須的
output:設置輸出目標
二、logstash部署
可以直接到 https://www.elastic.co/downloads/logstash 下載想要的版本,這裏使用的是6.6.2 版本。部署其實很簡單,現在下來直接解壓就可以使用了,類似於flume,關鍵在於採集配置文件的編寫。
一般就是使用如下方式啓動logstash
調試方式:直接啓動前臺進程
bin/logstash -f /path/to/configfile
生產環境中一般後臺啓動:
nohup bin/logstash -f /path/to/configfile >標準日誌 2>錯誤日誌 &
啓動前,可以使用 -t 選項測試配置文件是否有語法錯誤,如:
bin/logstash -f /path/to/configfile -t
三、編寫配置文件
3.1 input配置
3.1.1 讀取文件
例子:監控文件內容輸出到console
input {
file {
path => ["/var/log/*.log", "/var/log/message"]
type => "system"
start_position => "beginning"
}
}
output{stdout{codec=>rubydebug}}
有一些比較有用的配置項,可以用來指定 FileWatch 庫的行爲:
discover_interval
logstash 每隔多久去檢查一次被監聽的 path 下是否有新文件。默認值是 15 秒。
exclude
不想被監聽的文件可以排除出去,這裏跟 path 一樣支持 glob 展開。
close_older
一個已經監聽中的文件,如果超過這個值的時間內沒有更新內容,就關閉監聽它的文件句柄。默認是 3600 秒,即一小時。
ignore_older
在每次檢查文件列表的時候,如果一個文件的最後修改時間超過這個值,就忽略這個文件。默認是 86400 秒,即一天。
sincedb_path
如果你不想用默認的 $HOME/.sincedb(Windows 平臺上在 C:\Windows\System32\config\systemprofile\.sincedb),可以通過這個配置定義 sincedb 文件到其他位置。
sincedb_write_interval
logstash 每隔多久寫一次 sincedb 文件,默認是 15 秒。
stat_interval
logstash 每隔多久檢查一次被監聽文件狀態(是否有更新),默認是 1 秒。
start_position
logstash 從什麼位置開始讀取文件數據,默認是結束位置,也就是說 logstash 進程會以類似 tail -F 的形式運行。如果你是要導入原有數據,把這個設定改成 "beginning",logstash 進程就從頭開始讀取,類似 less +F 的形式運行。
3.1.2 標準輸入
stdin模塊是用於標準輸入,簡單來說就是從標準輸入讀取數據。例子:
input {
stdin {
add_field => {"key" => "value"}
codec => "plain"
tags => ["add"]
type => "std"
}
}
output{stdout{codec=>rubydebug}}
輸入hello,可以看到打印以下信息:
hello
{
"message" => "hello",
"tags" => [
[0] "[add]"
],
"@version" => "1",
"host" => "bigdata121",
"@timestamp" => 2019-09-07T03:20:35.569Z,
"type" => "std",
"key" => "value"
}
type 和 tags 是 logstash 事件中兩個特殊的字段。通常來說我們會在輸入區段中通過 type 來標記事件類型。而 tags 則是在數據處理過程中,由具體的插件來添加或者刪除的。
3.2 codec配置
默認情況下,logstash只支持純文本形式的輸入,然後在過濾器filter中將數據加工成指定格式。但現在,我們可以在輸入期處理不同類型的數據,這全是因爲有了 codec 設置。所以,這裏需要糾正之前的一個概念。Logstash 不只是一個input | filter | output 的數據流,而是一個 input | decode | filter | encode | output 的數據流!codec 就是用來 decode、encode 事件的。
例子,輸入json格式數據
input {
stdin {
add_field => {"key" => "value"}
codec => "json"
type => "std"
}
}
output {
stdout {codec => rubydebug}
}
當輸入json數據時,會自動解析出來
輸入:{"name":"king"}
輸出:
{
"name" => "king",
"host" => "bigdata121",
"@timestamp" => 2019-09-07T04:05:42.550Z,
"@version" => "1",
"type" => "std",
"key" => "value"
}
3.3 filter配置
3.3.1 grok插件
logstash擁有豐富的filter插件,它們擴展了進入過濾器的原始數據,進行復雜的邏輯處理,甚至可以無中生有的添加新的 logstash 事件到後續的流程中去!Grok 是 Logstash 最重要的插件之一。也是迄今爲止使蹩腳的、無結構的日誌結構化和可查詢的最好方式。Grok在解析 syslog logs、apache and other webserver logs、mysql logs等任意格式的文件上表現完美。
這個工具非常適用於系統日誌,Apache和其他網絡服務器日誌,MySQL日誌等。
配置:
input {
stdin {
type => "std"
}
}
filter {
grok {
match=>{"message"=> "%{IP:client} %{WORD:method} %{URIPATHPARAM:request} %{NUMBER:bytes} %{NUMBER:duration}" }
}
}
output{stdout{codec=>rubydebug}}
輸入:55.3.244.1 GET /index.html 15824 0.043
輸出:
{
"@version" => "1",
"host" => "zzc-203",
"request" => "/index.html",
"bytes" => "15824",
"duration" => "0.043",
"method" => "GET",
"@timestamp" => 2019-03-19T05:09:55.777Z,
"message" => "55.3.244.1 GET /index.html 15824 0.043",
"type" => "std",
"client" => "55.3.244.1"
}
grok模式的語法如下:
%{SYNTAX:SEMANTIC}
SYNTAX:代表匹配值的類型,例如3.44可以用NUMBER類型所匹配,127.0.0.1可以使用IP類型匹配。
SEMANTIC:代表存儲該值的一個變量名稱,例如 3.44 可能是一個事件的持續時間,127.0.0.1可能是請求的client地址。所以這兩個值可以用 %{NUMBER:duration} %{IP:client} 來匹配。
你也可以選擇將數據類型轉換添加到Grok模式。默認情況下,所有語義都保存爲字符串。如果您希望轉換語義的數據類型,例如將字符串更改爲整數,則將其後綴爲目標數據類型。例如%{NUMBER:num:int}將num語義從一個字符串轉換爲一個整數。目前唯一支持的轉換是int和float。
Logstash附帶約120個模式。你可以在這裏找到它們https://github.com/logstash-plugins/logstash-patterns-core/tree/master/patterns
自定義類型
更多時候logstash grok沒辦法提供你所需要的匹配類型,這個時候我們可以使用自定義。
創建自定義 patterns 文件。
①創建一個名爲patterns其中創建一個文件postfix (文件名無關緊要,隨便起),在該文件中,將需要的模式寫爲模式名稱,空格,然後是該模式的正則表達式。例如:
POSTFIX_QUEUEID [0-9A-F]{10,11}
②然後使用這個插件中的patterns_dir設置告訴logstash目錄是你的自定義模式。
配置:
input {
stdin {
type => "std"
}
}
filter {
grok {
patterns_dir => ["./patterns"]
match => { "message" => "%{SYSLOGBASE} %{POSTFIX_QUEUEID:queue_id}: %{GREEDYDATA:syslog_message}" }
}
}
output{stdout{codec=>rubydebug}}
輸入:
Jan 1 06:25:43 mailserver14 postfix/cleanup[21403]: BEF25A72965: message-id=<20130101142543.5828399CCAF@mailserver1
輸出:
{
"queue_id" => "BEF25A72965",
"message" => "Jan 1 06:25:43 mailserver14 postfix/cleanup[21403]: BEF25A72965: message-id=<20130101142543.5828399CCAF@mailserver1",
"pid" => "21403",
"program" => "postfix/cleanup",
"@version" => "1",
"type" => "std",
"logsource" => "mailserver14",
"host" => "zzc-203",
"timestamp" => "Jan 1 06:25:43",
"syslog_message" => "message-id=<20130101142543.5828399CCAF@mailserver1",
"@timestamp" => 2019-03-19T05:31:37.405Z
}
3.3.2 GeoIP插件
GeoIP 是最常見的免費 IP 地址歸類查詢庫,同時也有收費版可以採購。GeoIP 庫可以根據 IP 地址提供對應的地域信息,包括國別,省市,經緯度等,對於可視化地圖和區域統計非常有用。
配置:
input {
stdin {
type => "std"
}
}
filter {
geoip {
source => "message"
}
}
output{stdout{codec=>rubydebug}}
輸入:183.60.92.253
輸出:
{
"type" => "std",
"@version" => "1",
"@timestamp" => 2019-03-19T05:39:26.714Z,
"host" => "zzc-203",
"message" => "183.60.92.253",
"geoip" => {
"country_code3" => "CN",
"latitude" => 23.1167,
"region_code" => "44",
"region_name" => "Guangdong",
"location" => {
"lon" => 113.25,
"lat" => 23.1167
},
"city_name" => "Guangzhou",
"country_name" => "China",
"continent_code" => "AS",
"country_code2" => "CN",
"timezone" => "Asia/Shanghai",
"ip" => "183.60.92.253",
"longitude" => 113.25
}
}
3.4 output配置
3.4.1 標準輸出stdout
前面已經說到了,通常用於測試,如:
input {
stdin {
type => "std"
}
}
output{stdout{codec=>rubydebug}}
3.4.2 file保存成文件
通過日誌收集系統將分散在數百臺服務器上的數據集中存儲在某中心服務器上,這是運維最原始的需求。Logstash 當然也能做到這點。例子
input {
stdin {
type => "std"
}
}
output {
file {
# 表示年 月 日 主機
path => "/tmp/%{+yyyy}-%{+MM}-%{+dd}-%{host}.log"
codec => line {format => "%{message}"}
}
}
輸入:this is hello world
[2019-09-07T12:08:45,327][INFO ][logstash.outputs.file ] Opening file {:path=>"/tmp/2019-09-07-bigdata121.log"}
可以看到日誌顯示,將內容保存到 /tmp/2019-09-07-bigdata121.log 中
接着看看這個文件的內容,就是我們輸入的內容
3.4.3 通過端口讀取數據
接收日誌服務器配置:
input {
tcp {
mode => "server"
port => 9600
ssl_enable => false
}
}
filter {
json {
source => "message"
}
}
output {
file {
path => "/home/hduser/app/logstash-6.6.2/data_test/%{+YYYY-MM-dd}/%{servip}-%{filename}"
codec => line { format => "%{message}"}
}
}
發送日誌服務器配置:
input{
file {
path => ["/home/hduser/app/logstash-6.6.2/data_test/send.log"]
type => "ecolog"
start_position => "beginning"
}
}
filter {
if [type] =~ /^ecolog/ {
ruby {
code => "file_name = event.get('path').split('/')[-1]
event.set('file_name',file_name)
event.set('servip','接收方ip')"
}
mutate {
rename => {"file_name" => "filename"}
}
}
}
output {
tcp {
host => "接收方ip"
port => 9600
codec => json_lines
}
}
從發送方發送message,接收方可以看到寫出文件。
3.4.4 寫入到ES
例子:將文件內容寫入到es
input {
file {
path => ["/usr/local/logstash-6.6.2/data_test/run_error.log"]
# index和type名字中不能有大寫
type => "error"
start_position => "beginning"
}
}
output {
elasticsearch {
# ES的serverip列表
hosts => ["192.168.109.133:9200"]
# 寫入的index的名稱
index => "logstash-%{type}-%{+YYYY.MM.dd}"
# type
document_type => "%{type}"
# 當上面的es節點無法使用,是否尋找其他es節點
sniffing => true
# 是否重寫模板
template_overwrite => true
}
}
有個小問題:
當日志中一行的內容過長時,在日誌文件中會寫成多行的形式。但是默認寫入到es中時,是每一行就當做document來寫入,而我們想要的是一條完整的日誌作爲一個document來寫入。這時候就需要 codec的一個模塊 multiline,例子
input {
file {
path =>"/the path/tmp.log"
#若日誌爲多行信息顯示,需要codec配置
codec => multiline {
pattern => "^\[" 表示[ 開頭的纔是新的一條日誌
negate => true
what => "previous"
}
start_position=>"beginning"
}
}
# filter爲logstash的解析日誌模塊
filter {
# 解析日誌生成相關的IP,訪問地址,日誌級別
grok {
match => {
"message" => "%{SYSLOG5424SD:time} %{IP:hostip} %{URIPATHPARAM:url}\s*%{LOGLEVEL:loglevel}"
}
}
#解析log生成的時間爲時間戳
grok{
match => {
"message" => "%{TIMESTAMP_ISO8601:log_create_date}"
}
}
# 替換插入信息的時間戳爲日誌生成的時間戳
date {
match => ["log_create_date", "yyyy-MM-dd HH:mm:ss" ]
target => "@timestamp"
}
}
#定義日誌輸出的配置,此處爲寫入解析結果到es集羣
output {
elasticsearch {
hosts => ["ip:9200"]
index => "system-log"
}
}
================================================
主要是input這一段的codec配置
input {
file {
path =>"/the path/tmp.log"
#若日誌爲多行信息顯示,需要codec配置
codec => multiline {
pattern => "^\["
negate => true
what => "previous"
}
start_position=>"beginning"
}
}
pattern => "^\["
默認是換行符分隔行,現在可以使用正則匹配來指定自定義的分隔符,作爲一行
negate => true
如果前面的匹配成功了,true就表示取反,false表示維持原來的值,默認是false
what => "previous"或者“next”
匹配到的內容是屬於上一個event還是下一個event