Jelajahi Sumber

docs(guidelines): 更新开发指南中的日志记录规范

- 将全局异常处理中的 printStackTrace() 替换为 SLF4J 日志记录
- 添加 Logback 配置文件 (logback-spring.xml) 实现控制台和文件滚动日志
- 明确约定代码中禁用 printStackTrace(),优先使用 log.debug/info/warn/error
- 提供标准的日志记录代码示例和最佳实践指导

fix(exception): 统一异常处理器返回类型并改进日志记录

- 为所有异常处理方法添加泛型返回类型 R<Void>
- 引入 SLF4J Logger 记录未处理异常,替代原来的 printStackTrace()
- 在 CustomExceptionHandler 中初始化日志记录器实例

refactor(page): 优化分页工具类的泛型声明

- 为 PageInfo 实例添加泛型参数,明确指定列表元素类型
- 提高代码类型安全性,消除潜在的类型转换警告

refactor(cors): 完善跨域配置方法的参数注解

- 为 addCorsMappings 方法的 CorsRegistry 参数添加 @NonNull 注解
- 增强代码健壮性,明确标注非空约束条件
mcbaiyun 2 bulan lalu
induk
melakukan
ca38f6792e

+ 13 - 1
docs/DEVELOPMENT_GUIDELINES.md

@@ -39,7 +39,19 @@
 - 参数校验使用 Jakarta Validation(见 `ConstraintViolationException` 的处理),并在 Controller/DTO 上使用注解进行字段校验。
 
 9. 日志与调试
-- 在全局未捕获异常时打印堆栈(当前实现为 `exception.printStackTrace()`)。建议使用日志框架(如 SLF4J + Logback)替换直接打印。
+- 在全局未捕获异常时使用日志记录(已将 `printStackTrace()` 替换为 SLF4J 的 `log.error(...)`)。
+- 推荐使用 SLF4J + Logback(项目中添加了 `logback-spring.xml`),控制台+文件滚动保存日志。示例:
+
+```java
+private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(YourClass.class);
+try {
+  // ...
+} catch (Exception e) {
+  log.error("处理异常", e);
+}
+```
+
+- 约定:不要在代码中使用 `e.printStackTrace()`;优先使用 `log.debug/info/warn/error` 并在生产环境避免过多的 DEBUG/TRACE 输出。
 
 10. Lombok 使用
 - 项目使用 Lombok(见 `@Data`, `@Getter`, `@Setter`),请在 IDE 中启用 Lombok 插件并配置编译器注解处理。

+ 1 - 1
src/main/java/work/baiyun/chronicdiseaseapp/common/Page.java

@@ -14,7 +14,7 @@ public class Page<T> {
     private Long total;
     private List<T> list;
     public Page(int pageNum, int pageSize, List<T> list){
-        PageInfo pageInfo = new PageInfo(list);
+        PageInfo<T> pageInfo = new PageInfo<>(list);
         this.pageNum = pageNum;
         this.pageSize = pageSize;
         this.list = list;

+ 2 - 1
src/main/java/work/baiyun/chronicdiseaseapp/config/CorsConfig.java

@@ -2,13 +2,14 @@ package work.baiyun.chronicdiseaseapp.config;
 
 import org.springframework.context.annotation.Configuration;
 import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.lang.NonNull;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
 @Configuration
 public class CorsConfig implements WebMvcConfigurer {
 //    由于在Vite配置了反向代理,因此不需要配置跨域了
     @Override
-    public void addCorsMappings(CorsRegistry registry) {
+    public void addCorsMappings(@NonNull CorsRegistry registry) {
         registry.addMapping("/api/**") // 指定需要跨域的路径
                 .allowedOrigins("http://localhost:3000") // 允许的源
                 .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 允许的方法

+ 8 - 5
src/main/java/work/baiyun/chronicdiseaseapp/exception/CustomExceptionHandler.java

@@ -3,6 +3,8 @@ package work.baiyun.chronicdiseaseapp.exception;
 import jakarta.validation.*;
 import work.baiyun.chronicdiseaseapp.common.R;
 import work.baiyun.chronicdiseaseapp.enums.ExceptionResultCode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.validation.BindException;
 import org.springframework.validation.BindingResult;
 import org.springframework.validation.ObjectError;
@@ -20,6 +22,7 @@ import java.util.stream.Collectors;
  */
 @RestControllerAdvice
 public class CustomExceptionHandler {
+    private static final Logger log = LoggerFactory.getLogger(CustomExceptionHandler.class);
     /**
      * 参数校验异常
      *
@@ -27,7 +30,7 @@ public class CustomExceptionHandler {
      * @return
      */
     @ExceptionHandler(value = BindException.class)
-    public R errorHandler(BindException ex) {
+    public R<Void> errorHandler(BindException ex) {
         BindingResult result = ex.getBindingResult();
         StringBuilder errorMsg = new StringBuilder();
         for (ObjectError error : result.getAllErrors()) {
@@ -43,7 +46,7 @@ public class CustomExceptionHandler {
      * @return
      */
     @ExceptionHandler(ConstraintViolationException.class)
-    public R validationErrorHandler(ConstraintViolationException ex) {
+    public R<Void> validationErrorHandler(ConstraintViolationException ex) {
         List<String> errorInformation = ex.getConstraintViolations()
                 .stream()
                 .map(ConstraintViolation::getMessage)
@@ -58,7 +61,7 @@ public class CustomExceptionHandler {
      * @return
      */
     @ExceptionHandler(value = CustomException.class)
-    public R customExceptionHandler(CustomException customException) {
+    public R<Void> customExceptionHandler(CustomException customException) {
         String message = customException.getMessage();
         return R.fail(ExceptionResultCode.EXCEPTION.getCode(), message);
     }
@@ -69,8 +72,8 @@ public class CustomExceptionHandler {
      * @return
      */
     @ExceptionHandler(value = Exception.class)
-    public R exceptionHandler(Exception exception) {
-        exception.printStackTrace();
+    public R<Void> exceptionHandler(Exception exception) {
+        log.error("Unhandled exception", exception);
         return R.fail(ExceptionResultCode.EXCEPTION.getCode(), ExceptionResultCode.EXCEPTION.getMsg());
     }
 }

+ 25 - 0
src/main/resources/logback-spring.xml

@@ -0,0 +1,25 @@
+<configuration>
+    <property name="LOG_PATH" value="${LOG_PATH:-./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>
+
+    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${LOG_PATH}/app.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${LOG_PATH}/app.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <maxHistory>30</maxHistory>
+        </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="FILE" />
+    </root>
+</configuration>

+ 25 - 0
target/classes/logback-spring.xml

@@ -0,0 +1,25 @@
+<configuration>
+    <property name="LOG_PATH" value="${LOG_PATH:-./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>
+
+    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${LOG_PATH}/app.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${LOG_PATH}/app.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <maxHistory>30</maxHistory>
+        </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="FILE" />
+    </root>
+</configuration>