Ruoyi框架之系统监控的编写

大体思路

1. 项目初始化

  • 使用Spring Initializr来生成一个基本的Spring Boot项目,选择Spring WebSpring Boot ActuatorSpring Data等依赖。

2. 定义模块结构

  • 创建一个独立的监控模块,放置在com.example.monitor包下,包含以下部分:
    • controller:用于处理HTTP请求,返回监控数据。
    • service:用于封装监控逻辑。
    • model:用于定义监控数据的模型类。

3. 获取系统资源信息

  • 使用Java的ManagementFactoryOperatingSystemMXBean来获取CPU、内存等资源的信息。
  • 创建SystemMetricsService类,获取并封装系统资源信息。

4. 暴露监控数据

  • 创建一个RESTful API控制器SystemMetricsController,通过HTTP GET请求获取当前系统的CPU、内存和虚拟机相关信息。

5. 前端展示

  • 编写简单的HTML或使用Vue.js来展示这些监控数据。
  • 通过AJAX定时请求后台API,动态展示监控数据。

6. 优化与扩展

  • 监控更多的系统指标,如磁盘使用、网络流量等。
  • 使用Spring Boot Actuator来暴露更多的监控端点。
  • 实现告警机制,当系统指标超过阈值时,发送通知或记录日志

具体实现

1. 项目初始化

首先,使用 Spring Initializr 初始化一个Spring Boot项目。选择以下依赖:

  • Spring Web
  • Spring Boot Actuator

生成项目后,下载并解压,然后导入到你的IDE中。

2. 定义模块结构

src/main/java/com/example/monitor包下,创建以下目录结构:

com/example/monitor/
├── controller
│   └── SystemMetricsController.java
├── service
│   └── SystemMetricsService.java
└── model
    └── SystemMetrics.java

3. 获取系统资源信息

首先,创建一个SystemMetrics模型类,用于封装系统资源信息:

package com.example.monitor.model;

public class SystemMetrics {
    private double cpuLoad;
    private long totalMemory;
    private long freeMemory;
    private long maxMemory;

    // getters and setters
    public double getCpuLoad() {
        return cpuLoad;
    }

    public void setCpuLoad(double cpuLoad) {
        this.cpuLoad = cpuLoad;
    }

    public long getTotalMemory() {
        return totalMemory;
    }

    public void setTotalMemory(long totalMemory) {
        this.totalMemory = totalMemory;
    }

    public long getFreeMemory() {
        return freeMemory;
    }

    public void setFreeMemory(long freeMemory) {
        this.freeMemory = freeMemory;
    }

    public long getMaxMemory() {
        return maxMemory;
    }

    public void setMaxMemory(long maxMemory) {
        this.maxMemory = maxMemory;
    }
}

然后,创建SystemMetricsService服务类,用于获取CPU和内存信息:

package com.example.monitor.service;

import com.example.monitor.model.SystemMetrics;
import org.springframework.stereotype.Service;

import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;

@Service
public class SystemMetricsService {

    public SystemMetrics getSystemMetrics() {
        OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean();
        SystemMetrics metrics = new SystemMetrics();

        // 获取CPU负载(系统负载平均值)
        metrics.setCpuLoad(osBean.getSystemLoadAverage());

        // 获取JVM内存使用情况
        long totalMemory = Runtime.getRuntime().totalMemory();
        long freeMemory = Runtime.getRuntime().freeMemory();
        long maxMemory = Runtime.getRuntime().maxMemory();

        metrics.setTotalMemory(totalMemory);
        metrics.setFreeMemory(freeMemory);
        metrics.setMaxMemory(maxMemory);

        return metrics;
    }
}

4. 暴露监控数据

接下来,创建一个控制器SystemMetricsController,通过RESTful API来提供监控数据:

package com.example.monitor.controller;

import com.example.monitor.model.SystemMetrics;
import com.example.monitor.service.SystemMetricsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/metrics")
public class SystemMetricsController {

    @Autowired
    private SystemMetricsService systemMetricsService;

    @GetMapping
    public SystemMetrics getSystemMetrics() {
        return systemMetricsService.getSystemMetrics();
    }
}

启动应用后,你可以通过访问 http://localhost:8080/api/metrics 来查看监控数据。

5. 前端展示

如果你想简单展示这些数据,可以创建一个简单的HTML页面,使用JavaScript的fetch API来获取数据:

