日志管理

查看日志信息对于运维开发人员来说是非常必要的,常见的查看日志情况如下:

  • 当应用程序或服务出现故障时,查看日志可帮助快速定位问题,了解故障前后的系统状态
  • 通过分析日志,可以监控应用程序性能,识别性能瓶颈或资源消耗异常
  • 在开发和测试过程中,查看日志有助于开发人员调试代码、定位并修复bug,同时也是验证应用程序功能是否正常的重要依据

日志采集方式

在项目的logback.xml文件定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
<?xml version="1.0" encoding="UTF-8"?>  
<configuration>
<!-- 定义日志文件的存放路径 -->
<property name="log.path" value="/home/ruoyi/logs" />
<!-- 定义日志输出的格式 -->
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />

<!-- 控制台输出日志的配置 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>

<!-- 系统信息日志文件的配置 -->
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-info.log</file>
<!-- 基于时间的滚动策略 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件的命名模式 -->
<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 保留日志的最大历史天数 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<!-- 过滤日志级别,只记录DEBUG级别的日志 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>

<!-- 系统错误日志文件的配置 -->
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-error.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<!-- 过滤日志级别,只记录ERROR级别的日志 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>

<!-- 用户访问日志文件的配置 -->
<appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-user.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>

<!-- 设置系统模块(com.ruoyi)的日志级别为INFO -->
<logger name="com.ruoyi" level="info" />
<!-- 设置Spring框架的日志级别为WARN -->
<logger name="org.springframework" level="warn" />

<!-- 根日志处理器的配置,默认级别为INFO,只输出到控制台 -->
<root level="info">
<appender-ref ref="console" />
</root>

<!-- 另一个根日志处理器的配置,用于同时输出系统信息和错误日志 -->
<!-- 注意:通常一个<configuration>中只应有一个<root>元素,这里的配置可能是为了展示目的而重复,实际使用时应合并 -->
<root level="info">
<appender-ref ref="file_info" />
<appender-ref ref="file_error" />
</root>

<!-- 设置用户操作日志的级别为INFO,并指定输出到用户访问日志文件 -->
<logger name="sys-user" level="info">
<appender-ref ref="sys-user"/>
</logger>
</configuration>

具体的生成的日志文件列表如下:

9376ac80-31c5-46fb-b99c-98a46526e823

  • 目前有三类文件,分别是sys-error.log、sys-info.log、sys-user.log文件
  • 文件中不带日期记录的当天的日志
  • sys-info.log记录的是系统日志,包含了运行日志和错误日志(较全)
  • sys-error.log记录的是错误日志,排查错误一般看这个文件
  • sys-user.log记录的是用户登录相关的日志数据

Linux命令查看日志

docker logs 命令

  • 基本用法:docker logs [OPTIONS] CONTAINER,其中CONTAINER是容器的名称或ID,OPTIONS是可选参数

  • 实时查看日志:使用-f--follow参数,可以实时跟踪容器的日志输出。

    • 例如,docker logs -f mycontainer
  • 查看日志尾部内容:使用--tail参数,可以仅显示日志的最后几行

    • 例如,docker logs --tail=100 mycontainer

进入容器内部查看(不推荐)

如果容器的日志被写入到文件系统中,可以通过docker exec命令进入容器内部查看日志文件。

  • 进入容器:docker exec -it mycontainer /bin/bash(或/bin/sh,取决于容器内的shell)
  • 查看日志的所有内容:(cat | more | less) sys-info.2024-08-01.log
  • 实时监控日志的变化
    • 实时监控日志变化:tail -f sys-info.log
    • 实时监控日志部100行日志:tail -n 100 f sys-info.log (常用)
    • 实时监控日志部100行日志:head -n 100 f sys-info.log
  • 按照关键字找日志的信息(常用)
    • 查询日志文件中包含error的日志: cat -n sys-info.log | grep 'error'
    • 日志太多,分页查询日志:cat -n sys-info.log | grep 'error' | more

ELK日志管理

什么是ELK

ELK是一个开源的日志管理平台,由三个核心组件组成:Elasticsearch、Logstash 和 Kibana。这三个组件共同协作,提供了一个从日志收集、处理、存储到分析、可视化的完整解决方案

  1. Elasticsearch:这是一个基于Lucene的搜索引擎,用于存储和搜索日志数据。它提供了强大的全文搜索功能,支持复杂的查询和聚合操作,并且能够处理大规模的日志数据
  2. Logstash:这是一个日志收集和处理工具
  3. 将其转换为Elasticsearch可以理解的格式。Logstash还提供了丰富的过滤功能,可以对日志数据进行清洗、转换和增强
  4. Kibana:这是一个Web界面,用于可视化Elasticsearch中的日志数据。它提供了直观的图形和工具,使用户能够轻松地搜索、分析和可视化日志数据,从而更好地理解系统的运行状态和性能

环境准备

1. 服务启动

在提供的虚拟机中,已经安装了ELK这三个组件,直接启动即可:

1
2
3
docker start es
docker start kibana
docker start logstash

这三个服务占用内存较大,平常开发阶段可关闭

