常见问题之Java——使用lombok中的@Slf4j时log缺失

本文介绍了在Java开发中使用Lombok的@Slf4j注解时遇到的log变量缺失问题及其解决方案。问题包括缺少Lombok插件、未启用注解处理、编译器未使用javac以及build.gradle配置不正确。通过安装Lombok插件,启用注解处理,确保使用javac作为编译器,并在build.gradle中添加Lombok为注解处理包,可以成功解决该问题。

常见问题之Java——使用lombok中的@Slf4j时log缺失

背景

日常我们开发时,我们会遇到各种各样的奇奇怪怪的问题(踩坑o(╯□╰)o),这个常见问题系列就是我日常遇到的一些问题的记录文章系列,这里整理汇总后分享给大家,让其还在深坑中的小伙伴有绳索能爬出来。
同时在这里也欢迎大家把自己遇到的问题留言或私信给我,我看看其能否给大家解决。

开发环境

  • 系统:windows10
  • JDK:openjdk11
  • 开发工具:IDEA 教育版
  • 框架:SpringBoot
  • 包管理:Gradle

内容

本节问题:常见问题之Java——使用lombok中的@Slf4j时log缺失

错误: 找不到符号
        log.info("------------ Start Cookie Filter ------------");
        ^
  符号:   变量 log
  位置: 类 XssConfig

解决方法如图所示

问题1、缺失插件

打开File——settings——Plugins
在其中搜索Lombok并进行安装

image.png
问题2、编译时没有编译注解

打开File——settings——Build, Execution, Deployment——Compiler——Annotation Processors
勾选上Enable annotation processing

image.png
问题3、编译器没有使用javac

打开File ——Settings—— Build, Execution, Deployment——Compiler——Java Compiler
use compiler:选择javac

image.png
问题4、build.gradle中没有指定lombok为注解处理包

在其dependencies项下增加

annotationProcessor group: 'org.projectlombok', name: 'lombok', version: '1.18.20'

文章中的代码将同步更新至API接口管理平台仓库中,有需要的可以进行了解或下载需要的代码。如果您觉得本文不错,欢迎Star支持