src/main/resources/static/下创建一个index.html文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>System Metrics</title>
</head>
<body>
    <h1>System Metrics</h1>
    <div>
        <p>CPU Load: <span id="cpuLoad"></span></p>
        <p>Total Memory: <span id="totalMemory"></span></p>
        <p>Free Memory: <span id="freeMemory"></span></p>
        <p>Max Memory: <span id="maxMemory"></span></p>
    </div>

    <script>
        async function fetchMetrics() {
            const response = await fetch('/api/metrics');
            const data = await response.json();

            document.getElementById('cpuLoad').textContent = data.cpuLoad;
            document.getElementById('totalMemory').textContent = data.totalMemory;
            document.getElementById('freeMemory').textContent = data.freeMemory;
            document.getElementById('maxMemory').textContent = data.maxMemory;
        }

        setInterval(fetchMetrics, 5000); // 每5秒获取一次数据
        fetchMetrics(); // 初始获取数据
    </script>
</body>
</html>

启动Spring Boot应用后,访问 http://localhost:8080/ 就可以看到系统的监控数据了。

6. 优化与扩展

你可以继续扩展和优化这个模块,以下是一些建议:

  1. 监控更多指标:通过扩展SystemMetricsService,添加磁盘使用情况、网络流量等监控信息。

  2. 告警机制:当某些指标超过预设的阈值时,可以通过邮件、短信或者其他通知方式进行告警。

  3. 集成Spring Boot Actuator:Actuator可以提供更多的内置监控端点,帮助你监控应用健康状况。你可以在application.properties中启用更多的Actuator端点。

通过这些步骤,你将能够从零开始构建一个自定义的服务监控模块。这个模块可以帮助你更好地了解和管理你的应用。

具体时间监控的逻辑和模块使用

要实现对系统资源的监控,具体来说就是获取并展示系统的CPU负载、内存使用情况等信息。我们会使用Java标准库中的ManagementFactoryOperatingSystemMXBean来获取这些系统资源数据。

1. CPU 负载监控

Java 提供了 OperatingSystemMXBean 接口,可以通过 ManagementFactory 获取系统的管理接口,用于监控CPU负载。

  • CPU 负载:系统的平均负载(通常是最近1分钟的平均值),可以通过getSystemLoadAverage()方法获取。

2. 内存使用情况监控

Java 的 Runtime 类提供了用于获取JVM内存使用情况的方法:

  • Total Memory:JVM的总内存,可以通过 Runtime.getRuntime().totalMemory() 获取。
  • Free Memory:JVM当前的空闲内存,可以通过 Runtime.getRuntime().freeMemory() 获取。
  • Max Memory:JVM可以使用的最大内存,可以通过 Runtime.getRuntime().maxMemory() 获取。

SystemMetricsService类的实现

我们将以上的监控逻辑封装到 SystemMetricsService 服务类中,该类负责从系统中提取这些信息,并返回给调用方。

package com.example.monitor.service;

import com.example.monitor.model.SystemMetrics;
import org.springframework.stereotype.Service;

import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;

@Service
public class SystemMetricsService {

    public SystemMetrics getSystemMetrics() {
        // 获取操作系统管理接口
        OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean();
        
        // 实例化我们的数据模型类
        SystemMetrics metrics = new SystemMetrics();

        // 获取并设置 CPU 负载
        metrics.setCpuLoad(osBean.getSystemLoadAverage());

        // 获取并设置内存使用情况
        long totalMemory = Runtime.getRuntime().totalMemory();
        long freeMemory = Runtime.getRuntime().freeMemory();
        long maxMemory = Runtime.getRuntime().maxMemory();

        metrics.setTotalMemory(totalMemory);
        metrics.setFreeMemory(freeMemory);
        metrics.setMaxMemory(maxMemory);

        return metrics;
    }
}

代码解释

  • OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean();:这个对象可以用来获取与操作系统相关的信息,如CPU负载。

  • osBean.getSystemLoadAverage();:获取系统的平均负载,这个值通常表示最近一段时间(通常是1分钟)内的系统负载。

  • Runtime.getRuntime().totalMemory():返回当前JVM可以使用的内存总量。

  • Runtime.getRuntime().freeMemory():返回当前JVM中可用的空闲内存。

  • Runtime.getRuntime().maxMemory():返回JVM的最大可用内存。

总结

