ELK是什么
ELK是elastic公司提供的一套完整的日志收集以及前端展示的解決方案,是三個(gè)產(chǎn)品的首字母縮寫(xiě),分別是ElasticSearch、Logstash和Kibana。
其中Logstash負(fù)責(zé)對(duì)日志進(jìn)行處理,如日志的過(guò)濾、日志的格式化等;ElasticSearch具有強(qiáng)大的文本搜索能力,因此作為日志的存儲(chǔ)容器;而Kibana負(fù)責(zé)前端的展示。
ELK搭建架構(gòu)如下圖:
加入了filebeat用于從不同的客戶端收集日志,然后傳遞到Logstash統(tǒng)一處理。
ELK的搭建
因?yàn)镋LK是三個(gè)產(chǎn)品,可以選擇依次安裝這三個(gè)產(chǎn)品。
這里選擇使用Docker安裝ELk。
Docker安裝ELk也可以選擇分別下載這三個(gè)產(chǎn)品的鏡像并運(yùn)行,但是本次使用直接下載elk的三合一鏡像來(lái)安裝。
因此首先要保證已經(jīng)有了Docker的運(yùn)行環(huán)境,Docker運(yùn)行環(huán)境的搭建請(qǐng)查看:https://blog.csdn.net/qq13112...
拉取鏡像
有了Docker環(huán)境之后,在服務(wù)器運(yùn)行命令:
docker pull sebp/elk
這個(gè)命令是在從Docker倉(cāng)庫(kù)下載elk三合一的鏡像,總大小為2個(gè)多G,如果發(fā)現(xiàn)下載速度過(guò)慢,可以將Docker倉(cāng)庫(kù)源地址替換為國(guó)內(nèi)源地址。
下載完成之后,查看鏡像:
docker images
Logstash配置
在/usr/config/logstash
目錄下新建beats-input.conf,用于日志的輸入:
input {
beats {
port => 5044
}
}
新建output.conf,用于日志由Logstash到ElasticSearch的輸出:
output {
elasticsearch {
hosts => ["localhost"]
manage_template => false
index => "%{[@metadata][beat]}"
}
}
其中的index
為輸出到ElasticSearch后的index
。
運(yùn)行容器
有了鏡像之后直接啟動(dòng)即可:
docker run -d -p 5044:5044 -p 5601:5601 -p 9203:9200 -p 9303:9300 -v /var/data/elk:/var/lib/elasticsearch -v /usr/config/logstash:/etc/logstash/conf.d --name=elk sebp/elk
-d的意思是后臺(tái)運(yùn)行容器;
-p的意思是宿主機(jī)端口:容器端口,即將容器中使用的端口映射到宿主機(jī)上的某個(gè)端口,ElasticSearch的默認(rèn)端口是9200和9300,由于我的機(jī)器上已經(jīng)運(yùn)行了3臺(tái)ElasticSearch實(shí)例,因此此處將映射端口進(jìn)行了修改;
-v的意思是宿主機(jī)的文件|文件夾:容器的文件|文件夾,此處將容器中elasticsearch 的數(shù)據(jù)掛載到宿主機(jī)的/var/data/elk
上,以防容器重啟后數(shù)據(jù)的丟失;并且將logstash的配置文件掛載到宿主機(jī)的/usr/config/logstash
目錄。
--name的意思是給容器命名,命名是為了之后操作容器更加方便。
如果你之前搭建過(guò)ElasticSearch的話,會(huì)發(fā)現(xiàn)搭建的過(guò)程中有各種錯(cuò)誤,但是使用docker搭建elk的過(guò)程中并沒(méi)有出現(xiàn)那些錯(cuò)誤。
運(yùn)行后查看容器:
docker ps
查看容器日志:
docker logs -f elk
進(jìn)入容器:
docker exec -it elk /bin/bash
修改配置后重啟容器:
docker restart elk
查看kinaba
瀏覽器輸入http://my_host:5601/
即可看到kinaba界面。此時(shí)ElasticSearch中還沒(méi)有數(shù)據(jù),需要安裝Filebeat采集數(shù)據(jù)到elk中。
Filebeat搭建
Filebeat用于采集數(shù)據(jù)并上報(bào)到Logstash或者ElasticSearch,在需要采集日志的服務(wù)器上下載Filebeat并解壓即可使用
wget https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-6.2.1-linux-x86_64.tar.gz
tar -zxvf filebeat-6.2.1-linux-x86_64.tar.gz
修改配置文件
進(jìn)入filebeat,修改filebeat.yml。
filebeat.prospectors:
- type: log
#需要設(shè)置為true配置才能生效
enabled: true
path:
#配置需要采集的日志路徑
- /var/log/*.log
#可以打一個(gè)tag以后分類(lèi)使用
tag: ["my_tag"]
#對(duì)應(yīng)ElasticSearch的type
document_type: my_type
setup.kibana:
#此處為kibana的ip及端口,即kibana:5601
host: ""
output.logstash:
#此處為logstash的ip及端口,即logstash:5044
host: [""]
#需要設(shè)置為true,否則不生效
enabled: true
#如果想直接從Filebeat采集數(shù)據(jù)到ElasticSearch,則可以配置output.elasticsearch的相關(guān)配置
運(yùn)行Filebeat
運(yùn)行:
./filebeat -e -c filebeat.yml -d "publish"
此時(shí)可以看到Filebeat會(huì)將配置的path下的log發(fā)送到Logstash;然后在elk中,Logstash處理完數(shù)據(jù)之后就會(huì)發(fā)送到ElasticSearch。但我們想做的是通過(guò)elk進(jìn)行數(shù)據(jù)分析,因此導(dǎo)入到ElasticSearch的數(shù)據(jù)必須是JSON格式的。
這是之前我的單條日志的格式:
2019-10-22 10:44:03.441 INFO rmjk.interceptors.IPInterceptor Line:248 - {"clientType":"1","deCode":"0fbd93a286533d071","eaType":2,"eaid":191970823383420928,"ip":"xx.xx.xx.xx","model":"HONOR STF-AL10","osType":"9","path":"/applicationEnter","result":5,"session":"ef0a5c4bca424194b29e2ff31632ee5c","timestamp":1571712242326,"uid":"130605789659402240","v":"2.2.4"}
導(dǎo)入之后不好分析,之后又想到使用Logstash的filter中的grok來(lái)處理日志使之變成JSON格式之后再導(dǎo)入到ElasticSearch中,但是由于我的日志中的參數(shù)是不固定的,發(fā)現(xiàn)難度太大了,于是轉(zhuǎn)而使用Logback,將日志直接格式化成JSON之后,再由Filebeat發(fā)送。
Logback配置
我的項(xiàng)目是Spring Boot,在項(xiàng)目中加入依賴(lài):
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>5.2</version>
</dependency>
然后在項(xiàng)目中的resource目錄下加入logback.xml:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!--
說(shuō)明:
1、日志級(jí)別及文件
日志記錄采用分級(jí)記錄,級(jí)別與日志文件名相對(duì)應(yīng),不同級(jí)別的日志信息記錄到不同的日志文件中
例如:error級(jí)別記錄到log_error_xxx.log或log_error.log(該文件為當(dāng)前記錄的日志文件),而log_error_xxx.log為歸檔日志,
日志文件按日期記錄,同一天內(nèi),若日志文件大小等于或大于2M,則按0、1、2...順序分別命名
例如log-level-2013-12-21.0.log
其它級(jí)別的日志也是如此。
2、文件路徑
若開(kāi)發(fā)、測(cè)試用,在Eclipse中運(yùn)行項(xiàng)目,則到Eclipse的安裝路徑查找logs文件夾,以相對(duì)路徑../logs。
若部署到Tomcat下,則在Tomcat下的logs文件中
3、Appender
FILEERROR對(duì)應(yīng)error級(jí)別,文件名以log-error-xxx.log形式命名
FILEWARN對(duì)應(yīng)warn級(jí)別,文件名以log-warn-xxx.log形式命名
FILEINFO對(duì)應(yīng)info級(jí)別,文件名以log-info-xxx.log形式命名
FILEDEBUG對(duì)應(yīng)debug級(jí)別,文件名以log-debug-xxx.log形式命名
stdout將日志信息輸出到控制上,為方便開(kāi)發(fā)測(cè)試使用
-->
<contextName>service</contextName>
<property name="LOG_PATH" value="logs"/>
<!--設(shè)置系統(tǒng)日志目錄-->
<property name="APPDIR" value="doctor"/>
<!-- 日志記錄器,日期滾動(dòng)記錄 -->
<appender name="FILEERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在記錄的日志文件的路徑及文件名 -->
<file>${LOG_PATH}/${APPDIR}/log_error.log</file>
<!-- 日志記錄器的滾動(dòng)策略,按日期,按大小記錄 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 歸檔的日志文件的路徑,例如今天是2013-12-21日志,當(dāng)前寫(xiě)的日志文件路徑為file節(jié)點(diǎn)指定,可以將此文件與file指定文件路徑設(shè)置為不同路徑,從而將當(dāng)前日志文件或歸檔日志文件置不同的目錄。
而2013-12-21的日志文件在由fileNamePattern指定。%d{yyyy-MM-dd}指定日期格式,%i指定索引 -->
<fileNamePattern>${LOG_PATH}/${APPDIR}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- 除按日志記錄之外,還配置了日志文件不能超過(guò)2M,若超過(guò)2M,日志文件會(huì)以索引0開(kāi)始,
命名日志文件,例如log-error-2013-12-21.0.log -->
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>2MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<!-- 追加方式記錄日志 -->
<append>true</append>
<!-- 日志文件的格式 -->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger Line:%-3L - %msg%n</pattern>
<charset>utf-8</charset>
</encoder>
<!-- 此日志文件只記錄info級(jí)別的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>error</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 日志記錄器,日期滾動(dòng)記錄 -->
<appender name="FILEWARN" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在記錄的日志文件的路徑及文件名 -->
<file>${LOG_PATH}/${APPDIR}/log_warn.log</file>
<!-- 日志記錄器的滾動(dòng)策略,按日期,按大小記錄 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 歸檔的日志文件的路徑,例如今天是2013-12-21日志,當(dāng)前寫(xiě)的日志文件路徑為file節(jié)點(diǎn)指定,可以將此文件與file指定文件路徑設(shè)置為不同路徑,從而將當(dāng)前日志文件或歸檔日志文件置不同的目錄。
而2013-12-21的日志文件在由fileNamePattern指定。%d{yyyy-MM-dd}指定日期格式,%i指定索引 -->
<fileNamePattern>${LOG_PATH}/${APPDIR}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- 除按日志記錄之外,還配置了日志文件不能超過(guò)2M,若超過(guò)2M,日志文件會(huì)以索引0開(kāi)始,
命名日志文件,例如log-error-2013-12-21.0.log -->
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>2MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<!-- 追加方式記錄日志 -->
<append>true</append>
<!-- 日志文件的格式 -->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger Line:%-3L - %msg%n</pattern>
<charset>utf-8</charset>
</encoder>
<!-- 此日志文件只記錄info級(jí)別的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>warn</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 日志記錄器,日期滾動(dòng)記錄 -->
<appender name="FILEINFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在記錄的日志文件的路徑及文件名 -->
<file>${LOG_PATH}/${APPDIR}/log_info.log</file>
<!-- 日志記錄器的滾動(dòng)策略,按日期,按大小記錄 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 歸檔的日志文件的路徑,例如今天是2013-12-21日志,當(dāng)前寫(xiě)的日志文件路徑為file節(jié)點(diǎn)指定,可以將此文件與file指定文件路徑設(shè)置為不同路徑,從而將當(dāng)前日志文件或歸檔日志文件置不同的目錄。
而2013-12-21的日志文件在由fileNamePattern指定。%d{yyyy-MM-dd}指定日期格式,%i指定索引 -->
<fileNamePattern>${LOG_PATH}/${APPDIR}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- 除按日志記錄之外,還配置了日志文件不能超過(guò)2M,若超過(guò)2M,日志文件會(huì)以索引0開(kāi)始,
命名日志文件,例如log-error-2013-12-21.0.log -->
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>2MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<!-- 追加方式記錄日志 -->
<append>true</append>
<!-- 日志文件的格式 -->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger Line:%-3L - %msg%n</pattern>
<charset>utf-8</charset>
</encoder>
<!-- 此日志文件只記錄info級(jí)別的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>info</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="jsonLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在記錄的日志文件的路徑及文件名 -->
<file>${LOG_PATH}/${APPDIR}/log_IPInterceptor.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/${APPDIR}/log_IPInterceptor.%d{yyyy-MM-dd}.log</fileNamePattern>
</rollingPolicy>
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<jsonFactoryDecorator class="net.logstash.logback.decorate.CharacterEscapesJsonFactoryDecorator">
<escape>
<targetCharacterCode>10</targetCharacterCode>
<escapeSequence>\u2028</escapeSequence>
</escape>
</jsonFactoryDecorator>
<providers>
<pattern>
<pattern>
{
"timestamp":"%date{ISO8601}",
"uid":"%mdc{uid}",
"requestIp":"%mdc{ip}",
"id":"%mdc{id}",
"clientType":"%mdc{clientType}",
"v":"%mdc{v}",
"deCode":"%mdc{deCode}",
"dataId":"%mdc{dataId}",
"dataType":"%mdc{dataType}",
"vid":"%mdc{vid}",
"did":"%mdc{did}",
"cid":"%mdc{cid}",
"tagId":"%mdc{tagId}"
}
</pattern>
</pattern>
</providers>
</encoder>
</appender>
<!-- 彩色日志 -->
<!-- 彩色日志依賴(lài)的渲染類(lèi) -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wex"
converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
<conversionRule conversionWord="wEx"
converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN"
value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!--encoder 默認(rèn)配置為PatternLayoutEncoder-->
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>utf-8</charset>
</encoder>
<!--此日志appender是為開(kāi)發(fā)使用,只配置最底級(jí)別,控制臺(tái)輸出的日志級(jí)別是大于或等于此級(jí)別的日志信息-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>debug</level>
</filter>
</appender>
<!-- 指定項(xiàng)目中某個(gè)包,當(dāng)有日志操作行為時(shí)的日志記錄級(jí)別 -->
<!-- rmjk.dao.mappe為根包,也就是只要是發(fā)生在這個(gè)根包下面的所有日志操作行為的權(quán)限都是DEBUG -->
<!-- 級(jí)別依次為【從高到低】:FATAL > ERROR > WARN > INFO > DEBUG > TRACE -->
<logger name="rmjk.dao.mapper" level="DEBUG"/>
<logger name="rmjk.service" level="DEBUG"/>
<!--顯示日志-->
<logger name="org.springframework.jdbc.core" additivity="false" level="DEBUG">
<appender-ref ref="STDOUT"/>
<appender-ref ref="FILEINFO"/>
</logger>
<!-- 打印json日志 -->
<logger name="IPInterceptor" level="info" additivity="false">
<appender-ref ref="jsonLog"/>
</logger>
<!-- 生產(chǎn)環(huán)境下,將此級(jí)別配置為適合的級(jí)別,以免日志文件太多或影響程序性能 -->
<root level="INFO">
<appender-ref ref="FILEERROR"/>
<appender-ref ref="FILEWARN"/>
<appender-ref ref="FILEINFO"/>
<!-- 生產(chǎn)環(huán)境將請(qǐng)stdout,testfile去掉 -->
<appender-ref ref="STDOUT"/>
</root>
</configuration>
其中的關(guān)鍵為:
<logger name="IPInterceptor" level="info" additivity="false">
<appender-ref ref="jsonLog"/>
</logger>
在需要打印的文件中引入slf4j:
private static final Logger LOG = LoggerFactory.getLogger("IPInterceptor");
MDC中放入需要打印的信息:
MDC.put("ip", ipAddress);
MDC.put("path", servletPath);
MDC.put("uid", paramMap.get("uid") == null ? "" : paramMap.get("uid").toString());
此時(shí)如果使用了LOG.info("msg")
的話,打印的內(nèi)容會(huì)輸入到日志的message中,日志格式如下:
修改Logstash配置
修改/usr/config/logstash
目錄下的beats-input.conf:
input {
beats {
port => 5044
codec => "json"
}
}
只加了一句codec => "json"
,但是Logstash會(huì)按照J(rèn)SON格式來(lái)解析輸入的內(nèi)容。
因?yàn)樾薷牧伺渲茫貑lk:
docker restart elk
這樣,當(dāng)我們的日志生成完畢之后,使用Filebeat導(dǎo)入到elk中,就可以通過(guò)Kibana來(lái)進(jìn)行日志分析了。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。