项目地址: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
本章小结
✅ 完成的任务
- 完整示例应用:构建了电商系统的核心模块
- 注解应用演示:展示了@LogMethod和@LogClass的实际使用
- 链路追踪验证:验证了分布式环境下的上下文传递
- 敏感信息脱敏:测试了密码、支付信息等敏感数据的处理
- 性能测试:验证了高并发场景下的框架表现
🎯 学习要点
- 实际应用场景中的注解配置策略
- 敏感信息处理的最佳实践
- 链路追踪在微服务中的价值
- 性能监控的重要性
- 测试策略的完整性
💡 思考题
- 如何在更复杂的微服务架构中应用这个框架?
- 生产环境中还需要考虑哪些优化点?
- 如何与现有的日志系统进行集成?
🎉 系列总结
经过10章的学习,我们从零开始构建了一个完整的企业级日志框架:
技术栈掌握
- ✅ Java注解:设计优雅的API接口
- ✅ Spring AOP:实现无侵入的切面编程
- ✅ 配置管理:灵活的配置体系设计
- ✅ 链路追踪:分布式环境下的请求跟踪
- ✅ Web集成:HTTP请求的全链路追踪
- ✅ Spring Boot Starter:自动配置的最佳实践
- ✅ 监控运维:Actuator集成和健康检查
框架特性
- 🎯 注解驱动:声明式的日志配置
- 🔄 链路追踪:完整的请求生命周期跟踪
- 🛡️ 安全处理:敏感信息自动脱敏
- ⚡ 高性能:低开销的运行时性能
- 🏥 可观测性:完善的监控和运维支持
生产级特性
- 📦 开箱即用:Spring Boot Starter自动配置
- 🔧 灵活配置:支持多层级配置合并
- 🚀 易于扩展:模块化的架构设计
- 🔍 调试友好:详细的日志信息和监控指标
这个框架不仅展示了如何从零开始构建一个完整的技术框架,更重要的是体现了企业级软件开发的思维方式和最佳实践。希望通过这个系列,能够帮助你在技术成长的道路上更进一步!
💡 最后的话: 技术框架的价值不在于功能的复杂度,而在于能否真正解决实际问题,提升开发效率。愿这个框架能在你的项目中发挥价值!