最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring Boot/Spring Cloud 應(yīng)用日志書寫詳細實例指南

 更新時間:2026年03月20日 09:30:42   作者:龍茶清歡  
這篇文章主要介紹了Spring Boot/Spring Cloud 應(yīng)用日志書寫詳細指南,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧

Spring Boot/Spring Cloud 應(yīng)用日志書寫詳細指南

1. 日志框架選擇與配置

1.1 推薦日志框架組合

<!-- Spring Boot 默認使用,無需額外引入 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<!-- 如需使用 Log4j2 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>

1.2 基礎(chǔ)配置

# application.yml
logging:
  level:
    com.yourcompany: INFO
    org.springframework: WARN
    org.hibernate: ERROR
  pattern:
    console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
    file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
  file:
    name: logs/application.log
  logback:
    rollingpolicy:
      max-file-size: 10MB
      max-history: 30

2. 日志級別規(guī)范

2.1 級別定義與使用場景

@Component
public class UserService {
    private static final Logger logger = LoggerFactory.getLogger(UserService.class);
    public User createUser(UserDTO userDTO) {
        // TRACE: 詳細的調(diào)試信息,通常在生產(chǎn)環(huán)境關(guān)閉
        logger.trace("開始創(chuàng)建用戶,輸入?yún)?shù): {}", userDTO);
        // DEBUG: 調(diào)試信息,用于開發(fā)階段問題定位
        logger.debug("用戶數(shù)據(jù)驗證通過,準備持久化");
        try {
            User user = userRepository.save(convertToEntity(userDTO));
            // INFO: 重要的業(yè)務(wù)流程節(jié)點
            logger.info("用戶創(chuàng)建成功,用戶ID: {}, 用戶名: {}", user.getId(), user.getUsername());
            return user;
        } catch (DataIntegrityViolationException e) {
            // WARN: 非預(yù)期但可處理的情況
            logger.warn("用戶數(shù)據(jù)重復(fù),用戶名: {}", userDTO.getUsername(), e);
            throw new BusinessException("用戶已存在");
        } catch (Exception e) {
            // ERROR: 系統(tǒng)錯誤和異常情況
            logger.error("用戶創(chuàng)建失敗,用戶名: {}", userDTO.getUsername(), e);
            throw new SystemException("系統(tǒng)內(nèi)部錯誤");
        }
    }
}

3. 日志內(nèi)容規(guī)范

3.1 結(jié)構(gòu)化日志格式

@Service
@Slf4j // 推薦使用 Lombok @Slf4j 注解
public class OrderService {
    public Order createOrder(OrderRequest request) {
        // 關(guān)鍵業(yè)務(wù)操作開始日志
        log.info("創(chuàng)建訂單開始 | 用戶ID:{} | 商品數(shù)量:{} | 總金額:{}", 
                request.getUserId(), 
                request.getItems().size(),
                request.getTotalAmount());
        try {
            // 業(yè)務(wù)邏輯處理
            Order order = processOrder(request);
            // 成功日志包含關(guān)鍵業(yè)務(wù)ID
            log.info("訂單創(chuàng)建成功 | 訂單ID:{} | 狀態(tài):{} | 用戶ID:{}", 
                    order.getId(), 
                    order.getStatus(),
                    order.getUserId());
            return order;
        } catch (InventoryException e) {
            // 異常日志包含上下文信息和異常
            log.error("庫存不足創(chuàng)建訂單失敗 | 用戶ID:{} | 錯誤碼:{}", 
                     request.getUserId(), 
                     "INSUFFICIENT_INVENTORY", e);
            throw e;
        }
    }
}

3.2 JSON 結(jié)構(gòu)化日志(推薦用于微服務(wù))

@Component
public class StructuredLogger {
    public void logPaymentEvent(PaymentEvent event) {
        Map<String, Object> logData = new HashMap<>();
        logData.put("event", "payment_processed");
        logData.put("orderId", event.getOrderId());
        logData.put("amount", event.getAmount());
        logData.put("paymentMethod", event.getPaymentMethod());
        logData.put("timestamp", Instant.now().toString());
        logData.put("traceId", MDC.get("traceId"));
        log.info(JSON.toJSONString(logData));
    }
    public void logError(String errorCode, String message, String module, Throwable t) {
        Map<String, Object> errorLog = new HashMap<>();
        errorLog.put("level", "ERROR");
        errorLog.put("errorCode", errorCode);
        errorLog.put("message", message);
        errorLog.put("module", module);
        errorLog.put("timestamp", Instant.now().toString());
        errorLog.put("stackTrace", getStackTrace(t));
        errorLog.put("traceId", MDC.get("traceId"));
        log.error(JSON.toJSONString(errorLog));
    }
    private String getStackTrace(Throwable t) {
        StringWriter sw = new StringWriter();
        t.printStackTrace(new PrintWriter(sw));
        return sw.toString();
    }
}

4. 日志上下文信息

4.1 使用 MDC (Mapped Diagnostic Context)

