Springboot中如何记录日志

日志体系整体介绍

日志一直在系统中占据这十分重要的地位,他是我们在系统发生故障时用来排查问题的利器,也是我们做操作审计的重要依据。那么如何记录好日志呢?选择什么框架来记录日志,是不是日志打越多越好,带着这些问题我们今天一起来讨论下springboot应用如何记录好日志。

在我们java工程中,日志框架一般分为两层,日志门面和日志实现。

日志门面

日志门面是一个抽象层,它定义了一组统一的日志接口给用户使用。隐藏了底层日志实现的细节,提供了一种与具体日志实现解耦的方式。常见的日志门面有SLF4J(Simple Logging Facade for Java)和Apache Commons Logging等。日志门面的作用包括:

  • 提供统一的日志接口,方便在应用程序中记录日志。
  • 实现日志级别的控制,可以在不同环境中灵活地调整日志输出级别。
  • 提供日志消息的格式化和输出控制。

日志实现

日志实现是一种具体的日志框架,常见的包括Logback、Log4j、Java Util Logging等。不同的日志实现可能提供不同的功能和性能特性。最基础的都提供了以下共功能:

  • 实现日志门面定义的接口,提供日志记录的功能,可以将日志消息输出到指定的目标,如控制台、文件、数据库等
  • 提供灵活的日志配置选项,如输出格式、目标、日志级别等

总结一下,日志门面提供了统一的接口,让应用程序使用,而日志实现则负责将日志消息输出到具体的目标。那么我们可以直接用日志实现来记录日志吗,当然是可以的,但是使用日志门面,有他的优点:

  • 日志和代码解耦,每个不同的日志实现记录日志的api是不一样的,定义的日志级别可能也有差别,那么如果我们直接使用日志实现比如log4j在代码里记录日志,就需要用到log4j的api。这样将来有一天如果需要切换到logback,则需要将所有log4j的代码替换成logback。但是使用日志门面,我们只需要切换配置文件就可以了。
  • 兼容性:使用日志门面可以兼容不同的日志实现,因为大多数日志门面都支持多种日志实现。这样可以在项目中使用不同的日志库,以满足不同的开发环境和需求。
  • 统一接口:使用日志门面提供的统一接口,有利于维护和各个类的日志处理方式统一,提高代码的可读性和可维护性。
    在这里插入图片描述

springboot如何配置Logback做日志框架

  1. \src\main\resources目录下添加logback.xml的配置文件,
  2. 配置日志的输出路径、输出格式等
<?xml version="1.0" encoding="UTF-8"?>
<configuration>

    <!--定义日志文件的存储地址-->
    <property name="LOG_PATH" value="/home/app/logs" />

    <!-- 输出到控制台 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%logger{36}] - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 配置info级别日志的输出路径,滚动策略 -->
    <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/info/info-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
            <maxHistory>30</maxHistory>
            <maxFileSize>50MB</maxFileSize>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%logger{36}] - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 配置error级别日志的输出路径,滚动策略 -->
    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/error/error-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
            <maxHistory>30</maxHistory>
            <maxFileSize>50MB</maxFileSize>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%logger{36}] - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 设置日志级别 -->
    <root level="INFO">
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="INFO_FILE" />
        <appender-ref ref="ERROR_FILE" />
    </root>
</configuration>

滚动策略

rollingPolicy 用于配置日志文件的滚动策略,决定何时创建新的日志文件或对现有日志文件进行归档。常见的日志滚动策略有以下几种:

  1. TimeBasedRollingPolicy:按时间滚动日志文件。该策略会根据指定的时间模式,如日期或时间间隔,创建新的日志文件。可配置属性有:

    • fileNamePattern:指定日志文件的命名模式。可以使用日期格式和/或通配符来表示日志文件的命名规则。例如,logs/app-%d{yyyy-MM-dd}.log 表示按日期切割日志文件,并以app-2022-01-01.log的格式命名。
    • maxHistory:指定保留的历史日志文件的数量。当滚动发生时,将保留最大数量的历史日志文件,较旧的日志文件将被删除。例如,7 表示最多保留 7 个历史日志文件。
    • cleanHistoryOnStart:指定在启动时是否清除历史日志文件。为 true,即在启动时删除所有历史日志文件,只保留当前日志文件。如果设置为 false,则会保留历史日志文件,但仍按照日期规则创建新的日志文件。
    • totalSizeCap:指定所有历史日志文件的总大小上限。当滚动发生时,如果历史日志文件的总大小超过了这个上限,较旧的日志文件将被删除,以保持总大小在限制范围内。可以使用B、 KB、 MB、 GB作为单位。
  2. SizeAndTimeBasedRollingPolicy:继承自TimeBasedRollingPolicy,支持按照文件大小滚动的特性,可以通过maxFileSize来配置:

    • maxFileSize:指定单个日志文件的最大大小。当日志文件达到或超过这个大小时,将触发滚动操作。可以使用可以使用B、 KB、 MB、 GB作为单位。
  3. FixedWindowRollingPolicy:按指定的固定窗口大小滚动日志文件。该策略会创建固定数量的日志文件,并在写满一个日志文件后,将日志写入下一个文件,循环使用这些日志文件。可以通过 minIndexmaxIndex 属性指定日志文件的索引范围。

    • minIndex:指定最小索引值。当滚动时,索引将从这个值开始递增。默认值为1。
    • maxIndex:指定最大索引值。当索引达到这个值时,最旧的日志文件将被删除。默认值为7。

