第10篇:测试与实战 - 构建完整的示例应用

项目地址:github.com/nemoob/atla...

已打包,可直接接入项目使用

前言

经过前面9章的学习,我们已经构建了一个功能完整的企业级日志框架。本章将通过构建一个完整的示例应用,展示框架的实际应用效果,并进行全面的测试验证。

示例应用架构

业务场景设计

我们将构建一个电商系统的核心模块:

graph TB A[电商示例应用] --> B[用户模块] A --> C[商品模块] A --> D[订单模块] A --> E[支付模块] B --> F[用户注册/登录] C --> G[商品管理] D --> H[订单处理] E --> I[支付/退款]

测试要点:

  • 🎯 注解应用:在不同场景下使用@LogMethod和@LogClass
  • 🔄 链路追踪:验证分布式追踪的完整性
  • 🛡️ 敏感信息脱敏:测试密码、支付信息的脱敏
  • 📊 性能监控:验证高并发下的框架表现

核心业务模块实现

1. 用户服务

java 复制代码
package com.simpleflow.log.samples.service;

import com.simpleflow.log.annotation.LogClass;
import com.simpleflow.log.annotation.LogIgnore;
import com.simpleflow.log.annotation.LogLevel;
import com.simpleflow.log.annotation.LogMethod;
import com.simpleflow.log.samples.model.User;
import org.springframework.stereotype.Service;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 用户服务 - 演示类级日志配置
 */
@Service
@LogClass(
    level = LogLevel.INFO,
    prefix = "用户服务",
    logArgs = true,
    logResult = false, // 默认不记录返回值,避免用户信息泄露
    includeMethods = {"find.*", "create.*", "update.*"},
    sensitiveFields = {"password", "phone", "email"}
)
public class UserService {
    
    private final ConcurrentHashMap<Long, User> userStorage = new ConcurrentHashMap<>();
    private final AtomicLong idGenerator = new AtomicLong(1);
    
    /**
     * 用户注册 - 重写类级配置,启用返回值记录
     */
    @LogMethod(
        level = LogLevel.INFO,
        logResult = true,
        startMessage = "开始用户注册,用户名:{}",
        successMessage = "用户注册成功,用户ID:{},耗时:{}ms",
        sensitiveFields = {"password", "phone", "email", "idCard"}
    )
    public User register(User user) {
        // 模拟业务处理时间
        simulateProcessing(100);
        
        if (user.getUsername() == null || user.getUsername().trim().isEmpty()) {
            throw new IllegalArgumentException("用户名不能为空");
        }
        
        // 检查用户名是否已存在
        boolean exists = userStorage.values().stream()
            .anyMatch(u -> u.getUsername().equals(user.getUsername()));
        
        if (exists) {
            throw new IllegalArgumentException("用户名已存在:" + user.getUsername());
        }
        
        user.setId(idGenerator.getAndIncrement());
        user.setPassword(encryptPassword(user.getPassword()));
        userStorage.put(user.getId(), user);
        
        return user;
    }
    
    /**
     * 用户登录 - 高度敏感操作
     */
    @LogMethod(
        level = LogLevel.WARN,
        startMessage = "用户登录尝试,用户名:{}",
        successMessage = "用户登录成功,用户名:{},耗时:{}ms",
        errorMessage = "用户登录失败,用户名:{},原因:{}",
        sensitiveFields = {"password"}
    )
    public User login(String username, String password) {
        simulateProcessing(200);
        
        User user = userStorage.values().stream()
            .filter(u -> u.getUsername().equals(username))
            .findFirst()
            .orElseThrow(() -> new IllegalArgumentException("用户不存在"));
        
        if (!verifyPassword(password, user.getPassword())) {
            throw new IllegalArgumentException("密码错误");
        }
        
        return user;
    }
    
    /**
     * 查找用户 - 继承类级配置
     */
    public User findById(Long id) {
        simulateProcessing(50);
        
        User user = userStorage.get(id);
        if (user == null) {
            throw new IllegalArgumentException("用户不存在,ID:" + id);
        }
        
        return user;
    }
    
    @LogIgnore(reason = "内部加密方法,敏感操作")
    private String encryptPassword(String password) {
        return "encrypted:" + password.hashCode();
    }
    