@Aspect
@Component
@Slf4j
public class LoggingAspect {
    @Around("execution(* com.yourcompany..*Controller.*(..))")
    public Object logController(ProceedingJoinPoint joinPoint) throws Throwable {
        // 設(shè)置請求上下文
        MDC.put("traceId", generateTraceId());
        MDC.put("userId", getCurrentUserId());
        MDC.put("clientIp", getClientIp());
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        log.info("請求開始 | 類:{} | 方法:{}", className, methodName);
        long startTime = System.currentTimeMillis();
        try {
            Object result = joinPoint.proceed();
            long executionTime = System.currentTimeMillis() - startTime;
            log.info("請求完成 | 類:{} | 方法:{} | 耗時:{}ms", 
                    className, methodName, executionTime);
            return result;
        } catch (Exception e) {
            long executionTime = System.currentTimeMillis() - startTime;
            log.error("請求異常 | 類:{} | 方法:{} | 耗時:{}ms | 異常:{}", 
                     className, methodName, executionTime, e.getMessage(), e);
            throw e;
        } finally {
            // 清理 MDC
            MDC.clear();
        }
    }
}

4.2 請求鏈路追蹤

@Component
public class TraceFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                        FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String traceId = httpRequest.getHeader("X-Trace-Id");
        if (traceId == null || traceId.isEmpty()) {
            traceId = generateTraceId();
        }
        MDC.put("traceId", traceId);
        MDC.put("requestUri", httpRequest.getRequestURI());
        MDC.put("userAgent", httpRequest.getHeader("User-Agent"));
        try {
            chain.doFilter(request, response);
        } finally {
            MDC.clear();
        }
    }
}

5. 性能優(yōu)化建議

5.1 避免日志性能問題

@Service
@Slf4j
public class PerformanceSensitiveService {
    // 不好的寫法:在調(diào)試級別進行字符串拼接
    public void badLogging(User user) {
        // 即使日志級別高于DEBUG,字符串拼接仍然會執(zhí)行
        log.debug("用戶信息: " + user.toString() + ", 時間: " + System.currentTimeMillis());
    }
    // 好的寫法:使用參數(shù)化日志
    public void goodLogging(User user) {
        // 只有在DEBUG級別啟用時才會進行字符串處理
        log.debug("用戶信息: {}, 時間: {}", user, System.currentTimeMillis());
    }
    // 對于復(fù)雜的日志消息,使用條件判斷
    public void conditionalLogging(List<User> users) {
        if (log.isDebugEnabled()) {
            log.debug("處理用戶列表: {}", formatUserList(users)); // formatUserList 比較耗時
        }
    }
}

6. 異常日志處理

6.1 統(tǒng)一的異常日志處理

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
    @ExceptionHandler(BusinessException.class)
    public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException e) {
        // WARN 級別記錄業(yè)務(wù)異常
        log.warn("業(yè)務(wù)異常 | 錯誤碼:{} | 錯誤信息:{} | 請求路徑:{}", 
                e.getErrorCode(), e.getMessage(), getCurrentRequestPath());
        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                .body(new ErrorResponse(e.getErrorCode(), e.getMessage()));
    }
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleSystemException(Exception e) {
        // ERROR 級別記錄系統(tǒng)異常
        log.error("系統(tǒng)異常 | 異常類型:{} | 請求路徑:{}", 
                 e.getClass().getSimpleName(), getCurrentRequestPath(), e);
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(new ErrorResponse("SYSTEM_ERROR", "系統(tǒng)內(nèi)部錯誤"));
    }
}

7. 日志配置文件示例

7.1 Logback 配置 (logback-spring.xml)

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%X{traceId}] - %msg%n"/>
    <property name="LOG_PATH" value="logs"/>
    <!-- 控制臺輸出 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${LOG_PATTERN}</pattern>
        </encoder>
    </appender>
    <!-- 文件輸出 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/application.log</file>
        <encoder>
            <pattern>${LOG_PATTERN}</pattern>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/application.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <maxFileSize>10MB</maxFileSize>
            <maxHistory>30</maxHistory>
            <totalSizeCap>1GB</totalSizeCap>
        </rollingPolicy>
    </appender>
    <!-- 錯誤日志單獨文件 -->
    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/error.log</file>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level>
        </filter>
        <encoder>
            <pattern>${LOG_PATTERN}</pattern>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/error.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <maxFileSize>10MB</maxFileSize>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
    </appender>
    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="FILE"/>
        <appender-ref ref="ERROR_FILE"/>
    </root>
    <!-- 特定包日志級別 -->
    <logger name="com.yourcompany" level="DEBUG"/>
    <logger name="org.springframework" level="WARN"/>
    <logger name="org.hibernate" level="WARN"/>
</configuration>

8. 最佳實踐總結(jié)

8.1 必須遵循的原則

  1. 適當(dāng)?shù)娜罩炯墑e:生產(chǎn)環(huán)境使用 INFO,開發(fā)環(huán)境使用 DEBUG
  2. 有意義的日志消息:包含足夠的上下文信息
  3. 統(tǒng)一格式:團隊使用統(tǒng)一的日志格式
  4. 性能考慮:避免在日志中執(zhí)行耗時操作
  5. 安全考慮:不要記錄敏感信息(密碼、token等)

8.2 推薦的日志場景