到目前为止,我们已经完成了SystemMetricsService类的实现,这个类能够收集系统的CPU负载和内存使用情况。下一步,我们将创建一个控制器,将这些监控数据暴露为RESTful API,以便可以通过HTTP请求来访问这些信息。

扩展和优化

更新 SystemMetricsService

我们会在 SystemMetricsService 中增加对磁盘使用情况和线程信息的获取。

package com.example.monitor.service;

import com.example.monitor.model.SystemMetrics;
import org.springframework.stereotype.Service;

import java.io.File;
import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;
import java.lang.management.ThreadMXBean;

@Service
public class SystemMetricsService {

    public SystemMetrics getSystemMetrics() {
        // 获取操作系统管理接口
        OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean();
        
        // 实例化我们的数据模型类
        SystemMetrics metrics = new SystemMetrics();

        // 获取并设置 CPU 负载
        metrics.setCpuLoad(osBean.getSystemLoadAverage());

        // 获取并设置内存使用情况
        long totalMemory = Runtime.getRuntime().totalMemory();
        long freeMemory = Runtime.getRuntime().freeMemory();
        long maxMemory = Runtime.getRuntime().maxMemory();

        metrics.setTotalMemory(totalMemory);
        metrics.setFreeMemory(freeMemory);
        metrics.setMaxMemory(maxMemory);

        // 获取并设置磁盘使用情况
        File diskPartition = new File("/"); // "/" 表示根目录,可以改为指定分区
        long totalDiskSpace = diskPartition.getTotalSpace();
        long freeDiskSpace = diskPartition.getFreeSpace();
        long usableDiskSpace = diskPartition.getUsableSpace();

        metrics.setTotalDiskSpace(totalDiskSpace);
        metrics.setFreeDiskSpace(freeDiskSpace);
        metrics.setUsableDiskSpace(usableDiskSpace);

        // 获取并设置线程信息
        ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
        metrics.setThreadCount(threadBean.getThreadCount());

        return metrics;
    }
}

优化数据展示

为了使数据更易理解,我们将以下内容转换为人类可读的格式:

  1. 内存使用情况:将字节转换为MB或GB,并计算使用率。
  2. 磁盘使用情况:将字节转换为GB,并计算使用率。

我们已经在 SystemMetrics 类中实现了内存和磁盘使用的百分比计算方法。接下来,我们将展示如何将这些数据在前端以更易理解的方式显示出来。

前端展示改进

我们将增强前端展示,通过进度条来显示资源的使用率,数据格式转换成MB或GB等。

更新 index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>System Metrics</title>
    <style>
        .progress-bar {
            width: 100%;
            background-color: #f3f3f3;
            border-radius: 5px;
            margin-bottom: 10px;
        }

        .progress-bar-fill {
            display: block;
            height: 20px;
            background-color: #4caf50;
            border-radius: 5px;
            width: 0;
            transition: width 0.5s ease-in-out;
        }

        .metric {
            font-weight: bold;
        }
    </style>
</head>
<body>
    <h1>System Metrics</h1>

    <div>
        <p class="metric">CPU Load: <span id="cpuLoad"></span></p>
        
        <p class="metric">Memory Usage: <span id="usedMemory"></span> / <span id="maxMemory"></span> (MB)
            <span id="memoryUsagePercentage"></span>%</p>
        <div class="progress-bar">
            <span id="memoryProgress" class="progress-bar-fill"></span>
        </div>

        <p class="metric">Disk Usage: <span id="usedDiskSpace"></span> / <span id="totalDiskSpace"></span> (GB)
            <span id="diskUsagePercentage"></span>%</p>
        <div class="progress-bar">
            <span id="diskProgress" class="progress-bar-fill"></span>
        </div>

        <p class="metric">Thread Count: <span id="threadCount"></span></p>
    </div>

    <script>
        function formatBytes(bytes, decimals = 2) {
            if (bytes === 0) return '0 Bytes';
            const k = 1024;
            const dm = decimals < 0 ? 0 : decimals;
            const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
            const i = Math.floor(Math.log(bytes) / Math.log(k));
            return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
        }

        async function fetchMetrics() {
            const response = await fetch('/api/metrics');
            const data = await response.json();

            // 更新CPU负载
            document.getElementById('cpuLoad').textContent = data.cpuLoad.toFixed(2);

            // 更新内存使用情况
            document.getElementById('usedMemory').textContent = formatBytes(data.totalMemory - data.freeMemory);
            document.getElementById('maxMemory').textContent = formatBytes(data.maxMemory);
            document.getElementById('memoryUsagePercentage').textContent = data.memoryUsagePercentage.toFixed(2);
            document.getElementById('memoryProgress').style.width = data.memoryUsagePercentage + '%';

            // 更新磁盘使用情况
            document.getElementById('usedDiskSpace').textContent = formatBytes(data.totalDiskSpace - data.freeDiskSpace, 2);
            document.getElementById('totalDiskSpace').textContent = formatBytes(data.totalDiskSpace, 2);
            document.getElementById('diskUsagePercentage').textContent = data.diskUsagePercentage.toFixed(2);
            document.getElementById('diskProgress').style.width = data.diskUsagePercentage + '%';

            // 更新线程数
            document.getElementById('threadCount').textContent = data.threadCount;
        }

        setInterval(fetchMetrics, 5000); // 每5秒获取一次数据
        fetchMetrics(); // 初始获取数据
    </script>
