使用 Spring Boot 日志框架
為什么使用日志框架?
- 日志可以輸出到文件中,而不是輸出到應用程序的控制台中,這樣更容易收集和分析
- 可以通過異步多線程的方式,將日志輸出到文件中,這樣不會影響主線程,可以提高程序的吞吐量,節約性能。
通常使用的日志框架有 Log4J 等。
如何在 Spring Boot 中添加日志框架呢?Spring Boot 自帶了一款名為 Spring Boot Logging 的插件,它已經為我們提供了日志實現。
使用 Spring Boot Logging 插件
Spring Boot 使用 Commons Logging 作為內部的日志框架,而它是一個日志接口,在實際應用中,我們需要為該接口提供相應的日志實現。Spring 的默認日志實現是 Java.Util.Logging,它是 JDK 自帶的日志包,一般場景下很少被用到。Spring Boot 也提供了 Log4J, Logback 這類流行的日志實現,我們只要添加簡單的配置,就能開啟對這些日志的實現。
在 Java 應用程序中,日志一般分為 5 個級別: ERROR,WARN,INFO,DEBUG,TRACE。
Spring Boot Logging 默認輸出到 INFO 級別,如果希望日志可以輸出到 DEBUG 級別,需要在 application.yml 中添加如下配置
logging:
level:
root: DEBUG
在 java 文件中添加
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
...
private static Logger logger = LoggerFactory.getLogger(HelloController.class);
...
logger.debug("log ...");
就可以在運行代碼時看到 debug 級別的日志了。
如果不想關注 Spring Boot 框架的日志,則可將日志級別統一設置成 ERROR ,此時只會輸出 ERROR 級別的日志。隨后,將 Spring Boot 應用程序指定的包設置成 DEBUG 級別的日志,就能看到只有指定包的日志了。
logging:
level:
root: ERROR
demo:
msa: DEBUG
默認情況下日志框架會將日志輸出到控制台中,需要在 application.yml 文件中加入下面配置,才能將日志輸出到文件中。
logging:
level:
root: ERROR
demo:
msa: DEBUG
file: hello.log
集成 Log4J日志框架
首先需要在 pom.xml 中添加如下 Maven 配置,就能在 Spring Boot 中集成 Log4J。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
在第一段中,先排除掉默認的 Logback 日志功能。在第二段的 dependency 配置中,我們自行添加 spring-boot-starter-log4j2 依賴,它是 Spring Boot 提供的 Log4J 插件。
配置完成 Maven 依賴配置后,下面需要在 resources 目錄下添加 log4j2.xml 文件,具體內容如下
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appenders>
<File name="file" fileName="${sys:user.home}/logs/hello.log">
<PatternLayout pattern="%d{HH:mm:ss:SSS} %p %c (%L) - %m%n"/>
</File>
</appenders>
<loggers>
<root level="ERROR">
<appender-ref ref="file"/>
</root>
<logger name="demo.msa" level="DEBUG" />
</loggers>
</configuration>
這個配置文件分成兩部分, appenders 和 loggers 。通過以上配置,就將 Log4j 集成到 Spring Boot 應用中。雖然日志已經成功輸出到了文件中,但我們的微服務是以 Docker 容器的方式來運行的,此時輸出的日志文件仍然和應用程序在一個 Docker 容器中,我們得想辦法將日志文件輸出到 Docker 容器外。也就是將數據和程序分離,以便后續更方便的獲取並分析日志內容。
將日志輸出到 Docker 容器外
最常用的方法就是,通過 Docker 數據卷的方式,將文件路徑掛載到 Docker 容器上,這樣日志文件自然與 Docker 文件分離了。啟動命令如下
[tomcat@ ~]$ docker run -v ~/logs:/root/logs -d -p 18080:8081 demo.msa/mesa-hello:0.0.1-SNAPSHOT
這樣就可以隨時在宿主機上查看 Docker 容器內部的日志了。但我們還需要到文件中去查看,使用 docker logs 的方式獲取日志內容則不會有此限制,一起來看下。
使用 Docker 容器日志
使用 docker logs 命令可以隨時查看 Docker 容器內部應用程序運行時產生的日志,這樣就可以避免首先進入 Docker 容器,在打開應用程序的過程了。
Docker logs 的執行過程如下: 它會監控容器中操作系統的標准輸出設備 (STDOUT), 一旦 STDOUT 有產生,就會將這些數據傳輸到另一個設備中,該設備在 Docker 的世界中被稱為日志驅動(Logging Driver)。
Docker 日志驅動
以 nginx 為例,通過下面 Docker 命令啟動 Nginx 容器
docker run -d -p 80:80 --name nginx nginx
打開瀏覽器,在地址欄輸入 http://localhost:80,就可以看到 nginx 的首頁。這時就可以使用 docker logs 命令,查看 Nginx 容器的日志。
docker logs -f nginx
其中 -f 是指監控日志尾部的意思。
由此看出,只要 Docker 容器內部的應用程序在控制台中有日志輸出,就能通過 docker logs 命令來查看響應的日志。那么 Docker 是如何做到的呢?
首先來執行下 命令
docker info |grep "Logging Driver"
可以得到 Docker 當前設置的日志驅動類型,就是 json-file 。
json-file 表示 JSON 文件,就是說 Docker 容器內部的應用程序輸出的日志,將自動寫入一個 JSON 文件中,並存放在 /var/lib/docker/containers/<container_id>目錄中 <container_id>-json.log ,它就是要找的日志文件。
json-file 只是 Docker 日志驅動的一個默認選項,除了這個選項,我們還可以顯式的指定其他的類型:
none不輸出任何日志syslog容器輸出的日志寫入宿主機的Syslog中- ...
我們可以在 docker run 命令中通過 --log-driver 參數來設置具體的 Docker 日志驅動。並且可以通過 --log-opt 參數來指定對應日志驅動的相關選項。就拿默認的 json-file 來說,可以這樣來啟動 Docker 容器。
docker run -d -p 80:80 --log-driver json-file --log-opt max-size=10m --log-opt max-file=3 --name nginx nginx:1.14
--log-opt 參數有兩個選項:
max-file表示 JSON 文件最多為 3 個max-size表示 JSON 文件最大為 10M,超過 10M 會自動生成新文件
在上面這些日志驅動類型,最為常用的是 Syslog, 它是 Linux 的日志系統,很多日志分析工具都可以從 Syslog 獲取日志。比如流行的 ELK 日志中心,它包括下面 3 個部分
- 日志存儲 Elasticsearch
- 日志收集 Logstash
- 日志查詢 Kibana 負責
在這 3 個組件中, Logstash 用於收集日志, Syslog 寫入的日志可轉發到 Logstash 中,隨后會存儲到 Elasticsearch 中。
使用 Docker 容器日志
Linux 日志系統:Syslog
默認情況下,Linux 操作系統已經安裝了 syslog 軟件包,叫做 Rsyslog。Rsyslog 是 syslog 標准的一種實現。
可以通過下面命令可以查看 Rsyslog 是否已經安裝。
rsyslogd -v
如果要開啟 Rsyslog 服務,必須對 Rsyslog進行配置,打開配置文件
vi /etc/rsyslog.conf
手工開啟(去掉配置前面的注釋),啟動 TCP 鏈接的 Rsyslog 的 514 端口。
# Provides TCP syslog reception
$ModLoad imtcp
$InputTCPServerRun 514
配置文件修改完畢后,手工重啟 Rsyslog 服務。
service rsyslog restart
or
systemctl restart rsyslog
檢查下本地是否對外開啟了 514 端口。
[root@ ~]# netstat -anpt |grep 514
tcp 0 0 0.0.0.0:514 0.0.0.0:* LISTEN 63516/rsyslogd
tcp 0 0 :::514 :::* LISTEN 63516/rsyslogd
下面就啟動 Nginx 容器,並選擇 Syslog 作為日志驅動。
docker run -d -p 80:80 --log-driver syslog --log-opt syslog-address=tcp://localhost:514 --name nginx nginx
其中
--log-driver表示指定 Syslog 為日志驅動--log-opt指定了 Docker 環境可以通過 TCP 協議連接本地的 514 端口
可以使用下面命令來查看 linux 系統日志文件,該文件的內容就是 Syslog 所生成的日志
tail -f /var/log/messages
通常會在一台宿主機上同時運行多個 Docker 容器,如果每個容器都通過 Syslog 來聚合日志,那么在系統日志文件通過 Docker 容器的 ID 是很難識別出是哪個容器的。如何能區分某條日志是來自哪個容器呢?
Docker 日志驅動已經為我們提供了支持,只需要在 --log-opt 參數中添加一個 tag 選項,並在這個選項上給出恰當的命名,就能更好的識別出相應的日志。
docker run -d -p 80:80 --log-driver syslog --log-opt syslog-address=tcp://localhost:514 --log-opt tag="nginx" nginx:1.14
將 tag 選項設置成 nginx (容器名稱),就能在日志中看到帶有 nginx 的標識,這樣我們可以更加容易的識別這條日志來自 Nginx 容器。
如果不指定 tag 選項,則默認的 tag 為容器 ID 的前 12 個字符。也可以在 tag 選項中使用 Docker 已經提供的模板標簽,可將這些標簽理解為 tag 選項中的占位符。
{{.ID}}: 容器 ID 的前 12 個字符{{.FullID}}容器 ID 的完整名稱{{.Name}}容器名稱{{.ImageID}}容器鏡像 ID 的前 12 個字符{{.ImageName}}容器鏡像名稱
下面將這些 tag 標簽來個大融合
docker run -d -p 80:80 -v /etc/localtime:/etc/localtime --log-driver syslog --log-opt syslog-address=tcp://localhost:514 --log-opt tag="{{.ImageName}}/{{.Name}}/{{.ID}}" nginx:1.14
docker 時區問題
docker run -d -p 80:80 -v /etc/localtime:/etc/localtime --log-driver syslog --log-opt syslog-address=tcp://localhost:514 --log-opt tag="{{.ImageName}}/{{.Name}}/{{.ID}}" nginx:1.14
重點是 -v /etc/localtime:/etc/localtime 當然還有其他方法,可參考這篇鏈接 http://www.cnblogs.com/zengming/p/10190317.html
Docker 日志架構
Docker 容器中的應用程序 Application 將日志吸入到標准輸出設備 STDOUT, Docker Daemon 負責從 STDOUT 中獲取日志,並將日志寫入對應的日志驅動中。