// 1. 方法入口和出口
log.info("方法開始: {},參數(shù): {}", methodName, parameters);
log.info("方法結(jié)束: {},結(jié)果: {}", methodName, result);
// 2. 業(yè)務(wù)流程關(guān)鍵節(jié)點
log.info("訂單狀態(tài)變更 | 訂單ID:{} | 原狀態(tài):{} | 新狀態(tài):{}", orderId, oldStatus, newStatus);
// 3. 外部調(diào)用
log.info("調(diào)用外部服務(wù)開始 | 服務(wù)名:{} | 請求參數(shù):{}", serviceName, request);
log.info("調(diào)用外部服務(wù)結(jié)束 | 服務(wù)名:{} | 響應(yīng)結(jié)果:{}", serviceName, response);
// 4. 異常情況
log.error("數(shù)據(jù)庫操作失敗 | SQL:{} | 參數(shù):{}", sql, params, e);
// 5. 定時任務(wù)
log.info("定時任務(wù)開始 | 任務(wù)名:{} | 執(zhí)行時間:{}", taskName, startTime);
log.info("定時任務(wù)結(jié)束 | 任務(wù)名:{} | 執(zhí)行結(jié)果:{} | 耗時:{}ms", taskName, result, costTime);

通過遵循這些指南,可以建立統(tǒng)一、可維護、高效的日志系統(tǒng),大大提高系統(tǒng)的可觀測性和問題排查效率。

Spring Boot/Cloud 全場景日志書寫示例

1. 控制器層日志

1.1 REST Controller 完整示例

@RestController
@RequestMapping("/api/v1/users")
@Slf4j
public class UserController {
    @GetMapping("/{userId}")
    public ResponseEntity<UserVO> getUser(@PathVariable Long userId,
                                         @RequestHeader("User-Agent") String userAgent) {
        // 請求入口日志
        log.info("查詢用戶開始 | 用戶ID:{} | 客戶端:{} | 線程:{}", 
                userId, userAgent, Thread.currentThread().getName());
        long startTime = System.currentTimeMillis();
        try {
            UserVO user = userService.getUserById(userId);
            // 成功響應(yīng)日志
            log.info("查詢用戶成功 | 用戶ID:{} | 用戶名:{} | 耗時:{}ms", 
                    userId, user.getUsername(), System.currentTimeMillis() - startTime);
            return ResponseEntity.ok(user);
        } catch (UserNotFoundException e) {
            // 業(yè)務(wù)異常日志
            log.warn("用戶不存在 | 用戶ID:{} | 耗時:{}ms", 
                    userId, System.currentTimeMillis() - startTime, e);
            return ResponseEntity.notFound().build();
        } catch (Exception e) {
            // 系統(tǒng)異常日志
            log.error("查詢用戶異常 | 用戶ID:{} | 異常類型:{} | 耗時:{}ms", 
                     userId, e.getClass().getSimpleName(), 
                     System.currentTimeMillis() - startTime, e);
            return ResponseEntity.internalServerError().build();
        }
    }
    @PostMapping
    public ResponseEntity<CreateUserResponse> createUser(@Valid @RequestBody CreateUserRequest request,
                                                        HttpServletRequest httpRequest) {
        String clientIp = httpRequest.getRemoteAddr();
        log.info("創(chuàng)建用戶開始 | 用戶名:{} | 郵箱:{} | 客戶端IP:{}", 
                request.getUsername(), request.getEmail(), clientIp);
        // 敏感信息脫敏
        log.debug("創(chuàng)建用戶詳細參數(shù) | 用戶名:{} | 角色:{} | 部門:{}", 
                 request.getUsername(), request.getRole(), request.getDepartment());
        long startTime = System.currentTimeMillis();
        try {
            UserVO user = userService.createUser(request);
            log.info("創(chuàng)建用戶成功 | 用戶ID:{} | 用戶名:{} | 耗時:{}ms", 
                    user.getId(), user.getUsername(), System.currentTimeMillis() - startTime);
            return ResponseEntity.status(HttpStatus.CREATED)
                    .body(new CreateUserResponse(user.getId(), "創(chuàng)建成功"));
        } catch (DuplicateUserException e) {
            log.warn("創(chuàng)建用戶失敗-用戶已存在 | 用戶名:{} | 郵箱:{}", 
                    request.getUsername(), request.getEmail(), e);
            return ResponseEntity.badRequest()
                    .body(new CreateUserResponse(null, "用戶已存在"));
        }
    }
    @PutMapping("/{userId}/status")
    public ResponseEntity<Void> updateUserStatus(@PathVariable Long userId,
                                                @RequestBody UpdateStatusRequest request) {
        log.info("更新用戶狀態(tài)開始 | 用戶ID:{} | 原狀態(tài):{} | 新狀態(tài):{}", 
                userId, getCurrentStatus(userId), request.getStatus());
        userService.updateStatus(userId, request.getStatus());
        log.info("更新用戶狀態(tài)成功 | 用戶ID:{} | 新狀態(tài):{}", userId, request.getStatus());
        return ResponseEntity.ok().build();
    }
}

2. 服務(wù)層日志

2.1 業(yè)務(wù)服務(wù)完整示例