本文声明:
88x31.png
知识共享许可协议
本作品由 cn華少 采用 知识共享署名-非商业性使用 4.0 国际许可协议 进行许可。
/* * Copyright (c) 2025, TP-Link Corporation Limited. All rights reserved. */ package com.tplink.smb.omada.common.data.manager.service.actuate; import java.util.List; import cn.hutool.core.text.CharSequenceUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.WriteOperation; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import com.tplink.smb.omada.common.data.manager.common.JsonUtils; import com.tplink.smb.omada.common.data.manager.controller.response.OmadaHttpResponse; import com.tplink.smb.omada.common.data.manager.controller.vo.actuate.ImageInfoActuateVO; import com.tplink.smb.omada.common.data.manager.controller.vo.actuate.ImageInfoCollectionVO; import com.tplink.smb.omada.common.data.manager.dao.ImageInfoRepository; import com.tplink.smb.omada.common.data.manager.service.dto.ImageInfoDTO; import com.tplink.smb.omada.common.data.manager.service.dto.mapper.ImageInfoDTOMapper; /** * @author Zhou Zhuoran * @version 1.0 * @since 2025/2/14 */ @Slf4j @Component @Endpoint(id = "imageInfo") public class ImageInfoEndpoint { private final ImageInfoRepository imageInfoRepository; public ImageInfoEndpoint(ImageInfoRepository imageInfoRepository) { this.imageInfoRepository = imageInfoRepository; } @WriteOperation public OmadaHttpResponse<String> addOrUpdateImageInfo(String imageInfo) { log.info("modify imageInfo operation start, imageInfo is:{}", imageInfo); ImageInfoCollectionVO imageInfoCollectionVO; try { imageInfoCollectionVO = JsonUtils.json2bean(imageInfo, ImageInfoCollectionVO.class); } catch (Exception e) { log.error(e.getMessage(), e); return OmadaHttpResponse.error("convert json error"); } if (imageInfoCollectionVO == null || CollectionUtils.isEmpty( imageInfoCollectionVO.getImageInfos())) { log.info("addOrUpdateImageInfo operation failed, image info is empty"); return OmadaHttpResponse.error("image info is empty"); } List<ImageInfoDTO> imageInfoDTOS = imageInfoRepository.findAll(); String maxControllerVersion = imageInfoDTOS.stream().max( (imageInfoDTO1, imageInfoDTO2) -> CharSequenceUtil.compareVersion( imageInfoDTO1.getControllerVersion(), imageInfoDTO2.getControllerVersion())) .map(ImageInfoDTO::getControllerVersion).orElse("0"); List<ImageInfoActuateVO> imageInfos = imageInfoCollectionVO.getImageInfos(); boolean inputControllerVersionIsSmall = imageInfos.stream().anyMatch( imageInfoVO -> imageInfoVO.getControllerVersion() == null || CharSequenceUtil.compareVersion(imageInfoVO.getControllerVersion(), maxControllerVersion) <= 0); if (inputControllerVersionIsSmall) { log.info( "image info controller version is small, exist image info max controller version is {}", maxControllerVersion); return OmadaHttpResponse.error("image info controller version is small"); } int addNum = 0; int updateNum = 0; for (ImageInfoActuateVO imageInfoVO : imageInfos) { ImageInfoDTO imageInfoDTO = imageInfoRepository.findByModelAndModelVersion( imageInfoVO.getModel(), imageInfoVO.getModelVersion()); if (imageInfoDTO == null) { imageInfoRepository.save(ImageInfoDTOMapper.convertActuateVoToDto(imageInfoVO)); addNum++; } else { imageInfoRepository.update(ImageInfoDTOMapper.convertActuateVoToDto(imageInfoVO)); updateNum++; } } log.info( "modify imageInfo operation end, add imageInfo num : {}, update imageInfo num: {}", addNum, updateNum); return OmadaHttpResponse.isOk( String.format("add imageInfo num: %s, update imageInfo num: %s.", addNum, updateNum)); } }
最新发布
10-01
使用 Lombok 的 `@Slf4j` 注解可以简化日志记录器的声明,避免手动编写 `private static final Logger logger = LoggerFactory.getLogger(XXX.class)`。以下是详细说明和代码示例: --- ### 1. **添加 Lombok 依赖** 确保项目中已引入 Lombok(Maven 示例): ```xml <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.30</version> <scope>provided</scope> </dependency> ``` --- ### 2. **使用 `@Slf4j` 注解** 在类上添加 `@Slf4j`,Lombok 会自动生成 `org.slf4j.Logger` 类型的 `log` 变量。 #### 修改后的代码 ```java import lombok.extern.slf4j.Slf4j; @Slf4j public class Person { private String name; public void setName(String name) { int length = 64; String blank = " "; if (name == null || name.trim().isEmpty()) { String errorMsg = "姓名不能为空"; log.error(errorMsg); // 直接使用 log 对象 throw new IllegalArgumentException(errorMsg); } if (name.getBytes().length > length) { String errorMsg = "姓名长度不能超过64字节"; log.error(errorMsg); throw new IllegalArgumentException(errorMsg); } if (name.startsWith(blank) || name.endsWith(blank)) { String errorMsg = "姓名首尾不能有空格"; log.error(errorMsg); throw new IllegalArgumentException(errorMsg); } this.name = name; log.info("姓名设置成功: {}", name); // 示例:记录成功日志 } } ``` --- ### 3. **关键说明** #### 3.1 生成的日志变量 - `@Slf4j` 会自动生成以下代码(编译): ```java private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(Person.class); ``` #### 3.2 日志方法 - 支持 SLF4J 的所有日志级别: ```java log.trace("Trace 级别日志"); log.debug("Debug 级别日志"); log.info("Info 级别日志"); log.warn("Warn 级别日志"); log.error("Error 级别日志"); ``` #### 3.3 参数化日志 - 使用 `{}` 占位符(避免字符串拼接): ```java log.info("用户 {} 尝试设置姓名 {}", userId, name); ``` --- ### 4. **IDE 配置** 确保 IDE 支持 Lombok: - **IntelliJ IDEA**: 1. 安装 Lombok 插件(`Settings → Plugins → 搜索 "Lombok"`)。 2. 启用注解处理(`Settings → Build, Execution → Annotation Processors → 勾选 "Enable annotation processing"`)。 - **Eclipse**: 安装 Lombok 并重启 IDE(通过运行 `lombok.jar`)。 --- ### 5. **对比原始写法** #### 原始方式(无 Lombok) ```java import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Person { private static final Logger log = LoggerFactory.getLogger(Person.class); private String name; // ... 其他代码 } ``` #### 使用 `@Slf4j` 后 - 代码更简洁,减少样板代码。 --- ### 6. **测试验证** #### 测试日志输出 通过日志测试框架(如 `Logback` 的测试工具)验证日志: ```java import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.AppenderBase; import static org.junit.jupiter.api.Assertions.*; class PersonTest { @Test void testSetName_LogsErrorWhenEmpty() { // 模拟 Logback Appender TestAppender appender = new TestAppender(); ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); root.addAppender(appender); Person person = new Person(); Exception exception = assertThrows(IllegalArgumentException.class, () -> person.setName("")); assertEquals("姓名不能为空", exception.getMessage()); assertTrue(appender.logContains("姓名不能为空")); // 验证日志内容 } } // 自定义测试 Appender class TestAppender extends AppenderBase<ILoggingEvent> { private final List<String> logs = new ArrayList<>(); @Override protected void append(ILoggingEvent event) { logs.add(event.getFormattedMessage()); } public boolean logContains(String message) { return logs.stream().anyMatch(log -> log.contains(message)); } } ``` --- ### 7. **常见问题** #### 7.1 日志未输出? - 检查是否配置了日志实现(如 `logback.xml`)。 - 确保类路径下有 SLF4J 的绑定库(如 `logback-classic`)。 #### 7.2 与其他 Lombok 注解冲突 - `@Slf4j` 可以与其他 Lombok 注解(如 `@Data`)共存: ```java @Slf4j @Data public class User { private String id; } ``` #### 7.3 自定义日志名称 - 默认使用当前类名作为日志名,如需自定义: ```java @Slf4j(topic = "CUSTOM_LOGGER_NAME") public class CustomLogger { // log 对象会使用 "CUSTOM_LOGGER_NAME" 作为日志名 } ``` --- ### 8. **性能优化建议** - **避免字符串拼接**:始终使用 `log.debug("Value: {}", value)` 而非 `log.debug("Value: " + value)`。 - **条件日志**:在日志调用前检查级别(适用于复杂日志): ```java if (log.isDebugEnabled()) { log.debug("详细信息: {}", computeExpensiveLogMessage()); } ``` --- ### 总结 - **优势**:减少样板代码,提高可读性。 - **适用场景**:所有需要日志的类(尤其是业务逻辑复杂的类)。 - **注意**:确保项目已正确配置 Lombok 和 SLF4J 实现。 通过 `@Slf4j`,可以更专注于业务逻辑,而非日志对象的声明和初始化。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CN華少

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值