目录
- 使用 Spring Boot 日志框架
- 为什么使用日志框架?
- 使用 Spring Boot Logging 插件
- 集成 Log4J日志框架
- 将日志输出到 Docker 容器外
- 使用 Docker 容器日志
- Docker 日志驱动
- 使用 Docker 容器日志
- Linux 日志系统:Syslog
- docker 时区问题
- Docker 日志架构
- 搭建应用日志中心
- 开源日志中心: ELK
- 日志收集系统
使用 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 ...");
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
当然还有其他方法,可参考这篇链接
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: 合并多行文本