@Service
@Slf4j
@Transactional
public class OrderService {
    public Order createOrder(CreateOrderRequest request) {
        log.info("創(chuàng)建訂單開始 | 用戶ID:{} | 商品數(shù)量:{} | 總金額:{}", 
                request.getUserId(), request.getItems().size(), request.getTotalAmount());
        // 參數(shù)驗證日志
        if (log.isDebugEnabled()) {
            log.debug("訂單詳細參數(shù) | 商品列表:{} | 收貨地址:{}", 
                     request.getItems(), maskSensitiveInfo(request.getAddress()));
        }
        try {
            // 1. 驗證庫存
            log.debug("開始驗證庫存 | 商品數(shù)量:{}", request.getItems().size());
            inventoryService.validateStock(request.getItems());
            log.debug("庫存驗證通過");
            // 2. 計算價格
            log.debug("開始計算訂單價格");
            BigDecimal finalAmount = calculateFinalAmount(request);
            log.debug("價格計算完成 | 最終金額:{}", finalAmount);
            // 3. 創(chuàng)建訂單
            Order order = buildOrder(request, finalAmount);
            order = orderRepository.save(order);
            log.info("訂單創(chuàng)建完成 | 訂單ID:{} | 訂單號:{} | 狀態(tài):{}", 
                    order.getId(), order.getOrderNo(), order.getStatus());
            // 4. 扣減庫存
            log.debug("開始扣減庫存 | 訂單ID:{}", order.getId());
            inventoryService.deductStock(order.getId(), request.getItems());
            log.debug("庫存扣減完成");
            // 5. 發(fā)送創(chuàng)建事件
            eventPublisher.publishEvent(new OrderCreatedEvent(order));
            log.debug("訂單創(chuàng)建事件已發(fā)布");
            log.info("訂單流程全部完成 | 訂單ID:{} | 總耗時:{}ms", 
                    order.getId(), System.currentTimeMillis() - startTime);
            return order;
        } catch (InventoryException e) {
            log.error("創(chuàng)建訂單失敗-庫存不足 | 用戶ID:{} | 錯誤詳情:{}", 
                     request.getUserId(), e.getMessage(), e);
            throw new BusinessException("庫存不足,創(chuàng)建訂單失敗");
        } catch (PaymentException e) {
            log.error("創(chuàng)建訂單失敗-支付異常 | 用戶ID:{} | 錯誤碼:{}", 
                     request.getUserId(), e.getErrorCode(), e);
            throw new BusinessException("支付處理失敗");
        } catch (Exception e) {
            log.error("創(chuàng)建訂單失敗-系統(tǒng)異常 | 用戶ID:{} | 異常類型:{}", 
                     request.getUserId(), e.getClass().getSimpleName(), e);
            throw new SystemException("系統(tǒng)繁忙,請稍后重試");
        }
    }
    public void processExpiredOrders() {
        log.info("開始處理過期訂單 | 線程:{}", Thread.currentThread().getName());
        int processedCount = 0;
        int successCount = 0;
        int failureCount = 0;
        long startTime = System.currentTimeMillis();
        try {
            List<Order> expiredOrders = orderRepository.findExpiredOrders();
            log.info("找到過期訂單數(shù)量:{}", expiredOrders.size());
            for (Order order : expiredOrders) {
                processedCount++;
                try {
                    log.debug("處理過期訂單 | 訂單ID:{} | 訂單號:{}", order.getId(), order.getOrderNo());
                    cancelOrder(order.getId(), "系統(tǒng)自動取消-超時未支付");
                    successCount++;
                    log.debug("過期訂單處理成功 | 訂單ID:{}", order.getId());
                } catch (Exception e) {
                    failureCount++;
                    log.error("處理過期訂單失敗 | 訂單ID:{} | 異常:{}", order.getId(), e.getMessage(), e);
                }
            }
            log.info("過期訂單處理完成 | 總數(shù):{} | 成功:{} | 失敗:{} | 總耗時:{}ms", 
                    processedCount, successCount, failureCount, 
                    System.currentTimeMillis() - startTime);
        } catch (Exception e) {
            log.error("處理過期訂單任務(wù)異常 | 異常類型:{}", e.getClass().getSimpleName(), e);
            throw e;
        }
    }
}

3. 數(shù)據(jù)訪問層日志

3.1 Repository 日志示例