注意:如果ES容器的宿主机IP地址不为192.168.100.168,还需要进入logstash容器中修改配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 进入 Logstash 容器
docker exec -it logstash bash

# 查看 pipeline 配置
cat /usr/share/logstash/pipeline/logstash.conf

# 修改配置
cat > /usr/local/logstash/pipeline/logstash.conf << EOF
input {
tcp {
mode => "server"
host => "0.0.0.0"
port => 5044
codec => json_lines
}
}

output {
elasticsearch {
hosts => ["http://192.168.2.17:9200"] # 修改为正确的ES地址
index => "log-%{+YYYY.MM.dd}"
codec => "json"
}
stdout {
codec => rubydebug
}
}
EOF
  1. logback.xml 配置logstash

在zzyl-admin模块中引入logstash相关的依赖

1
2
3
4
5
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>6.6</version>
</dependency>

在zzyl-admin模块下新增一个文件:logback-logstash.xml(和logback.xml同级),内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 日志存放路径 -->
<property name="log.path" value="/home/ruoyi/logs" />
<!-- 日志输出格式 -->
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />

<!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>

<appender name="logstash" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<!--logstash的服务地址和端口,可以实际情况设置-->
<destination>192.168.2.17:5044</destination>
<!-- 日志输出编码 -->
<encoder charset="UTF-8" class="net.logstash.logback.encoder.LogstashEncoder">
<providers>
<timestamp>
<timeZone>UTC</timeZone>
</timestamp>
<pattern>
<pattern>
{
<!--应用名称 -->
"app": "ruoyi-admin",
<!--打印时间 -->
"timestamp": "%d{yyyy-MM-dd HH:mm:ss.SSS}",
<!--线程名称 -->
"thread": "%thread",
<!--日志级别 -->
"level": "%level",
<!--日志名称 -->
"logger_name": "%logger",
<!--日志信息 -->
"message": "%msg",
<!--日志堆栈 -->
"stack_trace": "%exception"
}
</pattern>
</pattern>
</providers>
</encoder>
</appender>

<!-- 系统日志输出 -->
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-info.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>DEBUG</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>

<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-error.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>ERROR</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>

<!-- 用户访问日志输出 -->
<appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-user.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 按天回滚 daily -->
<fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>

<!-- 系统模块日志级别控制 -->
<logger name="com.ruoyi" level="info" />
<!-- Spring日志级别控制 -->
<logger name="org.springframework" level="warn" />

<root level="info">
<appender-ref ref="console" />
</root>

<!--系统操作日志-->
<root level="info">
<appender-ref ref="logstash" />
<appender-ref ref="file_info" />
<appender-ref ref="file_error" />
</root>

<!--系统用户操作日志-->
<logger name="sys-user" level="info">
<appender-ref ref="sys-user"/>
</logger>
</configuration>

logstash的服务地址和端口要根据自己的情况设置

在正式环境集成logstash,方便监控日志,所以需修改application-prod.yml文件,指定新创建的日志文件

1
2
3
4
5
logging:
config: classpath:logback-logstash.xml
level:
com.zzyl: debug
org.springframework: warn

使用ELK检索日志

1. 查看索引文件

对接项目之后,可以启动项目,产生一些日志数据

然后打开kibana, http://192.168.2.17:5601/(容器所在宿主机的ip地址和端口)找到索引管理,导航:Stack Management –> 索引管理

efb0962e-b1b8-4bd8-b19c-d65b92aff286

可以看到,日志文件都是按照日期进行区分的,每天一个日志索引文件

2. 添加索引模式

如果想用kibana方便的查看日志的数据,可以添加索引模式,如下图

6c5ae6c4-4e70-4212-ba1c-95a7e7d98983

上图中,点击创建索引模式,然后可以添加索引模式,如下图

3eaeec07-8325-42dd-9a0f-0f05c8a8f593

然后,选择时间戳作为时间段即可,就可以完成了,如下图

5b1736a3-2975-4fdd-b8cf-5207ec37f3f2

完成后的效果:

a58509d6-8b03-4c8b-a689-50e5626ca0ea

**3.检索日志 **

打开Discover

9df29e33-d946-49a9-a972-a336a9be7e4b

检索日志,选择不同的索引,可以按照不同的字段检索,或者在输入框直接输入内容,也是可以的

2f6a369f-4211-4689-ab3e-9fb4ed0aee4c

其他检索技巧

  1. 模糊搜索

    • 使用通配符*可以匹配任意数量的字符,?可以匹配单个字符。例如,nursing*会匹配任何以“nursing”开头的单词
  2. 时间范围查询

    • 一般检查时间检索的字段为:@timestamp,可以提供以下几种时间范围查询方式
      • f8bb0262-c814-45da-b52c-0276e301bcb4
      • 按照年查询:@timestamp < "2024"
      • 按照年月查询:@timestamp < "2024-09"
      • 按照年月日查询:@timestamp:"2024-09-05"
      • 按照年月日时分秒查询:@timestamp < "2024-09-02T21:55:59"
  3. 字段搜索:通过指定字段名进行搜索,可以更精确地定位到包含特定信息的日志条目。例如,message:"error"会匹配消息字段中包含“error”的日志条目