</body>
</html>

前端代码解释

  • 格式化字节数据formatBytes() 函数将字节转换为更容易理解的MB、GB等。

  • 进度条progress-bar-fill 用于展示资源使用率的进度条。通过调整其宽度展示内存和磁盘使用的百分比。

  • 周期性数据获取:通过setInterval,每5秒从后端获取最新的系统资源数据,并动态更新页面内容。

总结

​ 监控模块不仅能够监控更多的系统资源信息(如磁盘使用和线程数量),而且通过优化后的数据展示方式,用户可以更直观地理解这些信息。你可以根据需求进一步扩展这个模块,比如增加更多的监控指标、告警机制或更复杂的前端展示。

Spring Boot Actuator 是一个用于监控和管理 Spring Boot 应用程序的子项目,它提供了一组内置的端点,用于暴露应用程序的运行时信息,如健康状态、环境属性、日志配置、JVM 指标等。接下来,我们将 Spring Boot Actuator 集成到当前的监控模块中。

集成Spring Boot Actuator

1. 引入 Spring Boot Actuator 依赖

首先,确保在 pom.xml 文件中引入了 Spring Boot Actuator 的依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

这个依赖会自动包含 Actuator 的所有核心功能。

2. 配置 Actuator 端点

接下来,我们可以在 application.properties 文件中配置 Actuator 的端点访问路径及其安全性。默认情况下,只有部分端点是启用的,我们可以通过配置启用其他端点。

src/main/resources/application.properties 文件中添加以下配置:

# 设置所有 Actuator 端点可用
management.endpoints.web.exposure.include=*

# 自定义 Actuator 端点路径(可选)
management.endpoints.web.base-path=/actuator

# 开启健康检查
management.endpoint.health.show-details=always

3. 访问 Actuator 端点

现在,Spring Boot Actuator 已经集成到应用中。你可以通过浏览器或其他 HTTP 客户端访问以下一些常用的端点:

  • 健康检查: http://localhost:8080/actuator/health - 提供应用程序的健康状态信息。
  • JVM 指标: http://localhost:8080/actuator/metrics - 提供 JVM 和系统的各种指标,如 CPU 使用、内存使用等。
  • 环境信息: http://localhost:8080/actuator/env - 提供应用程序的环境属性。
  • HTTP 路径映射: http://localhost:8080/actuator/mappings - 显示所有的 HTTP 请求映射。

4. 自定义监控信息

你可以通过创建自定义的 @Endpoint 来扩展 Actuator 的功能,例如将之前实现的 SystemMetrics 集成到 Actuator 中。

自定义 Endpoint

我们可以创建一个自定义的 Actuator 端点,通过它来暴露我们之前实现的 SystemMetrics 信息。

package com.example.monitor.actuator;

import com.example.monitor.model.SystemMetrics;
import com.example.monitor.service.SystemMetricsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.stereotype.Component;

@Component
@Endpoint(id = "systemMetrics")
public class SystemMetricsEndpoint {

    private final SystemMetricsService systemMetricsService;

    @Autowired
    public SystemMetricsEndpoint(SystemMetricsService systemMetricsService) {
        this.systemMetricsService = systemMetricsService;
    }

    @ReadOperation
    public SystemMetrics systemMetrics() {
        return systemMetricsService.getSystemMetrics();
    }
}