过滤器

在Logback中,可以使用Filter来对日志事件进行筛选和过滤。常用的过滤器有:ThresholdFilterLevelFilter

  1. ThresholdFilter:基于日志级别进行过滤,只有达到或超过指定级别的日志事件才会被接受。以下是一个示例配置:

    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
      <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
        <level>WARN</level>
      </filter>
    </appender>
    
  2. LevelFilter:LevelFilter也是基于日志级别进行过滤,可以根据指定的最低日志级别(level)来决定是否接受或拒绝日志事件,允许更细粒度地控制不同appender或logger的过滤行为。以下是一个示例配置:

    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
      <filter class="ch.qos.logback.classic.filter.LevelFilter">
        <level>WARN</level>
        <onMatch>DENY</onMatch>
        <onMismatch>ACCEPT</onMismatch>
      </filter>
    </appender>
    

    <onMatch>标签指定了满足过滤条件时的操作为DENY(拒绝),<onMismatch>标签指定了未满足过滤条件时的操作为ACCEPT(接受)。

springboot中如何通过日志切面将业务和日志进行解耦

在我们的应用中,经常需要记录每一次请求的参数、请求的结果、耗时以及请求异常时需要记录异常堆栈,用来在必要的时候排查问题。如果直接记录的话,存在日志和代码耦合,日志风格不统一,日志排查困难等问题,所以我们往往通过AOP的方式将日志和代码进行解耦,让程序员可以专注在业务的开发上。

@Aspect
@Component
@Slf4j
public class LogAspect {

    @Around(value = "execution(* com.pinellia.framework.controller.*.*(..))")
    public Object logRequest(ProceedingJoinPoint joinPoint) throws Throwable {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        String requestUrl = request.getRequestURL().toString();

        Object result = null;
        try {
            result = joinPoint.proceed();
        } catch (Throwable e) {
            log.error("Request url: {} failed...", requestUrl, e);
            throw e;
        } finally {
            stopWatch.stop();
            log.info("Request url: {}, params: {}, response: {}, cost: {}",
                    requestUrl, joinPoint.getArgs(), GsonUtil.toJson(result), stopWatch.getTotalTimeMillis());
        }

        return result;
    }
}

相关文章

使用Go Validator在Go应用中有效验证数据

Go Validator是一个开源的包,为Go结构体提供强大且易于使用的数据验证功能。该库允许开发者为其数据结构定义自定义验证规则,并确保传入的数据满足指定的条件。Go Validator支持内置验证器、自定义验证器,甚至允许您链式多个验证规则以满足更复杂的数据验证需求。如果内置验证器无法满足您的需求,您可以通过定义自己的验证函数来创建自定义验证器。这个功能允许您实现特定于应用程序需求的验证逻辑。

【Vue3】使用ref与reactive创建响应式对象

先来简单介绍一下ref,它可以定义响应式的变量let xxx = ref(初始值)。**返回值:**一个RefImpl的实例对象,简称ref对象或refref对象的value属性是响应式的。JSxxx.value,但模板中不需要.value,直接使用即可。对于let name = ref('张三')来说,name不是响应式的,name.value是响应式的。下面我们看一看上图红框中代表的意思是,我们哪里需要响应就在哪个里面导入上述代码即可。

如何设置页面恢复运行事件触发回调

由于 Android 原生的 resume 和 pause 事件不能区分是压后台导致还是页面切换导致,所以 pageResume 和 pagePause 事件是通过 JSAPI 调用记录回调的,仅适用于同一个 session 内 Window 之间的互相切换。当一个 WebView 界面重新回到栈顶时,例如从后台被唤起、锁屏界面恢复、从下个页面回退,会触发页面恢复运行(resume)事件。如果这个界面是通过 popWindow 或 popTo 到达,且传递了 data 参数,则此页可以获取到这些参数。

日常遇到Maven出现依赖版本/缓存问题通用思路。