@Repository
@Slf4j
public class UserRepository {
    public User findByUsername(String username) {
        log.debug("根據(jù)用戶名查詢用戶 | 用戶名:{}", username);
        long startTime = System.currentTimeMillis();
        try {
            User user = userMapper.selectByUsername(username);
            if (user == null) {
                log.debug("用戶不存在 | 用戶名:{} | 耗時:{}ms", 
                         username, System.currentTimeMillis() - startTime);
            } else {
                log.debug("用戶查詢成功 | 用戶ID:{} | 用戶名:{} | 耗時:{}ms", 
                         user.getId(), username, System.currentTimeMillis() - startTime);
            }
            return user;
        } catch (DataAccessException e) {
            log.error("數(shù)據(jù)庫查詢異常 | SQL:查詢用戶 | 參數(shù):{} | 異常類型:{}", 
                     username, e.getClass().getSimpleName(), e);
            throw new RepositoryException("用戶查詢失敗", e);
        }
    }
    @Transactional
    public void updateUserStatus(Long userId, UserStatus newStatus) {
        UserStatus oldStatus = getUserStatus(userId);
        log.info("更新用戶狀態(tài) | 用戶ID:{} | 原狀態(tài):{} | 新狀態(tài):{}", userId, oldStatus, newStatus);
        try {
            int affectedRows = userMapper.updateStatus(userId, newStatus);
            if (affectedRows == 0) {
                log.warn("更新用戶狀態(tài)失敗-記錄不存在 | 用戶ID:{}", userId);
                throw new EntityNotFoundException("用戶不存在");
            }
            log.info("用戶狀態(tài)更新成功 | 用戶ID:{} | 影響行數(shù):{}", userId, affectedRows);
        } catch (DataIntegrityViolationException e) {
            log.error("更新用戶狀態(tài)失敗-數(shù)據(jù)完整性異常 | 用戶ID:{} | 新狀態(tài):{}", userId, newStatus, e);
            throw new RepositoryException("數(shù)據(jù)完整性約束沖突", e);
        }
    }
    public Page<User> findUsersByCondition(UserQuery query, Pageable pageable) {
        if (log.isDebugEnabled()) {
            log.debug("分頁查詢用戶 | 查詢條件:{} | 分頁參數(shù):{}", 
                     query.toString(), pageable.toString());
        }
        long startTime = System.currentTimeMillis();
        try {
            Page<User> result = userMapper.selectByCondition(query, pageable);
            log.info("用戶分頁查詢完成 | 總數(shù):{} | 當(dāng)前頁:{} | 耗時:{}ms", 
                    result.getTotalElements(), result.getNumber(), 
                    System.currentTimeMillis() - startTime);
            return result;
        } catch (DataAccessException e) {
            log.error("用戶分頁查詢異常 | 查詢條件:{} | 異常類型:{}", 
                     query, e.getClass().getSimpleName(), e);
            throw new RepositoryException("用戶查詢失敗", e);
        }
    }
}

4. 外部服務(wù)調(diào)用日志

4.1 HTTP 客戶端調(diào)用

@Component
@Slf4j
public class PaymentServiceClient {
    public PaymentResponse createPayment(PaymentRequest request) {
        String requestId = generateRequestId();
        log.info("調(diào)用支付服務(wù)開始 | 請求ID:{} | 訂單號:{} | 金額:{} | 端點:{}", 
                requestId, request.getOrderNo(), request.getAmount(), "/api/payments");
        long startTime = System.currentTimeMillis();
        try {
            // 記錄請求詳情(敏感信息脫敏)
            if (log.isDebugEnabled()) {
                log.debug("支付請求詳情 | 請求ID:{} | 支付方式:{} | 銀行編碼:{}", 
                         requestId, request.getPaymentMethod(), 
                         maskBankCode(request.getBankCode()));
            }
            PaymentResponse response = restTemplate.postForObject(
                paymentServiceUrl + "/api/payments", request, PaymentResponse.class);
            log.info("支付服務(wù)調(diào)用成功 | 請求ID:{} | 支付狀態(tài):{} | 耗時:{}ms", 
                    requestId, response.getStatus(), System.currentTimeMillis() - startTime);
            return response;
        } catch (HttpClientErrorException e) {
            log.warn("支付服務(wù)調(diào)用失敗-客戶端錯誤 | 請求ID:{} | 狀態(tài)碼:{} | 響應(yīng):{}", 
                    requestId, e.getStatusCode(), e.getResponseBodyAsString(), e);
            throw new PaymentException("支付請求參數(shù)錯誤");
        } catch (HttpServerErrorException e) {
            log.error("支付服務(wù)調(diào)用失敗-服務(wù)端錯誤 | 請求ID:{} | 狀態(tài)碼:{} | 異常:{}", 
                     requestId, e.getStatusCode(), e.getMessage(), e);
            throw new PaymentException("支付服務(wù)暫時不可用");
        } catch (ResourceAccessException e) {
            log.error("支付服務(wù)調(diào)用失敗-網(wǎng)絡(luò)異常 | 請求ID:{} | 異常類型:{}", 
                     requestId, e.getClass().getSimpleName(), e);
            throw new PaymentException("網(wǎng)絡(luò)連接超時");
        } catch (Exception e) {
            log.error("支付服務(wù)調(diào)用失敗-未知異常 | 請求ID:{} | 異常類型:{}", 
                     requestId, e.getClass().getSimpleName(), e);
            throw new PaymentException("支付處理失敗");
        }
    }
}

4.2 Feign Client 調(diào)用

@FeignClient(name = "inventory-service")
@Slf4j
public class InventoryServiceClient {
    @PostMapping("/api/inventory/deduct")
    ResponseEntity<InventoryResponse> deductStock(@RequestBody DeductRequest request);
    // Feign 配置類中的日志
    @Component
    @Slf4j
    public class FeignLogger extends Logger {
        @Override
        protected void log(String configKey, String format, Object... args) {
            if (log.isInfoEnabled()) {
                log.info(String.format(methodTag(configKey) + format, args));
            }
        }
    }
}

5. 消息隊列處理日志