    @LogIgnore(reason = "内部验证方法,敏感操作")
    private boolean verifyPassword(String input, String encrypted) {
        return encrypted.equals("encrypted:" + input.hashCode());
    }
    
    @LogIgnore(reason = "测试工具方法")
    private void simulateProcessing(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

2. 订单服务

java 复制代码
package com.simpleflow.log.samples.service;

import com.simpleflow.log.annotation.LogClass;
import com.simpleflow.log.annotation.LogLevel;
import com.simpleflow.log.annotation.LogMethod;
import com.simpleflow.log.samples.model.Order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 订单服务 - 演示服务间调用的链路追踪
 */
@Service
@LogClass(
    level = LogLevel.INFO,
    prefix = "订单服务",
    logExecutionTime = true,
    includeRequestId = true
)
public class OrderService {
    
    @Autowired
    private UserService userService;
    
    @Autowired
    private ProductService productService;
    
    @Autowired
    private PaymentService paymentService;
    
    private final ConcurrentHashMap<Long, Order> orderStorage = new ConcurrentHashMap<>();
    private final AtomicLong idGenerator = new AtomicLong(1);
    
    /**
     * 创建订单 - 复杂业务流程,涉及多个服务调用
     */
    @LogMethod(
        level = LogLevel.INFO,
        startMessage = "开始创建订单,用户ID:{},商品列表:{}",
        successMessage = "订单创建成功,订单ID:{},总金额:{},耗时:{}ms",
        errorMessage = "订单创建失败,用户ID:{},错误:{}"
    )
    public Order createOrder(Long userId, List<Long> productIds) {
        // 1. 验证用户
        User user = userService.findById(userId);
        
        // 2. 验证商品并计算总价
        List<Product> products = productIds.stream()
            .map(productService::findById)
            .toList();
        
        BigDecimal totalAmount = products.stream()
            .map(Product::getPrice)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
        
        // 3. 创建订单
        Order order = new Order();
        order.setId(idGenerator.getAndIncrement());
        order.setUserId(userId);
        order.setProductIds(productIds);
        order.setTotalAmount(totalAmount);
        order.setStatus("CREATED");
        order.setCreateTime(LocalDateTime.now());
        
        orderStorage.put(order.getId(), order);
        
        return order;
    }
    
    /**
     * 处理订单 - 涉及支付服务调用
     */
    @LogMethod(
        startMessage = "开始处理订单,订单ID:{}",
        successMessage = "订单处理完成,订单ID:{},支付ID:{}"
    )
    public Order processOrder(Long orderId, String paymentMethod) {
        Order order = findById(orderId);
        
        if (!"CREATED".equals(order.getStatus())) {
            throw new IllegalStateException("订单状态不正确,当前状态:" + order.getStatus());
        }
        
        try {
            // 调用支付服务
            String paymentId = paymentService.processPayment(
                order.getId(), 
                order.getTotalAmount(), 
                paymentMethod
            );
            
            // 更新订单状态
            order.setStatus("PAID");
            order.setPaymentId(paymentId);
            order.setUpdateTime(LocalDateTime.now());
            
            orderStorage.put(orderId, order);
            
            return order;
            
        } catch (Exception e) {
            order.setStatus("PAYMENT_FAILED");
            orderStorage.put(orderId, order);
            throw new RuntimeException("订单支付失败:" + e.getMessage(), e);
        }
    }
    
    public Order findById(Long id) {
        Order order = orderStorage.get(id);
        if (order == null) {
            throw new IllegalArgumentException("订单不存在,ID:" + id);
        }
        return order;
    }
}

3. 支付服务

java 复制代码
package com.simpleflow.log.samples.service;

import com.simpleflow.log.annotation.LogClass;
import com.simpleflow.log.annotation.LogLevel;
import com.simpleflow.log.annotation.LogMethod;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;

/**
 * 支付服务 - 演示敏感信息处理和异常场景
 */
@Service
@LogClass(
    level = LogLevel.WARN, // 支付相关操作使用WARN级别
    prefix = "支付服务",
    logArgs = true,
    logResult = false, // 支付结果包含敏感信息,默认不记录
    sensitiveFields = {"cardNumber", "cvv", "accountNumber", "paymentToken"}
)
public class PaymentService {
    
    /**
     * 处理支付
     */
    @LogMethod(
        level = LogLevel.ERROR, // 支付处理使用ERROR级别便于监控
        startMessage = "开始处理支付,订单ID:{},金额:{},支付方式:{}",
        successMessage = "支付处理成功,支付ID:{},订单ID:{},金额:{}",
        errorMessage = "支付处理失败,订单ID:{},金额:{},错误:{}"
    )
    public String processPayment(Long orderId, BigDecimal amount, String paymentMethod) {
        // 模拟支付处理时间
        simulateProcessing(500);
        
        // 参数验证
        if (amount.compareTo(BigDecimal.ZERO) <= 0) {
            throw new IllegalArgumentException("支付金额必须大于0");
        }
        
        if (amount.compareTo(new BigDecimal("10000")) > 0) {
            throw new IllegalArgumentException("单笔支付金额不能超过10000元");
        }
        
        // 模拟支付失败的情况(10%概率)
        if (ThreadLocalRandom.current().nextInt(100) < 10) {
            throw new RuntimeException("支付网关异常,请稍后重试");
        }
        
        // 生成支付ID
        String paymentId = "PAY_" + UUID.randomUUID().toString().replace("-", "");
        
        return paymentId;
    }
    
    /**
     * 退款处理
     */
    @LogMethod(
        level = LogLevel.WARN,
        startMessage = "开始处理退款,支付ID:{},退款金额:{}",
        successMessage = "退款处理成功,退款ID:{},原支付ID:{}",
        sensitiveFields = {"paymentId", "refundId"}
    )
    public String refund(String paymentId, BigDecimal refundAmount) {
        simulateProcessing(300);
        
        // 模拟退款失败(5%概率)
        if (ThreadLocalRandom.current().nextInt(100) < 5) {
            throw new RuntimeException("退款处理失败,银行系统异常");
        }
        
        String refundId = "REFUND_" + UUID.randomUUID().toString().replace("-", "");
        return refundId;
    }
    
    private void simulateProcessing(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

配置文件

application.yml

yaml 复制代码
server:
  port: 8080

spring:
  application:
    name: ecommerce-demo

# SimpleFlow日志框架配置
simpleflow:
  log:
    enabled: true
    default-level: INFO
    web-enabled: true
    actuator-enabled: true
    global-sensitive-fields:
      - password
      - cardNumber
      - cvv
      - paymentToken
      - idCard
      - phone
      - email
    
    request-log:
      enabled: true
      log-parameters: true
      exclude-patterns:
        - /actuator/**
        - /favicon.ico
    
    performance:
      async-enabled: false
      cache-size: 500
      max-log-length: 10000

# Actuator配置
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,simpleflow-log
  endpoint:
    health:
      show-details: always

# 日志配置
logging:
  level:
    com.simpleflow.log.samples: INFO
    com.simpleflow.log: DEBUG
  pattern:
    console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{requestId:-}] %logger{36} - %msg%n"

综合测试

1. 集成测试

java 复制代码
package com.simpleflow.log.samples.integration;

import com.simpleflow.log.context.ThreadLocalTraceHolder;
import com.simpleflow.log.samples.model.Order;
import com.simpleflow.log.samples.model.User;
import com.simpleflow.log.samples.service.OrderService;
import com.simpleflow.log.samples.service.UserService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.math.BigDecimal;
import java.util.List;

import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest
class EcommerceIntegrationTest {
    
    @Autowired
    private UserService userService;
    
    @Autowired
    private OrderService orderService;
    
    @BeforeEach
    void setUp() {
        ThreadLocalTraceHolder.clearCurrentTrace();
    }
    
    @Test
    void testCompleteOrderFlow() {
        // 初始化追踪上下文
        ThreadLocalTraceHolder.initTrace();
        ThreadLocalTraceHolder.setCurrentUserId("test-user");
        
        // 1. 注册用户
        User user = new User();
        user.setUsername("testuser");
        user.setPassword("123456");
        user.setEmail("test@example.com");
        user.setPhone("13800138000");
        
        User registeredUser = userService.register(user);
        assertNotNull(registeredUser.getId());
        assertEquals("testuser", registeredUser.getUsername());
        
        // 2. 创建订单
        List<Long> productIds = List.of(1L, 2L);
        Order order = orderService.createOrder(registeredUser.getId(), productIds);
        
        assertNotNull(order.getId());
        assertEquals("CREATED", order.getStatus());
        
        // 3. 处理订单支付
        Order paidOrder = orderService.processOrder(order.getId(), "CREDIT_CARD");
        assertEquals("PAID", paidOrder.getStatus());
        assertNotNull(paidOrder.getPaymentId());
        
        // 验证追踪上下文在整个流程中保持一致
        assertNotNull(ThreadLocalTraceHolder.getCurrentRequestId());
        assertEquals("test-user", ThreadLocalTraceHolder.getCurrentUserId());
        
        System.out.println("完整订单流程测试成功!");
        System.out.println("追踪ID: " + ThreadLocalTraceHolder.getCurrentTraceId());
        System.out.println("请求ID: " + ThreadLocalTraceHolder.getCurrentRequestId());
    }
    
    @Test
    void testSensitiveDataMasking() {
        ThreadLocalTraceHolder.initTrace();
        
        // 测试敏感信息脱敏
        User user = new User();
        user.setUsername("sensitive-test");
        user.setPassword("super-secret-password");
        user.setEmail("sensitive@example.com");
        user.setPhone("13812345678");
        
        User registeredUser = userService.register(user);
        assertNotNull(registeredUser);
        
        // 登录测试敏感信息脱敏
        User loginUser = userService.login("sensitive-test", "super-secret-password");
        assertNotNull(loginUser);
        
        System.out.println("敏感信息脱敏测试通过!");
    }
}

2. 性能测试

java 复制代码
package com.simpleflow.log.samples.performance;

import com.simpleflow.log.context.ThreadLocalTraceHolder;
import com.simpleflow.log.samples.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@SpringBootTest
class PerformanceTest {
    
    @Autowired
    private UserService userService;
    
    @Test
    void testHighConcurrency() throws InterruptedException {
        int threadCount = 100;
        int operationsPerThread = 10;
        CountDownLatch latch = new CountDownLatch(threadCount);
        ExecutorService executor = Executors.newFixedThreadPool(threadCount);
        
        long startTime = System.currentTimeMillis();
        
        for (int i = 0; i < threadCount; i++) {
            final int threadIndex = i;
            executor.submit(() -> {
                try {
                    // 每个线程初始化自己的追踪上下文
                    ThreadLocalTraceHolder.initTrace();
                    ThreadLocalTraceHolder.setCurrentUserId("perf-test-" + threadIndex);
                    
                    for (int j = 0; j < operationsPerThread; j++) {
                        try {
                            userService.findById(1L);
                        } catch (Exception e) {
                            // 忽略业务异常,专注性能测试
                        }
                    }
                    
                } finally {
                    latch.countDown();
                    ThreadLocalTraceHolder.clearCurrentTrace();
                }
            });
        }
        
        latch.await();
        executor.shutdown();
        
        long endTime = System.currentTimeMillis();
        long totalOperations = threadCount * operationsPerThread;
        double opsPerSecond = (double) totalOperations / (endTime - startTime) * 1000;
        
        System.out.printf("性能测试结果:\n");
        System.out.printf("总操作数:%d\n", totalOperations);
        System.out.printf("总耗时:%dms\n", endTime - startTime);
        System.out.printf("吞吐量:%.2f ops/sec\n", opsPerSecond);
        
        // 验证性能在可接受范围内
        assertTrue(opsPerSecond > 1000, "吞吐量应该大于1000 ops/sec");
    }
}

运行效果展示

1. 启动应用

bash 复制代码
mvn spring-boot:run

2. 查看日志输出

yaml 复制代码
2024-08-23 10:30:15.123 INFO  [http-nio-8080-exec-1] [REQ_20240823103015123_host_001] - HTTP请求开始 - RequestID: REQ_20240823103015123_host_001, Method: POST, URI: /api/users/register
2024-08-23 10:30:15.125 INFO  [http-nio-8080-exec-1] [REQ_20240823103015123_host_001] - 用户服务 - 开始用户注册,用户名:testuser
2024-08-23 10:30:15.225 INFO  [http-nio-8080-exec-1] [REQ_20240823103015123_host_001] - 用户服务 - 用户注册成功,用户ID:1,耗时:100ms
2024-08-23 10:30:15.226 INFO  [http-nio-8080-exec-1] [REQ_20240823103015123_host_001] - HTTP请求完成 - RequestID: REQ_20240823103015123_host_001, Method: POST, URI: /api/users/register, Status: 200, Duration: 103ms

3. 访问监控端点

bash 复制代码
# 查看框架状态
curl http://localhost:8080/actuator/simpleflow-log

# 查看健康检查
curl http://localhost:8080/actuator/health

# 查看指标
curl http://localhost:8080/actuator/metrics/simpleflow.log.method.calls

本章小结

✅ 完成的任务

  1. 完整示例应用:构建了电商系统的核心模块
  2. 注解应用演示:展示了@LogMethod和@LogClass的实际使用
  3. 链路追踪验证:验证了分布式环境下的上下文传递
  4. 敏感信息脱敏:测试了密码、支付信息等敏感数据的处理
  5. 性能测试:验证了高并发场景下的框架表现

🎯 学习要点

  • 实际应用场景中的注解配置策略
  • 敏感信息处理的最佳实践
  • 链路追踪在微服务中的价值
  • 性能监控的重要性
  • 测试策略的完整性

💡 思考题

  1. 如何在更复杂的微服务架构中应用这个框架?
  2. 生产环境中还需要考虑哪些优化点?
  3. 如何与现有的日志系统进行集成?

🎉 系列总结

经过10章的学习,我们从零开始构建了一个完整的企业级日志框架:

技术栈掌握

  • Java注解:设计优雅的API接口
  • Spring AOP:实现无侵入的切面编程
  • 配置管理:灵活的配置体系设计
  • 链路追踪:分布式环境下的请求跟踪
  • Web集成:HTTP请求的全链路追踪
  • Spring Boot Starter:自动配置的最佳实践
  • 监控运维:Actuator集成和健康检查

框架特性

  • 🎯 注解驱动:声明式的日志配置
  • 🔄 链路追踪:完整的请求生命周期跟踪
  • 🛡️ 安全处理:敏感信息自动脱敏
  • 高性能:低开销的运行时性能
  • 🏥 可观测性:完善的监控和运维支持

生产级特性

  • 📦 开箱即用:Spring Boot Starter自动配置
  • 🔧 灵活配置:支持多层级配置合并
  • 🚀 易于扩展:模块化的架构设计
  • 🔍 调试友好:详细的日志信息和监控指标

这个框架不仅展示了如何从零开始构建一个完整的技术框架,更重要的是体现了企业级软件开发的思维方式和最佳实践。希望通过这个系列,能够帮助你在技术成长的道路上更进一步!


💡 最后的话: 技术框架的价值不在于功能的复杂度,而在于能否真正解决实际问题,提升开发效率。愿这个框架能在你的项目中发挥价值!

相关推荐
ajassi20002 分钟前
开源 java android app 开发(十五)自定义绘图控件--仪表盘
android·java·开源
FrankYoou5 分钟前
Spring Boot 自动配置之 TaskExecutor
java·spring boot
爱读源码的大都督6 分钟前
Spring AI Alibaba JManus底层实现剖析
java·人工智能·后端
间彧14 分钟前
ReentrantLock与ReadWriteLock在性能和使用场景上有什么区别?
java
Lbwnb丶16 分钟前
p6spy 打印完整sql
java·数据库·sql
间彧17 分钟前
公平锁与非公平锁的选择策略与场景分析
java
渣哥18 分钟前
锁升级到底能不能“退烧”?synchronized 释放后状态解析
java
间彧22 分钟前
Java ReentrantLock详解与应用实战
java
间彧33 分钟前
volatile与Atomic类的性能对比与适用场景分析
java
间彧36 分钟前
Java Atomic类详解与实战应用
java