如果怀疑是本地仓库中缓存的依赖有问题,可以手动删除本地仓库(默认位置在用户的.m2/repository目录下),但这是一个较为极端的做法,因为这会删除所有项目的所有本地依赖,之后Maven将不得不重新下载这些依赖。针对于这样的问题 首先我们的第一思路 就是怀疑到是缓存的问题,那么我在这里去描述一下 我们遇到这类通用类的问题如何解决。检查项目的pom.xml文件,确认依赖声明正确无误,没有冲突的版本号或不正确的依赖范围。版本问题导致的,但是我确认过了一下的一些操作 依然没有解决我的问题。

什么是tomcat?tomcat是干什么用的?

Tomcat是一个开源的、轻量级的应用服务器,是Apache软件基金会的一个项目。它实现了Java Servlet、JavaServer Pages(JSP)和Java Expression Language(EL)等Java技术,用于支持在Java平台上运行的动态Web应用程序。AJP是用于Apache服务器与Tomcat之间进行通信的协议,通常用于将动态生成的内容传递给Apache服务器进行处理。它能够运行Servlet和JSP,提供了一个环境,使得开发者能够构建和运行基于Java的Web应用。

C# winfrom中excel文件导入导出

在C#交流群里,看到很多小伙伴在excel数据导入导出到C#界面上存在疑惑,所以今天专门做了这个主题,希望大家有所收获!环境:win10+vs2017界面:主要以演示为主,所以没有做优化,然后主界面上添加两个按钮,分别命名为ExportExcel和ImportExcel,添加两个dataGridView,分别是dataGridView1和dataGridView2然后在窗体加载程序中给dataGr...

Java 与 JavaScript 的区别与联系

Java 和 JavaScript 两种编程语言在软件开发中扮演着重要的角色。尽管它们都以“Java”命名,但实际上它们是完全不同的语言,各有其独特的特点和用途。本文将深入探讨 Java 和 JavaScript 的区别与联系,帮助大家更好地理解它们在编程世界中的作用。

C语言中的作用域与生命周期

但是全局变量被 static 修饰之后,外部链接属性就变成了内部链接属性,只能在自己所在的源文件内部使用了,其他源文件,即使声明了,也是无法正常使用的。结论:static修饰局部变量改变了变量的生命周期,生命周期改变的本质是改变了变量的存储类型,本来一个局部变量是存储在内存的栈区的,但是被 static 修饰后存储到了静态区。extern 是用来声明外部符号的,如果一个全局的符号在A文件中定义的,在B文件中想使用,就可以使用extern进行声明,然后使用。全局变量的生命周期是:整个程序的生命周期。

Python和Java的区别(不断更新)

运行效率:一般来说,Java的运行效率要高于Python,这主要是因为Java是编译型语言,其代码在执行前会进行预编译,而Python是解释型语言,边解释边执行。而Python没有类似的强大虚拟机,但它的核心是可以很方便地使用C语言函数或C++库,这使得Python可以轻松地与底层硬件进行交互。**类型系统:**Java是一种静态类型语言,所有变量需要先声明(类型)才能使用,且类型在编译时就已经确定。总的来说,Python和Java各有其优势和特点,选择哪种语言取决于具体的项目需求、开发环境以及个人偏好。

服务器与电脑的区别?

服务器是指一种专门提供计算和存储资源、运行特定软件服务的物理或虚拟计算机。服务器主要用于接受和处理来自客户端(如个人电脑、手机等)的请求,并向客户端提供所需的服务或数据。服务器在网络环境中扮演着中心节点的角色,负责存储和管理数据、提供网络服务、处理计算任务等。

windows下ngnix自启动(借助工具winSw)

在windows下安装nginx后,不想每次都手动启动。本文记录下windows下ngnix自启动(借助工具winSw)的操作流程提示:以下是本篇文章正文内容,下面案例可供参考本文记录下windows下ngnix自启动(借助工具winSw)的操作流程。

synchronized 和 Lock 有什么区别?synchronized 和 ReentrantLock 区别是什么?说一下 atomic 的原理?

例如,AtomicInteger 的 incrementAndGet() 方法就是通过 CAS 操作实现的,它首先尝试原子地将共享变量加 1,如果操作成功,则返回新的值,否则重试直到操作成功为止。CAS 操作的原理是,当 V 的值等于 A 时,将 V 的值更新为 B,否则什么也不做。synchronized 和 Lock 都是 Java 中用于实现线程同步的关键字/类库,它们都能够提供对共享资源的安全访问和防止数据竞争的功能,但是在实现方式、特性、适用场景等方面存在一些差异。

SpringBoot security 安全认证(二)——登录拦截器

本节内容:实现登录拦截器,除了登录接口之外所有接口访问都要携带Token,并且对Token合法性进行验证,实现登录状态的保持。核心内容:1、要实现登录拦截器,从Request请求中获取token,从缓存中获取Token并验证登录是否过期,若验证通过则放行;2、实现对拦截器配置,SpringBoot 安全模块使用HttpSecurity 来完成请求安全管理。