5.1 RabbitMQ 消費者

@Component
@Slf4j
public class OrderMessageConsumer {
    @RabbitListener(queues = "order.create.queue")
    public void handleOrderCreate(OrderCreateMessage message, Channel channel, 
                                @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) {
        log.info("收到訂單創(chuàng)建消息 | 消息ID:{} | 訂單號:{} | 路由鍵:{}", 
                message.getMessageId(), message.getOrderNo(), "order.create");
        long startTime = System.currentTimeMillis();
        try {
            // 業(yè)務(wù)處理
            orderService.processOrderCreate(message);
            // 手動確認消息
            channel.basicAck(deliveryTag, false);
            log.info("訂單消息處理成功 | 消息ID:{} | 訂單號:{} | 耗時:{}ms", 
                    message.getMessageId(), message.getOrderNo(), 
                    System.currentTimeMillis() - startTime);
        } catch (BusinessException e) {
            log.warn("訂單消息處理失敗-業(yè)務(wù)異常 | 消息ID:{} | 訂單號:{} | 錯誤:{}", 
                    message.getMessageId(), message.getOrderNo(), e.getMessage());
            // 業(yè)務(wù)異常,拒絕消息并不再重試
            channel.basicReject(deliveryTag, false);
        } catch (Exception e) {
            log.error("訂單消息處理失敗-系統(tǒng)異常 | 消息ID:{} | 訂單號:{} | 異常類型:{}", 
                     message.getMessageId(), message.getOrderNo(), 
                     e.getClass().getSimpleName(), e);
            // 系統(tǒng)異常,拒絕消息并重新入隊
            channel.basicReject(deliveryTag, true);
        }
    }
}

6. 定時任務(wù)日志

6.1 分布式定時任務(wù)