访问自定义的 SystemMetrics Endpoint

  • 现在,你可以通过 http://localhost:8080/actuator/systemMetrics 访问自定义的系统监控信息。这将返回我们之前收集的 CPU、内存、磁盘等监控数据。

5. 安全配置(可选)

如果你希望限制 Actuator 端点的访问,可以使用 Spring Security 进行保护。你可以为敏感的端点配置用户名和密码,或根据需求进行权限控制。

总结

通过集成 Spring Boot Actuator,你不仅获得了开箱即用的应用监控能力,还可以自定义和扩展 Actuator 的功能,将特定的监控数据集成到应用程序的监控中。你现在可以访问各种内置的和自定义的端点,实时监控应用的健康状况和性能指标。

实现监控的逻辑代码为:

        OperatingSystemMXBean operatingSystemMXBean =
                ManagementFactory.getOperatingSystemMXBean();//获取当前操作系统的接口,检测CPU

        log.info("CPU的消耗为:{}",operatingSystemMXBean.getSystemLoadAverage());

        //监控JVM虚拟机

        log.info("JVM的总内存为:{}GB",Runtime.getRuntime().totalMemory()/1024/1024);
        log.info("JVM的空余内存为:{}GB",Runtime.getRuntime().freeMemory()/1024/1024);
        log.info("JVM的最大内存为:{}GB",Runtime.getRuntime().maxMemory()/1024/1024);

        //监控本地磁盘
        File file = new File("D:\\");  // 磁盘路径,D盘,单位为B

        log.info("磁盘总容量为:{}GB",file.getTotalSpace()/1024/1024/1024);
        log.info("磁盘剩余容量为:{}GB",file.getFreeSpace()/1024/1024/1024);
        log.info("磁盘最大容量为:{}GB",file.getUsableSpace()/1024/1024/1024);


        //监控线程
        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
        log.info("当前线程数:{}",threadMXBean.getThreadCount());
    }

还有就是前端的异步刷新,在前面已写!

日志编写之SLF4J

1. maven依赖

<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-logging</artifactId>
     <version>2.6.13</version>
 </dependency>
 <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.32</version>
</dependency>

2. slf4j配置结构

configuration:配置根目录
  conversionRule:彩色日志,配置之后控制台输出的日志信息是彩色的,非常好看
  appender:日志输出配置规则
    file:日志文件输出路径
    encoder:日志文件输出格式
      pattern:日志文件输出模板格式
      charset:日志文件输出编码方式
    rollingPolicy:日志记录器的滚动策略;简单来说就是一天过后把前一天的日志文件名变成 "2021-xx-xx.log";每个日志文件最大尺寸多少,超过尺寸后多余部分生成一个新的日志文件等,一些动态的策略处理
      fileNamePattern:滚动策略输出日志文件目录,及文件名模板;以尺寸策略来说,超过10m的文件,多余部分输出的文件名和路劲为appender的file值,已满10m的部分输出路径为此值
      timeBasedFileNamingAndTriggeringPolicy:设置日志文件尺寸滚动策略
        maxFilesSize:单个日志文件最大size
      maxHistory:日志文件最大保存时间
    filter:日志文件记录数据过滤器
      level:只记录对应等级的记录
      onMatch:常量:ACCEPT
      onMismatch:常量:DENY
  root:设置日志记录写入和控制器打印等级,低于这个等级的记录不写入日志文件,不在控制器打印
    appender-ref:日志规则生效列表,值为appender对应的name属性,只有name存在appender-ref的appender配置才会生效;
  logger:指定某个报下的日志输出规则,配置之后只要是这个包下产生的记录,都是遵循此规则,并且与root中配置的appender-ref重复的话,会叠加生效
    appender-ref:使用的日志生成规则
  springProfile:设置不同环境的配置,上诉标签除了configuration都可以放置在此标签内;会根据不同的环境,使用不同的规则

3. 案例详解

3.1 configuration

配置文件的根节点

<?xml version="1.0" encoding="UTF-8"?>
<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
<!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true -->
<!-- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<configuration scan="true" scanPeriod="10 seconds" debug="false">
    <conversionRule ....></conversionRule>
    <root ....></root>
    <appender ...></appender>
</configuration>
3.2 conversionRule