Kafka常见生产问题详解

比如,在原有Topic下,可以调整Producer的分区策略,让Producer将后续的消息更多的发送到新增的Partition里,这样可以让各个Partition上的消息能够趋于平衡。思路是可行的,但是重试的次数,发送消息的数量等都是需要考虑的问题。PageCache缓存中的消息是断电即丢失的。因为如果业务逻辑异步进行,而消费者已经同步提交了Offset,那么如果业务逻辑执行过程中出现了异常,失败了,那么Broker端已经接收到了消费者的应答,后续就不会再重新推送消息,这样就造成了业务层面的消息丢失。

深入理解 Java 变量类型、声明及应用

Java 变量 变量是用于存储数据值的容器。在 Java 中,有不同类型的变量,例如: String - 存储文本,例如 &amp;quot;你好&amp;quot;。字符串值用双引号引起来。 int - 存储整数(全数字),没有小数,例如 123 或 -123。 float - 存储浮点数,带有小数,例如 19.

Zookeeper分布式队列实战

ZooKeeper实现队列步骤1.创建队列根节点:在Zookeeper中创建一个持久节点,用作队列的根节点。所有队列元素的节点将放在这个根节点下。2.实现入队操作:当需要将一个元素添加到队列时,可以在队列的根节点下创建一个临时有序节点。节点的数据可以包含队列元素的信息。3.实现出队操作:当需要从队列中取出一个元素时,先获取根节点下的所有子节点。再找到具有最小序号的子节点,获取该节点的数据,删除该节点,然后返回节点的数据。

SpringMVC校验注解不生效

用来实现参数校验功能。Spring使用hibernate-validator作为它的默认实现,我们只需要进行一些简单的注解声明,就可以达到参数校验的功能。但是在实际使用场景中,经常会出现校验没生效的问题。

Web 安全之点击劫持(Clickjacking)攻击详解

点击劫持(Clickjacking)攻击,又称为界面伪装攻击,是一种利用视觉欺骗手段进行攻击的方式。攻击者通过技术手段欺骗用户点击本没有打算点击的位置,当用户在被攻击者攻击的页面上进行操作时,实际点击结果被劫持,从而被攻击者利用。这种攻击方式利用了用户对网站的信任,通过覆盖层(通常是透明的iframe)覆盖在另一个网页之上,使受害者无法察觉。

为什么Java中的String类被设计为final类?

String类作为Java中不可或缺的类之一,被设计成final类带来了不可变性、安全性、可靠性和性能优势。不可变的特性使得String对象在多线程环境下安全共享,提高了应用程序的并发性和性能。此外,String类的设计还符合Java类库的一致性和规范,确保了整个语言的稳定性和可靠性。因此,String类被设计成final类是出于多方面的考虑,以提供最佳的使用体验和编程效率。

bat脚本打开多个黑窗口并执行不同的命令

在使用java -jar运行jar包之前,需要先启动redis,而redis的安装目录与jar包不在同一目录下,所以每次启动项目的时候都需要来回的切换目录。现写了一个bat脚本,用来一键启动redis和jar包。start cmd /k &quot;cd /d redis安装目录 &amp;&amp; redis-server redis.windows.conf&quot;其中,cmd /k命令是不关闭黑窗口的命令,timeout /T 3表示等待3秒,/NOBREAK表示键盘输入不会中断等待。

鸿蒙(ArkUI)开发:实现二级联动

列表的二级联动(Cascading List)是指根据一个列表(一级列表)的选择结果,来更新另一个列表(二级列表)的选项。这种联动可以使用户根据实际需求,快速定位到想要的选项,提高交互体验。例如,短视频中拍摄风格的选择、照片编辑时的场景的选择,本文即为大家介绍如何开发二级联动。

Vue和React的区别 | | React函数式写法和类写法的区别

React 更多的是一个库而不是框架,它更专注于视图层的管理,通过社区和第三方库来进行补充和扩展。类式组件: 类式组件是 ES6 中引入的 class 类的一种用法,它继承自 React.Component,拥有完整的生命周期和内部状态管理能力。它是无状态的,没有生命周期和内部状态。而在 React 中,我们使用 JSX 语法,它是一种 JavaScript 的扩展语法,可以在 JavaScript 中直接编写类似 HTML 的结构。在 React 中,我们可以定义组件的两种方式,即函数式组件和类式组件。

SpringBoot:详解Bean生命周期和作用域

前面我们讲诉了将Bean正确地装配到IoC容器,却未讲诉IoC如何装配和销毁Bean。本篇文章主要讲诉一下Bean的生命周期和作用域。以上就是Bean生命周期和作用域的讲解。
返回
顶部