@Component
@Slf4j
public class ScheduledTasks {
    @Scheduled(cron = "0 0 2 * * ?") // 每天凌晨2點執(zhí)行
    @SchedulerLock(name = "dataCleanupTask", lockAtMostFor = "30m")
    public void dataCleanupTask() {
        String taskId = UUID.randomUUID().toString();
        log.info("數(shù)據(jù)清理任務(wù)開始 | 任務(wù)ID:{} | 執(zhí)行時間:{}", 
                taskId, LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
        long startTime = System.currentTimeMillis();
        int successCount = 0;
        int failureCount = 0;
        try {
            // 清理過期訂單
            log.debug("開始清理過期訂單 | 任務(wù)ID:{}", taskId);
            int orderCount = orderService.cleanExpiredOrders();
            log.debug("過期訂單清理完成 | 清理數(shù)量:{}", orderCount);
            successCount++;
            // 清理日志文件
            log.debug("開始清理日志文件 | 任務(wù)ID:{}", taskId);
            int logCount = logService.cleanOldLogs();
            log.debug("日志文件清理完成 | 清理數(shù)量:{}", logCount);
            successCount++;
            log.info("數(shù)據(jù)清理任務(wù)完成 | 任務(wù)ID:{} | 成功子任務(wù):{} | 失敗子任務(wù):{} | 總耗時:{}ms", 
                    taskId, successCount, failureCount, 
                    System.currentTimeMillis() - startTime);
        } catch (Exception e) {
            log.error("數(shù)據(jù)清理任務(wù)異常 | 任務(wù)ID:{} | 異常類型:{}", 
                     taskId, e.getClass().getSimpleName(), e);
            throw e;
        }
    }
}

7. 安全認證日志

7.1 JWT 認證日志

@Component
@Slf4j
public class JwtTokenProvider {
    public String generateToken(UserDetails userDetails) {
        log.debug("生成JWT Token開始 | 用戶名:{} | 角色:{}", 
                 userDetails.getUsername(), userDetails.getAuthorities());
        String token = doGenerateToken(userDetails);
        log.info("JWT Token生成成功 | 用戶名:{} | Token前綴:{}", 
                userDetails.getUsername(), token.substring(0, 20));
        return token;
    }
    public boolean validateToken(String token) {
        log.debug("驗證JWT Token | Token長度:{}", token.length());
        try {
            boolean isValid = doValidateToken(token);
            if (isValid) {
                log.debug("JWT Token驗證通過");
            } else {
                log.warn("JWT Token驗證失敗");
            }
            return isValid;
        } catch (ExpiredJwtException e) {
            log.warn("JWT Token已過期 | 過期時間:{}", e.getClaims().getExpiration());
            return false;
        } catch (MalformedJwtException e) {
            log.warn("JWT Token格式錯誤");
            return false;
        } catch (Exception e) {
            log.error("JWT Token驗證異常 | 異常類型:{}", e.getClass().getSimpleName(), e);
            return false;
        }
    }
}

8. 文件操作日志

8.1 文件上傳下載

@Service
@Slf4j
public class FileService {
    public FileUploadResponse uploadFile(MultipartFile file, String businessType) {
        log.info("文件上傳開始 | 文件名:{} | 文件大小:{} | 業(yè)務(wù)類型:{}", 
                file.getOriginalFilename(), file.getSize(), businessType);
        long startTime = System.currentTimeMillis();
        try {
            // 文件驗證
            validateFile(file);
            log.debug("文件驗證通過 | 文件名:{}", file.getOriginalFilename());
            // 上傳處理
            String filePath = saveFile(file);
            log.debug("文件保存完成 | 存儲路徑:{}", filePath);
            FileUploadResponse response = new FileUploadResponse(filePath, "上傳成功");
            log.info("文件上傳成功 | 文件名:{} | 文件路徑:{} | 耗時:{}ms", 
                    file.getOriginalFilename(), filePath, 
                    System.currentTimeMillis() - startTime);
            return response;
        } catch (FileSizeException e) {
            log.warn("文件上傳失敗-文件大小超限 | 文件名:{} | 實際大小:{} | 限制大小:{}", 
                    file.getOriginalFilename(), file.getSize(), e.getLimitSize());
            throw new BusinessException("文件大小超過限制");
        } catch (FileTypeException e) {
            log.warn("文件上傳失敗-文件類型不支持 | 文件名:{} | 文件類型:{}", 
                    file.getOriginalFilename(), e.getFileType());
            throw new BusinessException("不支持的文件類型");
        } catch (IOException e) {
            log.error("文件上傳失敗-IO異常 | 文件名:{} | 異常類型:{}", 
                     file.getOriginalFilename(), e.getClass().getSimpleName(), e);
            throw new SystemException("文件上傳失敗");
        }
    }
}

9. 緩存操作日志

9.1 Redis 緩存操作

@Service
@Slf4j
public class CacheService {
    public <T> T getFromCache(String key, Class<T> clazz) {
        log.debug("查詢緩存開始 | 緩存鍵:{} | 類型:{}", key, clazz.getSimpleName());
        long startTime = System.currentTimeMillis();
        try {
            T value = redisTemplate.opsForValue().get(key);
            if (value == null) {
                log.debug("緩存未命中 | 緩存鍵:{} | 耗時:{}ms", 
                         key, System.currentTimeMillis() - startTime);
            } else {
                log.debug("緩存命中 | 緩存鍵:{} | 耗時:{}ms", 
                         key, System.currentTimeMillis() - startTime);
            }
            return value;
        } catch (RedisConnectionFailureException e) {
            log.error("緩存查詢失敗-連接異常 | 緩存鍵:{}", key, e);
            throw new CacheException("緩存服務(wù)不可用");
        } catch (Exception e) {
            log.error("緩存查詢失敗-未知異常 | 緩存鍵:{} | 異常類型:{}", 
                     key, e.getClass().getSimpleName(), e);
            throw new CacheException("緩存查詢失敗");
        }
    }
    public void setToCache(String key, Object value, Duration ttl) {
        log.debug("設(shè)置緩存開始 | 緩存鍵:{} | TTL:{}", key, ttl);
        try {
            redisTemplate.opsForValue().set(key, value, ttl);
            log.debug("緩存設(shè)置成功 | 緩存鍵:{}", key);
        } catch (Exception e) {
            log.error("緩存設(shè)置失敗 | 緩存鍵:{} | 異常類型:{}", 
                     key, e.getClass().getSimpleName(), e);
            // 緩存設(shè)置失敗不應(yīng)該影響主流程
        }
    }
}

10. 全局異常處理日志

10.1 統(tǒng)一異常處理器

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
    @ExceptionHandler(BusinessException.class)
    public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException e, 
                                                               HttpServletRequest request) {
        String errorId = generateErrorId();
        log.warn("業(yè)務(wù)異常處理 | 錯誤ID:{} | 錯誤碼:{} | 錯誤信息:{} | 請求路徑:{} | 客戶端IP:{}", 
                errorId, e.getErrorCode(), e.getMessage(), 
                request.getRequestURI(), getClientIp(request));
        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                .body(new ErrorResponse(errorId, e.getErrorCode(), e.getMessage()));
    }
    @ExceptionHandler(AuthenticationException.class)
    public ResponseEntity<ErrorResponse> handleAuthException(AuthenticationException e,
                                                           HttpServletRequest request) {
        String errorId = generateErrorId();
        log.warn("認證異常處理 | 錯誤ID:{} | 錯誤信息:{} | 請求路徑:{} | 客戶端IP:{}", 
                errorId, e.getMessage(), request.getRequestURI(), getClientIp(request));
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                .body(new ErrorResponse(errorId, "UNAUTHORIZED", "認證失敗"));
    }
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleSystemException(Exception e,
                                                             HttpServletRequest request) {
        String errorId = generateErrorId();
        log.error("系統(tǒng)異常處理 | 錯誤ID:{} | 異常類型:{} | 請求路徑:{} | 客戶端IP:{} | 堆棧跟蹤:{}", 
                 errorId, e.getClass().getSimpleName(), request.getRequestURI(), 
                 getClientIp(request), getStackTrace(e));
        // 生產(chǎn)環(huán)境隱藏詳細錯誤信息
        String message = isProduction() ? "系統(tǒng)內(nèi)部錯誤" : e.getMessage();
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(new ErrorResponse(errorId, "SYSTEM_ERROR", message));
    }
}

11. 配置類日志

11.1 應(yīng)用啟動配置

