网站首页 > java教程 正文
在互联网软件开发的广袤领域中,日志记录宛如一座灯塔,照亮着我们排查系统故障、优化程序性能的道路。对于使用 Spring Boot3 进行开发的互联网软件开发人员而言,日志的重要性更是不言而喻。而当涉及到日志的自定义封装处理操作时,这里面蕴藏着诸多提升开发效率与系统可维护性的技巧与奥秘,接下来就让我们一同深入探索。
Spring Boot3 日志系统概述
Spring Boot 对内部日志使用Commons Logging,但其底层日志实现极为开放,支持Java Util Logging、Log4J2以及Logback等多种框架。在默认配置下,若引入了
spring-boot-starter-logging依赖,Spring Boot 会采用Logback(SLF4J)来进行日志记录。这种组合方式,确保了依赖库中使用Java Util Logging、Commons Logging、Log4J或SLF4J的日志操作都能正常运转。
例如,在一个简单的 Spring Boot 项目中,当我们启动应用时,默认的日志输出格式大致如下:它会清晰地显示日志级别,如ERROR、WARN、INFO、DEBUG、TRACE等,同时还会给出日志名称,通常为源类名的缩写形式。这种默认的日志输出,在消息写入时会回显到控制台,默认记录ERROR、WARN和INFO级别的日志信息。
日志级别的调整与配置
在实际开发过程中,我们常常需要根据不同的场景来灵活调整日志级别。通过在application.properties或application.yaml配置文件中进行相应设置,即可轻松实现这一需求。比如,我们可以使用logging.level.<logger-name>=<level>这样的格式来指定特定类或包的日志级别。其中,<level>的取值范围包括TRACE、DEBUG、INFO、WARN、ERROR、FATAL或OFF ,这些级别定义在LogLevel类中。
假设我们希望将org.springframework.web包下的日志级别设置为DEBUG,以便在开发阶段更详细地了解 Web 层的运行情况,那么在application.properties中添加
logging.level.org.springframework.web=DEBUG即可。若要对整个应用的根日志级别进行调整,例如设置为WARN,让未明确指定日志级别的部分都遵循该级别,可配置logging.level.root=warn。
此外,我们还能通过命令行参数来临时调整日志级别。比如,使用--debug标志启动应用程序,即可启用 “debug” 模式。在这种模式下,核心日志记录器(如嵌入式容器、Hibernate 和 Spring Boot 自身)会输出更多详细信息,帮助我们深入排查问题。同样,使用--trace标志启动应用(或在application.properties中设置trace=true),能够为特定的核心日志记录器开启跟踪日志记录功能。
日志输出到文件
默认情况下,Spring Boot 仅将日志记录到控制台,并不写入日志文件。但在生产环境中,将日志保存到文件以便后续分析是非常必要的操作。我们可以通过设置logging.file.name或logging.file.path属性来实现这一目的。
若在application.properties中添加logging.file.name=myapp.log,应用启动后,日志信息便会同时输出到控制台以及myapp.log文件中。默认情况下,当日志文件大小达到 10MB 时,会进行归档操作,即创建新的日志文件,原文件则按照一定规则进行备份,这有助于我们管理日志文件的大小,避免单个文件过大带来的处理不便。
自定义日志格式
在很多场景下,Spring Boot 默认的日志格式可能无法满足我们的特定需求,此时就需要进行自定义日志格式的设置。以常用的Logback为例,我们可以在resource目录下创建logback-spring.xml文件(建议使用-spring后缀,以便 Spring 更好地控制日志初始化),并在其中定义日志输出格式。
假设我们希望日志输出中包含时间戳、线程名、日志级别、类名以及详细的日志消息,同时将控制台日志和文件日志进行区分配置。在logback-spring.xml中可以这样编写:
<configuration>
<!-- 定义控制台输出 -->
<appender name="STDOUT" 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>
<!-- 定义文件输出 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/myapp.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/myapp.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
</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="STDOUT" />
<appender-ref ref="FILE" />
</root>
</configuration>
上述配置中,STDOUT appender 负责将日志输出到控制台,FILE appender 则将日志写入文件,并按照时间进行滚动归档。encoder标签内的pattern定义了日志的输出格式,其中%d表示时间,%thread为线程名,%level是日志级别,%logger为类名,%msg则是日志消息内容。
Spring Boot3.4 中的结构化日志
随着 Spring Boot3.4 版本的发布,日志结构化功能为我们的日志处理带来了全新的视角。结构化日志是一种以预定义的、机器可读的格式输出日志信息的技术,它能够被轻松引入日志管理系统,实现更强大的搜索、过滤和分析功能。目前,最常用的结构化日志格式之一便是 JSON。
在 Spring Boot 3.4 中,结构化日志记录功能开箱即用,默认支持Elastic Common Schema (ECS)和Logstash格式,并且还允许我们根据自身需求进行灵活扩展。
启用结构化日志
若要在控制台中启用结构化日志记录,只需在application.properties中添加配置
logging.structured.format.console=ecs,这将指示 Spring Boot 以Elastic Common Schema (ECS)格式输出日志。启动应用程序后,我们便能看到以 JSON 格式输出的日志。
如果希望将结构化日志写入文件,可以在application.properties中添加如下配置:
logging.structured.format.file=ecs
logging.file.name=log.json
如此一来,启动应用程序后,控制台将显示人类可读的日志,而log.json文件中则会记录结构化的 JSON 日志内容。
自定义结构化日志格式
除了使用默认的结构化日志格式,Spring Boot 3.4 还支持我们自定义日志格式。实现步骤如下:
创建一个StructuredLogFormatter接口的自定义实现。例如:
package cn.com.ut.cloud.demolog.config;
import ch.qos.logback.classic.spi.ILoggingEvent;
import org.springframework.boot.logging.structured.StructuredLogFormatter;
public class MyStructuredLoggingFormatter implements StructuredLogFormatter<ILoggingEvent> {
@Override
public String format(ILoggingEvent event) {
return "time=" + event.getTimeStamp() + ",level=" + event.getLevel() + ",message=" + event.getMessage() + "\n";
}
}
在application.properties中配置该自定义实现,如
logging.structured.format.console=
cn.com.ut.cloud.demolog.config.MyStructuredLoggingFormatter。此时,日志便会按照我们自定义的格式进行打印输出。
结构化日志添加额外信息
结构化日志的一大显著优势在于能够以结构化的方式为日志事件添加额外信息。例如,我们可以将用户 ID 添加到每个日志事件中,以便后续通过该 ID 进行过滤,查看特定用户的操作记录。在 Spring Boot 中,我们既可以通过MDC(映射诊断上下文)来实现,也可以使用流式日志 API 在不依赖MDC的情况下添加附加字段。
通过MDC添加用户 ID 的示例代码如下:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import java.util.UUID;
@Component
class MyLogger {
private static final Logger logger = LoggerFactory.getLogger(MyLogger.class);
public void logWithUserId() {
MDC.put("userId", UUID.randomUUID().toString());
logger.info("Hello world!");
MDC.remove("userId");
}
}
使用流式日志 API 添加附加字段的示例如下:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;
public class MyLogger {
private static final Logger LOGGER = LoggerFactory.getLogger(MyLogger.class);
private static final Marker MARKER = MarkerFactory.getMarker("USER_ID_MARKER");
public void logWithExtraFields() {
LOGGER.atInfo().addMarker(MARKER).setMessage("Hello structured logging!").addArgument("userId", "12345").log();
}
}
利用 AOP + 自定义注解简化日志记录
在大型项目开发中,为了避免在每个业务方法中重复编写日志记录代码,我们可以借助 AOP(面向切面编程)结合自定义注解的方式来简化日志记录操作。
首先,定义一个自定义注解,例如:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomLog {
String value() default "";
}
上述注解CustomLog可以作用于方法,并且可以携带一个字符串参数value,用于描述该方法的日志信息。
然后,实现一个基于该注解的切面类,用于拦截带有CustomLog注解的方法,并在方法执行前后记录日志:
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class CustomLogAspect {
private static final Logger logger = LoggerFactory.getLogger(CustomLogAspect.class);
@Around("@annotation(com.example.CustomLog)")
public Object logMethod(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
CustomLog annotation = signature.getMethod().getAnnotation(CustomLog.class);
String message = annotation.value().isEmpty()? signature.getName() : annotation.value();
logger.info("开始执行: {}", message);
Object result = joinPoint.proceed();
logger.info("执行完成: {}", message);
return result;
}
}
在业务代码中,当我们希望对某个方法进行日志记录时,只需在该方法上添加@CustomLog注解即可。例如:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
@CustomLog("用户登录方法")
@GetMapping("/login")
public String login() {
return "login successful!";
}
}
当/login接口被调用时,日志中便会记录下 “开始执行:用户登录方法” 以及 “执行完成:用户登录方法” 的信息,极大地简化了日志记录的代码量,同时也提高了代码的可读性和可维护性。
总结
在 Spring Boot3 的开发旅程中,日志的自定义封装处理操作犹如一把多用途的瑞士军刀,为我们在不同场景下的开发工作提供了有力支持。从基础的日志级别调整、输出到文件,到自定义日志格式,再到 Spring Boot3.4 中强大的结构化日志功能以及利用 AOP 和自定义注解简化日志记录,每一个环节都蕴含着提升系统可观测性和开发效率的潜力。希望通过本文的介绍,各位互联网软件开发人员能够更加熟练地运用这些日志处理技巧,让我们的 Spring Boot3 项目在日志的精准记录与高效管理下,稳健地运行在互联网的浪潮之中。在实际项目中,不妨根据具体需求灵活运用这些知识,相信一定会为你的开发工作带来诸多便利与优化。
猜你喜欢
- 2025-09-06 如何优雅地记录操作日志?_操作日志怎么实现
- 2025-09-06 开发利器丨如何使用ELK设计微服务中的日志收集方案?
- 2025-09-06 Java应用日志如何与Jaeger的trace关联
- 2025-09-06 Springboot强大的日志功能你真的了解吗?这些功能你肯定不知道
- 2025-09-06 SpringBoot+Kafka+ELK 完成海量日志收集(超详细)
- 2025-09-06 SpringBoot中统一日志管理_springboot日志每天一个文件
你 发表评论:
欢迎- 最近发表
-
- 如何优雅地记录操作日志?_操作日志怎么实现
- 开发利器丨如何使用ELK设计微服务中的日志收集方案?
- Java应用日志如何与Jaeger的trace关联
- Springboot强大的日志功能你真的了解吗?这些功能你肯定不知道
- 深入探索 Spring Boot3 中日志的自定义封装处理操作
- SpringBoot+Kafka+ELK 完成海量日志收集(超详细)
- SpringBoot中统一日志管理_springboot日志每天一个文件
- 16.3K Star!简洁高效的Java权限认证与会话管理框架——Sa-Token
- 27 | API网关:系统的门面要如何做呢?
- 微服务网关—Zuul1.0和2.0我们该如何选择?
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)