配置完彩色日志后,就可以在模板内使用彩色变量,使得输出的日志记录变成彩色的,稍后在pattern中举例使用彩色变量,现在先配置

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="10 seconds" debug="false">   ......一些其他的配置  
    <!-- 彩色日志 -->    <!-- conversionWord:指定在pattern中使用的别名 -->    <!-- converterClass:指定使用彩色日志渲染类 -->
    <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"/>
    <!-- 彩色日志格式 -->
</configuration>
3.3 appender

日志输出规则

<!--输出到控制台-->
<!--    name 表示规则名称,提供与root -> appender-ref 的使用-->
<!--    class 表示使用的日志输出类 ConsoleAppender表示为控制台日志输出类,使用此类日志会输出的控制台-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <!--        配置日志输出过滤条件-->
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
        <!--            只输出info等级及以上的日志-->
        <!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
        <level>info</level>
    </filter>
    <!--        配置日志输出格式-->
    <encoder>
        <!--            日志输出格式模板,请注意一下格式是为了更好的描述,在打印的时候出现换行情况,实际使用要将其写成一行,换行符由%n代替-->
        <Pattern>
            ${
            CONSOLE_LOG_PATTERN:- //常量
            %clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} //%clr表示使用conversionRule中conversionWord为clr的converterClass输出()中的变量文本;%d表示日期;{faint}表示颜色;颜色值有[faint:灰白,red,green,yellow,blue,magenta,cyan]
            %clr(${LOG_LEVEL_PATTERN:-%5p})
            %clr(${PID:- }){magenta}
            %clr(---){faint}
            %clr([%15.15t]){faint}
            %clr(%-40.40logger{39}){cyan}
            %clr(:){cyan}
            %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}
            }
        </Pattern>
        <!-- 日志输出字符集,没错就是为了解决中文乱码-->
        <charset>UTF-8</charset>
    </encoder>
</appender>
3.4 Pattern中特殊符号含义
-X号: X信息输出时左对齐;
%p: //输出日志信息优先级,即DEBUG,INFO,WARN,ERROR,FATAL,
%d: //输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921
%r: //输出自应用启动到输出该log信息耗费的毫秒数
%c: //输出日志信息所属的类目,通常就是所在类的全名
%t: //输出产生该日志事件的线程名
%l: //输出日志事件的发生位置,相当于%C.%M(%F:%L)的组合,包括类目名、发生的线程,以及在代码中的行数。举例:Testlog4.main (TestLog4.java:10)
%x: //输出和当前线程相关联的NDC(嵌套诊断环境),尤其用到像java servlets这样的多客户多线程的应用中。
%%: //输出一个”%”字符
%F: //输出日志消息产生时所在的文件名称
%L: //输出代码中的行号
%m://输出代码中指定的消息,产生的日志具体信息
%n: //输出一个回车换行符,Windows平台为”\r\n”,Unix平台为”\n”输出日志信息换行
3.5 appender

根据等级设置多个日志输出规则,把不同等级的日志分别存储,配置多个appender

<!--    设置一个变量,以免Pattern重复复制模板表达式-->
<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="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
        <level>info</level>
    </filter>
    <encoder>
        <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
        <!-- 设置字符集 -->
        <charset>UTF-8</charset>
    </encoder>
</appender>


<!--输出到文件-->

<!-- 时间滚动输出 level为 DEBUG 日志 -->
<appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <!-- 正在记录的日志文件的路径及文件名 -->
    <file>${log.path}/log_debug.log</file>
    <!--日志文件输出格式-->
    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        <charset>UTF-8</charset> <!-- 设置字符集 -->
    </encoder>
    <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <!-- 日志归档 -->
        <fileNamePattern>${log.path}/debug/log-debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
        <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
            <maxFileSize>100MB</maxFileSize>
        </timeBasedFileNamingAndTriggeringPolicy>
        <!--日志文件保留天数-->
        <maxHistory>15</maxHistory>
    </rollingPolicy>
    <!-- 此日志文件只记录debug级别的 -->
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
        <level>debug</level>
        <onMatch>ACCEPT</onMatch>
        <onMismatch>DENY</onMismatch>
    </filter>
</appender>