@SpringBootApplication
@Slf4j
public class Application {
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(Application.class);
        // 啟動前日志
        log.info("應(yīng)用啟動開始 | 環(huán)境:{} | 版本:{} | 啟動時間:{}", 
                getActiveProfile(), getAppVersion(), LocalDateTime.now());
        try {
            ConfigurableApplicationContext context = app.run(args);
            // 啟動成功日志
            Environment env = context.getEnvironment();
            String port = env.getProperty("server.port");
            String contextPath = env.getProperty("server.servlet.context-path", "");
            log.info("應(yīng)用啟動成功 | 環(huán)境:{} | 端口:{} | 上下文路徑:{} | 啟動耗時:{}ms", 
                    getActiveProfile(), port, contextPath, 
                    System.currentTimeMillis() - startTime);
            // 記錄重要的配置信息
            log.info("數(shù)據(jù)源配置 | URL:{} | 用戶名:{}", 
                    maskDatabaseUrl(env.getProperty("spring.datasource.url")),
                    env.getProperty("spring.datasource.username"));
        } catch (Exception e) {
            log.error("應(yīng)用啟動失敗 | 異常類型:{}", e.getClass().getSimpleName(), e);
            System.exit(1);
        }
    }
}

這些示例覆蓋了 Spring Boot/Cloud 應(yīng)用中絕大多數(shù)常見的日志場景,提供了完整的、生產(chǎn)可用的日志記錄模式。在實際開發(fā)中,可以根據(jù)具體業(yè)務(wù)需求進行調(diào)整和擴展。

到此這篇關(guān)于Spring Boot/Spring Cloud 應(yīng)用日志書寫詳細指南的文章就介紹到這了,更多相關(guān)Spring Boot日志書寫內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 閱讀EnumSet抽象類源碼

    閱讀EnumSet抽象類源碼

    這篇文章主要介紹了閱讀EnumSet抽象類源碼,具有一定參考價值,需要的朋友可以了解下。
    2017-12-12
  • 客戶端Socket與服務(wù)端ServerSocket串聯(lián)實現(xiàn)網(wǎng)絡(luò)通信

    客戶端Socket與服務(wù)端ServerSocket串聯(lián)實現(xiàn)網(wǎng)絡(luò)通信

    這篇文章主要為大家介紹了客戶端Socket與服務(wù)端ServerSocket串聯(lián)實現(xiàn)網(wǎng)絡(luò)通信的內(nèi)容詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2022-03-03
  • Java多線程中的CyclicBarrier詳解

    Java多線程中的CyclicBarrier詳解

    這篇文章主要介紹了Java多線程中的CyclicBarrier詳解,同步屏障,允許一組線程互相等待以到達一個公共的障礙點,當(dāng)設(shè)定的線程數(shù)到達屏障時,阻塞的線程繼續(xù)執(zhí)行,需要的朋友可以參考下
    2023-11-11
  • Java中JS引擎實現(xiàn)的一句話木馬

    Java中JS引擎實現(xiàn)的一句話木馬

    這篇文章主要為大家介紹了如何利用Java中JS引擎實現(xiàn)的一句話木馬,有需要的朋友可以借鑒參考下,希望能夠有所幫助祝大家多多進步
    2022-02-02
  • java生成sm2/hutool生成公鑰私鑰代碼示例

    java生成sm2/hutool生成公鑰私鑰代碼示例

    這篇文章主要給大家介紹了關(guān)于java生成sm2/hutool生成公鑰私鑰的相關(guān)資料,Java是一種廣泛使用的編程語言,可以用來生成公鑰和私鑰文件,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2023-09-09
  • Java selenium上傳文件的實現(xiàn)

    Java selenium上傳文件的實現(xiàn)

    本文主要介紹了Java selenium上傳文件的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • java實現(xiàn)通用分頁(后端)

    java實現(xiàn)通用分頁(后端)

    這篇文章主要介紹了java實現(xiàn)通用分頁(后端)方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • spring boot動態(tài)加載Echart餅狀圖

    spring boot動態(tài)加載Echart餅狀圖

    這篇文章主要為大家詳細介紹了spring boot動態(tài)加載Echart餅狀圖,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-12-12
  • mybatis數(shù)組和集合的長度判斷及插入方式

    mybatis數(shù)組和集合的長度判斷及插入方式

    這篇文章主要介紹了mybatis數(shù)組和集合的長度判斷及插入方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • java導(dǎo)出json格式文件的示例代碼

    java導(dǎo)出json格式文件的示例代碼

    本篇文章主要介紹了java導(dǎo)出json格式文件的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-09-09

最新評論

稻城县| 凤冈县| 花垣县| 松滋市| 大宁县| 庄浪县| 靖安县| 夹江县| 新平| 洛阳市| 建湖县| 宁陵县| 曲麻莱县| 五寨县| 温州市| 佛学| 安徽省| 咸丰县| 长治县| 长武县| 嵩明县| 西华县| 济南市| 莒南县| 金门县| 东台市| 乐至县| 古丈县| 鹰潭市| 施甸县| 商都县| 金门县| 望城县| 萨迦县| 枣庄市| 井研县| 克拉玛依市| 金塔县| 南和县| 五家渠市| 金坛市|