关于 logging-patterns
logging-patterns 是面向 Java 应用开发者的日志实践技能,侧重结构化 JSON 日志、请求追踪与 AI 可解析性。内容涵盖配置示例和日志编写、分析建议。
主要功能
- 01结构化 JSON 日志配置
- 02Spring Boot 多版本方案
- 03MDC 请求上下文追踪
- 04日志级别与字段实践
- 05使用 jq 筛选和分析日志
- 06异常与敏感数据处理
适用场景
- 01为 Spring Boot 应用添加结构化日志
- 02通过 requestId 追踪请求处理流程
- 03让 AI 分析应用日志与错误
- 04检查日志中的敏感信息和异常上下文
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ decebals/ claude-code-java --skill logging-patterns安装后在 Claude Code 中输入/logging-patterns确认已加载。
附带文件README.md
详细介绍
日志记录模式技能
面向 Java 应用的有效日志记录,重点关注结构化、便于 AI 解析的格式。
适用场景
- 用户说“添加日志”/“改进日志”/“调试这个”
- 根据日志分析应用流程
- 设置结构化日志(JSON)
- 使用关联 ID 追踪请求
- AI/Claude Code 需要分析应用行为
面向 AI 的日志记录
关键见解: JSON 日志更适合 AI 分析——解析更快、占用的令牌更少,还能直接访问字段。
为什么 AI/Claude Code 适合使用 JSON?
# Text format - AI must "interpret" the string
2026-01-29 10:15:30 INFO OrderService - Order 12345 created for user-789, total: 99.99
# JSON format - AI extracts fields directly
{"timestamp":"2026-01-29T10:15:30Z","level":"INFO","orderId":12345,"userId":"user-789","total":99.99}
| 方面 | 文本 | JSON |
|---|---|---|
| 解析 | 正则表达式/人工解读 | 直接访问字段 |
| 令牌用量 | 更高(重复模式) | 更低(结构化) |
| 提取错误 | 解析堆栈跟踪文本 | exception 字段 |
| 筛选 | grep 模式 | jq 查询 |
AI 辅助开发的推荐配置
# application.yml - JSON by default
logging:
structured:
format:
console: logstash # Spring Boot 3.4+
# When YOU need to read logs manually:
# Option 1: Use jq
# tail -f app.log | jq .
# Option 2: Switch profile temporarily
# java -jar app.jar --spring.profiles.active=human-logs
针对 AI 分析优化的日志格式
{
"timestamp": "2026-01-29T10:15:30.123Z",
"level": "INFO",
"logger": "com.example.OrderService",
"message": "Order created",
"requestId": "req-abc123",
"traceId": "trace-xyz",
"orderId": 12345,
"userId": "user-789",
"duration_ms": 45,
"step": "payment_completed"
}
AI 调试的关键字段:
requestId- 将同一请求的所有日志归为一组step- 跟踪流程进度duration_ms- 识别耗时操作level- 快速筛选错误
使用 AI/Claude Code 读取日志
让 AI 分析日志时:
# Get recent errors
cat app.log | jq 'select(.level == "ERROR")' | tail -20
# Follow specific request
cat app.log | jq 'select(.requestId == "req-abc123")'
# Find slow operations
cat app.log | jq 'select(.duration_ms > 1000)'
AI 随后可以:
- 直接解析 JSON(无需猜测)
- 通过 requestId 跟踪请求流程
- 准确定位错误发生的位置
- 测量各步骤之间的耗时
快速配置(Spring Boot 3.4+)
原生结构化日志
Spring Boot 3.4+ 内置支持,无需额外依赖!
# application.yml
logging:
structured:
format:
console: logstash # or "ecs" for Elastic Common Schema
# Supported formats: logstash, ecs, gelf
基于配置文件的切换
# application.yml (default - JSON for AI/prod)
spring:
profiles:
default: json-logs
---
spring:
config:
activate:
on-profile: json-logs
logging:
structured:
format:
console: logstash
---
spring:
config:
activate:
on-profile: human-logs
# No structured format = human-readable default
logging:
pattern:
console: "%d{HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n"
用法:
# Default: JSON (for AI, CI/CD, production)
./mvnw spring-boot:run
# Human-readable when needed
./mvnw spring-boot:run -Dspring.profiles.active=human-logs
Spring Boot < 3.4 的配置
Logstash Logback Encoder
pom.xml:
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>7.4</version>
</dependency>
logback-spring.xml:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<springProfile name="!human-logs">
<appender name="JSON" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<includeMdcKeyName>requestId</includeMdcKeyName>
<includeMdcKeyName>userId</includeMdcKeyName>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="JSON"/>
</root>
</springProfile>
<springProfile name="human-logs">
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</springProfile>
</configuration>
添加自定义字段(Logstash Encoder)
import static net.logstash.logback.argument.StructuredArguments.kv;
// Fields appear as separate JSON keys
log.info("Order created",
kv("orderId", order.getId()),
kv("userId", user.getId()),
kv("total", order.getTotal()),
kv("step", "order_created")
);
// Output:
// {"message":"Order created","orderId":123,"userId":"u-456","total":99.99,"step":"order_created"}
SLF4J 基础
Logger 声明
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Service
public class OrderService {
private static final Logger log = LoggerFactory.getLogger(OrderService.class);
}
// Or with Lombok
@Slf4j
@Service
public class OrderService {
// use `log` directly
}
参数化日志记录
// ✅ GOOD: Evaluated only if level enabled
log.debug("Processing order {} for user {}", orderId, userId);
// ❌ BAD: Always concatenates
log.debug("Processing order " + orderId + " for user " + userId);
// ✅ For expensive operations
if (log.isDebugEnabled()) {
log.debug("Full order details: {}", order.toJson());
}
日志级别
| 级别 | 适用场景 | 示例 |
|---|---|---|
| ERROR | 需要关注的故障 | 未处理的异常、服务宕机 |
| WARN | 意外情况,但已处理 | 重试成功、使用了已弃用的 API |
| INFO | 业务事件 | 订单已创建、付款已处理 |
| DEBUG | 技术细节 | 方法参数、SQL 查询 |
| TRACE | 非常详细的信息 | 循环迭代(很少使用) |
log.error("Payment failed", kv("orderId", id), kv("reason", reason), exception);
log.warn("Retry succeeded", kv("attempt", 3), kv("orderId", id));
log.info("Order shipped", kv("orderId", id), kv("trackingNumber", tracking));
log.debug("Fetching from DB", kv("query", "findById"), kv("id", id));
MDC(映射诊断上下文)
MDC 会为请求中的每条日志添加上下文,是追踪请求的关键。
请求 ID 过滤器
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class RequestContextFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {
try {
String requestId = Optional.ofNullable(request.getHeader("X-Request-ID"))
.filter(s -> !s.isBlank())
.orElse(UUID.randomUUID().toString().substring(0, 8));
MDC.put("requestId", requestId);
response.setHeader("X-Request-ID", requestId);
chain.doFilter(request, response);
} finally {
MDC.clear();
}
}
}
添加用户上下文
// After authentication
MDC.put("userId", authentication.getName());
// All subsequent logs include userId automatically
log.info("User action performed"); // {"userId":"john123","message":"User action performed"}
异步操作中的 MDC
// MDC doesn't auto-propagate to new threads!
// ✅ Copy MDC context
Map<String, String> context = MDC.getCopyOfContextMap();
CompletableFuture.runAsync(() -> {
try {
if (context != null) MDC.setContextMap(context);
log.info("Async task running"); // Has requestId, userId
} finally {
MDC.clear();
}
});
应记录的内容
业务事件(INFO)
// Include key identifiers and state
log.info("Order created",
kv("orderId", id),
kv("userId", userId),
kv("total", total),
kv("itemCount", items.size()),
kv("step", "order_created"));
log.info("Payment processed",
kv("orderId", id),
kv("amount", amount),
kv("method", "card"),
kv("step", "payment_completed"));
外部调用(包含耗时)
long start = System.currentTimeMillis();
try {
Result result = externalService.call(params);
log.info("External call succeeded",
kv("service", "PaymentGateway"),
kv("operation", "charge"),
kv("duration_ms", System.currentTimeMillis() - start));
return result;
} catch (Exception e) {
log.error("External call failed",
kv("service", "PaymentGateway"),
kv("operation", "charge"),
kv("duration_ms", System.currentTimeMillis() - start),
e);
throw e;
}
流程步骤(用于 AI 追踪)
public Order processOrder(CreateOrderRequest request) {
log.info("Processing started", kv("step", "start"), kv("requestData", request.summary()));
Order order = createOrder(request);
log.info("Order created", kv("step", "order_created"), kv("orderId", order.getId()));
validateInventory(order);
log.info("Inventory validated", kv("step", "inventory_ok"), kv("orderId", order.getId()));
processPayment(order);
log.info("Payment processed", kv("step", "payment_done"), kv("orderId", order.getId()));
log.info("Processing completed", kv("step", "complete"), kv("orderId", order.getId()));
return order;
}
不应记录的内容
// ❌ NEVER log sensitive data
log.info("Login", kv("password", password)); // Passwords
log.info("Payment", kv("cardNumber", card)); // Full card numbers
log.info("Request", kv("token", jwtToken)); // Tokens
log.info("User", kv("ssn", socialSecurity)); // PII
// ✅ Safe alternatives
log.info("Login attempted", kv("userId", userId));
log.info("Payment", kv("cardLast4", last4));
log.info("Token validated", kv("subject", sub), kv("exp", expiry));
异常日志记录
在边界处只记录一次
// ❌ BAD: Logs same exception multiple times
void methodA() {
try { methodB(); }
catch (Exception e) { log.error("Error", e); throw e; } // Log #1
}
void methodB() {
try { methodC(); }
catch (Exception e) { log.error("Error", e); throw e; } // Log #2
}
// ✅ GOOD: Log at service boundary only
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<?> handle(Exception e, HttpServletRequest request) {
log.error("Request failed",
kv("path", request.getRequestURI()),
kv("method", request.getMethod()),
kv("errorType", e.getClass().getSimpleName()),
e); // Full stack trace
return ResponseEntity.status(500).body(errorResponse);
}
}
包含上下文
// ❌ Useless
log.error("Error occurred", e);
// ✅ Useful for debugging
log.error("Order processing failed",
kv("orderId", orderId),
kv("step", "payment"),
kv("userId", userId),
kv("attemptNumber", attempt),
e);
快速参考
// === Setup ===
private static final Logger log = LoggerFactory.getLogger(MyClass.class);
// === Logging with structured fields ===
import static net.logstash.logback.argument.StructuredArguments.kv;
log.info("Event", kv("key1", value1), kv("key2", value2));
log.error("Failed", kv("context", ctx), exception);
// === MDC ===
MDC.put("requestId", requestId);
MDC.put("userId", userId);
// ... all logs now include these
MDC.clear(); // cleanup
// === Levels ===
log.error() // Failures
log.warn() // Handled issues
log.info() // Business events
log.debug() // Technical details
分析日志(AI/人工)
# Pretty print JSON logs
tail -f app.log | jq .
# Filter errors
cat app.log | jq 'select(.level == "ERROR")'
# Follow request flow
cat app.log | jq 'select(.requestId == "abc123")'
# Find slow operations (>1s)
cat app.log | jq 'select(.duration_ms > 1000)'
# Get timeline of steps
cat app.log | jq 'select(.requestId == "abc123") | {time: .timestamp, step: .step, message: .message}'
相关技能
spring-boot-patterns- Spring Boot 配置jpa-patterns- 数据库日志(SQL 查询)- 未来:
observability-patterns- 指标、追踪和完整的可观测性
评论
支持 Markdown