<!-- 时间滚动输出 level为 INFO 日志 -->
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <!-- 正在记录的日志文件的路径及文件名 -->
    <file>${log.path}/log_info.log</file>
    <!--日志文件输出格式-->
    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        <charset>UTF-8</charset>
    </encoder>
    <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <!-- 每天日志归档路径以及格式 -->
        <fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
        <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
            <maxFileSize>100MB</maxFileSize>
        </timeBasedFileNamingAndTriggeringPolicy>
        <!--日志文件保留天数-->
        <maxHistory>15</maxHistory>
    </rollingPolicy>
    <!-- 此日志文件只记录info级别的 -->
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
        <level>info</level>
        <onMatch>ACCEPT</onMatch>
        <onMismatch>DENY</onMismatch>
    </filter>
</appender>

<!-- 时间滚动输出 level为 WARN 日志 -->
<appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <!-- 正在记录的日志文件的路径及文件名 -->
    <file>${log.path}/log_warn.log</file>
    <!--日志文件输出格式-->
    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        <charset>UTF-8</charset> <!-- 此处设置字符集 -->
    </encoder>
    <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
        <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
            <maxFileSize>1KB</maxFileSize>
        </timeBasedFileNamingAndTriggeringPolicy>
        <!--日志文件保留天数-->
        <maxHistory>15</maxHistory>
    </rollingPolicy>
    <!-- 此日志文件只记录warn级别的 -->
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
        <level>warn</level>
        <onMatch>ACCEPT</onMatch>
        <onMismatch>DENY</onMismatch>
    </filter>
</appender>


<!-- 时间滚动输出 level为 ERROR 日志 -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <!-- 正在记录的日志文件的路径及文件名 -->
    <file>${log.path}/log_error.log</file>
    <!--日志文件输出格式-->
    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        <charset>UTF-8</charset> <!-- 此处设置字符集 -->
    </encoder>
    <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
        <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
            <maxFileSize>100MB</maxFileSize>
        </timeBasedFileNamingAndTriggeringPolicy>
        <!--日志文件保留天数-->
        <maxHistory>15</maxHistory>
    </rollingPolicy>
    <!-- 此日志文件只记录ERROR级别的 -->
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
        <level>ERROR</level>
        <onMatch>ACCEPT</onMatch>
        <onMismatch>DENY</onMismatch>
    </filter>
</appender>
3.6 root

appender必须要放置在root中,才能生效

<!-- 表明日志输出等级,只有info及info以上的日志才会在控制台和日志文件中输出-->
<root level="info">
    <appender-ref ref="CONSOLE"/> <!--ref 表明使用一个name=CONSOLE的appender标签,的规则-->
    <appender-ref ref="DEBUG_FILE"/>
    <appender-ref ref="INFO_FILE"/>
    <appender-ref ref="WARN_FILE"/>
    <appender-ref ref="ERROR_FILE"/>
</root>
3.7 logger

更精确的指定annender生效范围

<!--    name:指定日志输出规则生效范围,可以是包名和类型;-->
<!--    level表示只输出info等级及以上的日志记录-->
<!--    下例会将com.example.mybitesstudy02.service包下的日志只输出info等级及以上的日志记录,并且使用CONSOLE的apender规则-->
<logger name="com.example.mybitesstudy02.service" level="info">
    <appender-ref ref="CONSOLE"/>
</logger>
3.8 springProfile

指定不同环境下的日志配置,注意springProfile判断当前是处于什么环境,取决于spring boot配置文件(application.properties)的spring.profiles.active值,默认是default

<!--    这里表示不管在所有环境都使用的配置-->
 <root level="info">
     <appender-ref ref="DEBUG_FILE"/>
     <appender-ref ref="INFO_FILE"/>
     <appender-ref ref="WARN_FILE"/>
     <appender-ref ref="ERROR_FILE"/>
 </root>

 <!--这里表示在dev环境使用的配置-->
 <springProfile name="dev">
     <root level="info">
         <appender-ref ref="CONSOLE"/>
     </root>
 </springProfile>
 
 <!--这里表示在pro环境使用的配置-->
 <springProfile name="pro">
     <root level="info">
         <appender-ref ref="CONSOLE"/>
         <appender-ref ref="INFO_FILE"/>
         <appender-ref ref="ERROR_FILE"/>
     </root>
 </springProfile>

4. 超详细配置案例