當應用程序的日志從 Docker 容器內部寫入宿主機的 Syslog 中后,后面我們要做的就是將 Syslog 中的日志轉發到 ELK 平台的 Logstash 中,從而建立我們需要的日志中心

搭建應用日志中心
開源日志中心: ELK
Elastic 官方推出了 6 款開源產品
- Kibana: 用於數據可視化
- Elasticsearch: 用於數據搜索,分析與存儲
- Logstash: 用於數據收集,將數據存入 Elasticsearch 中
- Beats: 用於數據傳輸,將數據從磁盤傳輸到 Logstash 中
- X-Pack: 提供了一些擴展功能,包括安全,預警,監控,報表和圖形化等
- Elasticsearch Cloud: 提供 Elastic 棧的雲服務,提供公有雲與私有雲的解決方案
日志收集系統
Logstash 是一款開源的數據收集引擎,它既提供了實時管道數據能力,也提供了靈活的插件機制。我們可以自由選擇已有的插件,也能自行開發所需的插件。我們使用 Logstash 更多的時候都在做參數配置,以實現我們所需的功能。
從系統架構的角度來看,它提供了 3 個內部組件,分別是輸入組件,過濾組件,輸出組件,而且每個組件都提供了插件機制。可以將這些組件及其插件想象成一個管道(pipeline),數據從數據源(Data Source)流向 INPUTS,FILTERS,OUTPUT,最終到達 Elasticsearch 中存儲。

上面 3 個組件包含的常用插件如下:
- 輸入插件
- file: 讀取文本文件
- syslog: 讀取 syslog,包含 Rsyslog
- redis: 讀取 Redis 消息隊列
- beats: 處理 Filebeat 事件
- 過濾插件
- grok: 解析日志文本
- mutate: 修改事件字段
- drop: 刪除事件
- clone: 復制事件
- geoip: 添加 IP 地理位置
- 輸出插件
- elasticsearch: 寫入 elasticsearch
- file: 寫入文本文件
- graphite: 一種開源工具,用於存儲與圖形化指標
- statsd: 寫入 statsd 統計服務,監聽 UDP 端口
- 編解碼插件
- json:編解碼為 json 格式
- multiline: 合並多行文本
