ELK是elastic公司提供的一套完整的日誌收集以及前端展現的解決方案,是三個產品的首字母縮寫,分別是ElasticSearch、Logstash和Kibana。前端
其中Logstash負責對日誌進行處理,如日誌的過濾、日誌的格式化等;ElasticSearch具備強大的文本搜索能力,所以做爲日誌的存儲容器;而Kibana負責前端的展現。java
ELK搭建架構以下圖: linux
加入了filebeat用於從不一樣的客戶端收集日誌,而後傳遞到Logstash統一處理。spring
由於ELK是三個產品,能夠選擇依次安裝這三個產品。docker
這裏選擇使用Docker安裝ELk。json
Docker安裝ELk也能夠選擇分別下載這三個產品的鏡像並運行,可是本次使用直接下載elk的三合一鏡像來安裝。瀏覽器
所以首先要保證已經有了Docker的運行環境,Docker運行環境的搭建請查看:https://blog.csdn.net/qq1311256696/article/details/85277220ruby
有了Docker環境以後,在服務器運行命令:bash
docker pull sebp/elk
服務器
這個命令是在從Docker倉庫下載elk三合一的鏡像,總大小爲2個多G,若是發現下載速度過慢,能夠將Docker倉庫源地址替換爲國內源地址。
下載完成以後,查看鏡像:
docker images
在/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
。
有了鏡像以後直接啓動便可:
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的意思是後臺運行容器;
-p的意思是宿主機端口:容器端口,即將容器中使用的端口映射到宿主機上的某個端口,ElasticSearch的默認端口是9200和9300,因爲個人機器上已經運行了3臺ElasticSearch實例,所以此處將映射端口進行了修改;
-v的意思是宿主機的文件|文件夾:容器的文件|文件夾,此處將容器中elasticsearch 的數據掛載到宿主機的/var/data/elk
上,以防容器重啓後數據的丟失;而且將logstash的配置文件掛載到宿主機的/usr/config/logstash
目錄。
--name的意思是給容器命名,命名是爲了以後操做容器更加方便。
若是你以前搭建過ElasticSearch的話,會發現搭建的過程當中有各類錯誤,可是使用docker搭建elk的過程當中並無出現那些錯誤。
運行後查看容器:
docker ps
查看容器日誌:
docker logs -f elk
進入容器:
docker exec -it elk /bin/bash
修改配置後重啓容器:
docker restart elk
瀏覽器輸入http://my_host:5601/ 便可看到kinaba界面。此時ElasticSearch中尚未數據,須要安裝Filebeat採集數據到elk中。
Filebeat用於採集數據並上報到Logstash或者ElasticSearch,在須要採集日誌的服務器上下載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
進入filebeat,修改filebeat.yml。
filebeat.prospectors: - type: log #須要設置爲true配置才能生效 enabled: true path: #配置須要採集的日誌路徑 - /var/log/*.log #能夠打一個tag之後分類使用 tag: ["my_tag"] #對應ElasticSearch的type document_type: my_type setup.kibana: #此處爲kibana的ip及端口,即kibana:5601 host: "" output.logstash: #此處爲logstash的ip及端口,即logstash:5044 host: [""] #須要設置爲true,不然不生效 enabled: true #若是想直接從Filebeat採集數據到ElasticSearch,則能夠配置output.elasticsearch的相關配置
運行:
./filebeat -e -c filebeat.yml -d "publish"
此時能夠看到Filebeat會將配置的path下的log發送到Logstash;而後在elk中,Logstash處理完數據以後就會發送到ElasticSearch。但咱們想作的是經過elk進行數據分析,所以導入到ElasticSearch的數據必須是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"}
導入以後很差分析,以後又想到使用Logstash的filter中的grok來處理日誌使之變成JSON格式以後再導入到ElasticSearch中,可是因爲個人日誌中的參數是不固定的,發現難度太大了,因而轉而使用Logback,將日誌直接格式化成JSON以後,再由Filebeat發送。
個人項目是Spring Boot,在項目中加入依賴:
<dependency> <groupId>net.logstash.logback</groupId> <artifactId>logstash-logback-encoder</artifactId> <version>5.2</version> </dependency>
而後在項目中的resource目錄下加入logback.xml:
<?xml version="1.0" encoding="UTF-8"?> <configuration> <!-- 說明: 一、日誌級別及文件 日誌記錄採用分級記錄,級別與日誌文件名相對應,不一樣級別的日誌信息記錄到不一樣的日誌文件中 例如:error級別記錄到log_error_xxx.log或log_error.log(該文件爲當前記錄的日誌文件),而log_error_xxx.log爲歸檔日誌, 日誌文件按日期記錄,同一天內,若日誌文件大小等於或大於2M,則按0、一、2...順序分別命名 例如log-level-2013-12-21.0.log 其它級別的日誌也是如此。 二、文件路徑 若開發、測試用,在Eclipse中運行項目,則到Eclipse的安裝路徑查找logs文件夾,以相對路徑../logs。 若部署到Tomcat下,則在Tomcat下的logs文件中 三、Appender FILEERROR對應error級別,文件名以log-error-xxx.log形式命名 FILEWARN對應warn級別,文件名以log-warn-xxx.log形式命名 FILEINFO對應info級別,文件名以log-info-xxx.log形式命名 FILEDEBUG對應debug級別,文件名以log-debug-xxx.log形式命名 stdout將日誌信息輸出到控制上,爲方便開發測試使用 --> <contextName>service</contextName> <property name="LOG_PATH" value="logs"/> <!--設置系統日誌目錄--> <property name="APPDIR" value="doctor"/> <!-- 日誌記錄器,日期滾動記錄 --> <appender name="FILEERROR" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 正在記錄的日誌文件的路徑及文件名 --> <file>${LOG_PATH}/${APPDIR}/log_error.log</file> <!-- 日誌記錄器的滾動策略,按日期,按大小記錄 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 歸檔的日誌文件的路徑,例現在天是2013-12-21日誌,當前寫的日誌文件路徑爲file節點指定,能夠將此文件與file指定文件路徑設置爲不一樣路徑,從而將當前日誌文件或歸檔日誌文件置不一樣的目錄。 而2013-12-21的日誌文件在由fileNamePattern指定。%d{yyyy-MM-dd}指定日期格式,%i指定索引 --> <fileNamePattern>${LOG_PATH}/${APPDIR}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <!-- 除按日誌記錄以外,還配置了日誌文件不能超過2M,若超過2M,日誌文件會以索引0開始, 命名日誌文件,例如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級別的 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>error</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender> <!-- 日誌記錄器,日期滾動記錄 --> <appender name="FILEWARN" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 正在記錄的日誌文件的路徑及文件名 --> <file>${LOG_PATH}/${APPDIR}/log_warn.log</file> <!-- 日誌記錄器的滾動策略,按日期,按大小記錄 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 歸檔的日誌文件的路徑,例現在天是2013-12-21日誌,當前寫的日誌文件路徑爲file節點指定,能夠將此文件與file指定文件路徑設置爲不一樣路徑,從而將當前日誌文件或歸檔日誌文件置不一樣的目錄。 而2013-12-21的日誌文件在由fileNamePattern指定。%d{yyyy-MM-dd}指定日期格式,%i指定索引 --> <fileNamePattern>${LOG_PATH}/${APPDIR}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <!-- 除按日誌記錄以外,還配置了日誌文件不能超過2M,若超過2M,日誌文件會以索引0開始, 命名日誌文件,例如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級別的 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>warn</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender> <!-- 日誌記錄器,日期滾動記錄 --> <appender name="FILEINFO" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 正在記錄的日誌文件的路徑及文件名 --> <file>${LOG_PATH}/${APPDIR}/log_info.log</file> <!-- 日誌記錄器的滾動策略,按日期,按大小記錄 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 歸檔的日誌文件的路徑,例現在天是2013-12-21日誌,當前寫的日誌文件路徑爲file節點指定,能夠將此文件與file指定文件路徑設置爲不一樣路徑,從而將當前日誌文件或歸檔日誌文件置不一樣的目錄。 而2013-12-21的日誌文件在由fileNamePattern指定。%d{yyyy-MM-dd}指定日期格式,%i指定索引 --> <fileNamePattern>${LOG_PATH}/${APPDIR}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <!-- 除按日誌記錄以外,還配置了日誌文件不能超過2M,若超過2M,日誌文件會以索引0開始, 命名日誌文件,例如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級別的 --> <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> <!-- 彩色日誌 --> <!-- 彩色日誌依賴的渲染類 --> <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 默認配置爲PatternLayoutEncoder--> <encoder> <pattern>${CONSOLE_LOG_PATTERN}</pattern> <charset>utf-8</charset> </encoder> <!--此日誌appender是爲開發使用,只配置最底級別,控制檯輸出的日誌級別是大於或等於此級別的日誌信息--> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>debug</level> </filter> </appender> <!-- 指定項目中某個包,當有日誌操做行爲時的日誌記錄級別 --> <!-- rmjk.dao.mappe爲根包,也就是隻要是發生在這個根包下面的全部日誌操做行爲的權限都是DEBUG --> <!-- 級別依次爲【從高到低】: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> <!-- 生產環境下,將此級別配置爲適合的級別,以避免日誌文件太多或影響程序性能 --> <root level="INFO"> <appender-ref ref="FILEERROR"/> <appender-ref ref="FILEWARN"/> <appender-ref ref="FILEINFO"/> <!-- 生產環境將請stdout,testfile去掉 --> <appender-ref ref="STDOUT"/> </root> </configuration>
其中的關鍵爲:
<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());
此時若是使用了LOG.info("msg")
的話,打印的內容會輸入到日誌的message中,日誌格式以下:
修改/usr/config/logstash
目錄下的beats-input.conf:
input { beats { port => 5044 codec => "json" } }
只加了一句codec => "json"
,可是Logstash會按照JSON格式來解析輸入的內容。
由於修改了配置,重啓elk:
docker restart elk
這樣,當咱們的日誌生成完畢以後,使用Filebeat導入到elk中,就能夠經過Kibana來進行日誌分析了。 轉評贊就是最大的鼓勵