<?xml version="1.0" encoding="UTF-8"?>
<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
<!-- scan:当此属性设置为true时,配置文档如果发生改变,将会被重新加载,默认值为true -->
<!-- scanPeriod:设置监测配置文档是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。
                 当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<configuration  scan="true" scanPeriod="10 seconds">
    <contextName>logback</contextName>

    <!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义后,可以使“${}”来使用变量。 -->
    <!-- 日志存放路径 -->
    <springProperty scope="context" name="log.path" source="logging.file.path"/>
    <springProperty scope="context" name="log.name" source="logging.file.name"/>

    <!--0. 日志格式和颜色渲染 -->
    <!-- 彩色日志依赖的渲染类 -->
    <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}}"/>
    <!--1. 输出到控制台-->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>debug</level>
        </filter>
        <encoder>
            <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
            <!-- 设置字符集 -->
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <!--2. 输出到文档-->
    <!-- 2.1 level为 DEBUG 日志,时间滚动输出  -->
    <appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在记录的日志文档的路径及文档名 -->
        <file>${log.path}/${log.name}_debug.log</file>
        <!--日志文档输出格式-->
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset> <!-- 设置字符集 -->
        </encoder>
        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 日志归档 -->
            <fileNamePattern>${log.path}/${log.name}-debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日志文档保留天数-->
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <!-- 此日志文档只记录debug级别的 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>debug</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- 2.2 level为 INFO 日志,时间滚动输出  -->
    <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在记录的日志文档的路径及文档名 -->
        <file>${log.path}/${log.name}_info.log</file>
        <!--日志文档输出格式-->
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 每天日志归档路径以及格式 -->
            <fileNamePattern>${log.path}/${log.name}-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日志文档保留天数-->
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <!-- 此日志文档只记录info级别的 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>info</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- 2.3 level为 WARN 日志,时间滚动输出  -->
    <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在记录的日志文档的路径及文档名 -->
        <file>${log.path}/${log.name}_warn.log</file>
        <!--日志文档输出格式-->
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
        </encoder>
        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log.path}/${log.name}-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日志文档保留天数-->
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <!-- 此日志文档只记录warn级别的 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>warn</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- 2.4 level为 ERROR 日志,时间滚动输出  -->
    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在记录的日志文档的路径及文档名 -->
        <file>${log.path}/${log.name}_error.log</file>
        <!--日志文档输出格式-->
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
        </encoder>
        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log.path}/${log.name}-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日志文档保留天数-->
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <!-- 此日志文档只记录ERROR级别的 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    

    <!-- 4. 最终的策略 -->
    <root level="info">
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="DEBUG_FILE" />
        <appender-ref ref="INFO_FILE" />
        <appender-ref ref="WARN_FILE" />
        <appender-ref ref="ERROR_FILE" />
    </root>

    <!-- 4.2 生产环境:输出到文档
    <springProfile name="pro">
        <root level="info">
            <appender-ref ref="CONSOLE" />
            <appender-ref ref="DEBUG_FILE" />
            <appender-ref ref="INFO_FILE" />
            <appender-ref ref="ERROR_FILE" />
            <appender-ref ref="WARN_FILE" />
        </root>
    </springProfile> -->

</configuration>
注意:

在resources目录下创建logback-spring.xml 并编写配置文件

在application.yaml里配置 xml的路径

在要输出日志的类上打上@Slf4j注解

用log.info/debug.....来输出自己想要的日志

支持用{}拼接字符串!!!

举例

 OperatingSystemMXBean operatingSystemMXBean =
                ManagementFactory.getOperatingSystemMXBean();//获取当前操作系统的接口,检测CPU

        log.info("CPU的消耗为:{}",operatingSystemMXBean.getSystemLoadAverage());

        //监控JVM虚拟机

        log.info("JVM的总内存为:{}GB",Runtime.getRuntime().totalMemory()/1024/1024);
        log.info("JVM的空余内存为:{}GB",Runtime.getRuntime().freeMemory()/1024/1024);
        log.info("JVM的最大内存为:{}GB",Runtime.getRuntime().maxMemory()/1024/1024);

        //监控本地磁盘
        File file = new File("D:\\");  // 磁盘路径,D盘,单位为B

        log.info("磁盘总容量为:{}GB",file.getTotalSpace()/1024/1024/1024);
        log.info("磁盘剩余容量为:{}GB",file.getFreeSpace()/1024/1024/1024);
        log.info("磁盘最大容量为:{}GB",file.getUsableSpace()/1024/1024/1024);


        //监控线程
        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
        log.info("当前线程数:{}",threadMXBean.getThreadCount());
    }