一键导入
logging-patterns
Use when implementing or reviewing logging to enforce SLF4J best practices, structured JSON logging, and MDC usage for traceability.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Use when implementing or reviewing logging to enforce SLF4J best practices, structured JSON logging, and MDC usage for traceability.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Use when implementing Spring Boot 3.x applications. Provides hands-on implementation patterns for web, data, security, testing, and cloud-native development.
Review REST API contracts for HTTP semantics, versioning, backward compatibility, and response consistency. Use when user asks "review API", "check endpoints", "REST review", or before releasing API changes.
Use when designing Spring Boot 3.x application architecture, making technology decisions, or planning project structure. Provides architectural patterns and references for project setup, JPA, security, testing, and reactive programming.
Use when designing, reviewing, or modifying REST API endpoints to enforce proper HTTP verbs, status codes, versioning, and API contract best practices.
Use when reviewing or writing code to enforce DRY, KISS, and YAGNI principles with Java-specific guidance.
Use when implementing or reviewing code that benefits from established design patterns like Builder, Factory, Strategy, Observer, Decorator, or Adapter.
| name | logging-patterns |
| description | Use when implementing or reviewing logging to enforce SLF4J best practices, structured JSON logging, and MDC usage for traceability. |
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Service
public class OrderService {
private static final Logger log = LoggerFactory.getLogger(OrderService.class);
public OrderResponse createOrder(OrderRequest request) {
log.info("Creating order for customer: {}", request.customerId());
// ...
log.info("Order created successfully: orderId={}", order.getId());
return response;
}
}
| Level | Usage | Example |
|---|---|---|
| ERROR | System failures requiring immediate attention | Database down, external service failure |
| WARN | Potential problems, recoverable issues | Retry succeeded, deprecated API usage |
| INFO | Business events, application lifecycle | Order placed, user logged in, app started |
| DEBUG | Detailed flow for troubleshooting | Method entry/exit, variable values |
| TRACE | Very detailed diagnostic information | Loop iterations, raw request/response bodies |
// Bad - string concatenation always evaluated
log.debug("Processing order " + orderId + " for customer " + customerId);
// Good - parameters only evaluated if level is enabled
log.debug("Processing order {} for customer {}", orderId, customerId);
// Good - for expensive computations, check level first
if (log.isDebugEnabled()) {
log.debug("Order details: {}", buildDetailedOrderSummary(order));
}
// Bad - loses stack trace
log.error("Payment failed: " + e.getMessage());
// Bad - exception in parameter placeholder
log.error("Payment failed: {}", e);
// Good - exception as last argument (not in placeholder)
log.error("Payment failed for order {}: {}", orderId, e.getMessage(), e);
// Good - simple case
try {
paymentGateway.charge(amount);
} catch (PaymentException e) {
log.error("Payment gateway error for order {}", orderId, e);
throw new ServiceException("Payment processing failed", e);
}
<!-- logback-spring.xml -->
<configuration>
<!-- Console output for development -->
<springProfile name="default,dev">
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE" />
</root>
</springProfile>
<!-- JSON output for production -->
<springProfile name="prod,staging">
<appender name="JSON_CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<includeMdcKeyName>traceId</includeMdcKeyName>
<includeMdcKeyName>spanId</includeMdcKeyName>
<includeMdcKeyName>userId</includeMdcKeyName>
<includeMdcKeyName>requestId</includeMdcKeyName>
<fieldNames>
<timestamp>@timestamp</timestamp>
<version>[ignore]</version>
</fieldNames>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="JSON_CONSOLE" />
</root>
</springProfile>
<!-- Reduce noise from libraries -->
<logger name="org.springframework" level="WARN" />
<logger name="org.hibernate.SQL" level="DEBUG" />
<logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE" />
</configuration>
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>7.4</version>
</dependency>
{
"@timestamp": "2024-01-15T10:30:45.123Z",
"level": "INFO",
"logger_name": "pl.piomin.services.order.OrderService",
"message": "Order created successfully",
"traceId": "abc123def456",
"userId": "user-42",
"requestId": "req-789",
"orderId": 12345,
"amount": 99.99
}
import net.logstash.logback.argument.StructuredArguments;
import static net.logstash.logback.argument.StructuredArguments.*;
@Service
public class PaymentService {
private static final Logger log = LoggerFactory.getLogger(PaymentService.class);
public PaymentResult processPayment(Long orderId, BigDecimal amount) {
log.info("Processing payment: {} {}",
keyValue("orderId", orderId),
keyValue("amount", amount));
PaymentResult result = gateway.charge(amount);
log.info("Payment completed: {} {} {}",
keyValue("orderId", orderId),
keyValue("transactionId", result.getTransactionId()),
keyValue("status", result.getStatus()));
return result;
}
}
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class MdcFilter implements Filter {
private static final String REQUEST_ID_HEADER = "X-Request-ID";
private static final String REQUEST_ID_KEY = "requestId";
private static final String CLIENT_IP_KEY = "clientIp";
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String requestId = httpRequest.getHeader(REQUEST_ID_HEADER);
if (requestId == null || requestId.isBlank()) {
requestId = UUID.randomUUID().toString();
}
MDC.put(REQUEST_ID_KEY, requestId);
MDC.put(CLIENT_IP_KEY, httpRequest.getRemoteAddr());
httpResponse.setHeader(REQUEST_ID_HEADER, requestId);
try {
chain.doFilter(request, response);
} finally {
MDC.clear();
}
}
}
@Component
public class UserMdcFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
try {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null && auth.isAuthenticated()) {
MDC.put("userId", auth.getName());
}
filterChain.doFilter(request, response);
} finally {
MDC.remove("userId");
}
}
}
@Configuration
@EnableAsync
public class AsyncMdcConfig {
@Bean(name = "mdcTaskExecutor")
public Executor mdcAwareExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(25);
executor.setTaskDecorator(new MdcTaskDecorator());
executor.initialize();
return executor;
}
}
public class MdcTaskDecorator implements TaskDecorator {
@Override
public Runnable decorate(Runnable runnable) {
Map<String, String> contextMap = MDC.getCopyOfContextMap();
return () -> {
try {
if (contextMap != null) {
MDC.setContextMap(contextMap);
}
runnable.run();
} finally {
MDC.clear();
}
};
}
}
@Component
public class RequestLoggingInterceptor implements HandlerInterceptor {
private static final Logger log = LoggerFactory.getLogger(RequestLoggingInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) {
request.setAttribute("startTime", System.currentTimeMillis());
log.info("Incoming request: {} {} from {}",
request.getMethod(),
request.getRequestURI(),
request.getRemoteAddr());
return true;
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
long startTime = (long) request.getAttribute("startTime");
long duration = System.currentTimeMillis() - startTime;
log.info("Request completed: {} {} status={} duration={}ms",
request.getMethod(),
request.getRequestURI(),
response.getStatus(),
duration);
}
}
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
private final RequestLoggingInterceptor requestLoggingInterceptor;
public WebMvcConfig(RequestLoggingInterceptor requestLoggingInterceptor) {
this.requestLoggingInterceptor = requestLoggingInterceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(requestLoggingInterceptor)
.addPathPatterns("/api/**");
}
}
// NEVER log sensitive data
log.info("User login: {}", password); // NEVER
log.info("Payment card: {}", cardNumber); // NEVER
log.info("Auth token: {}", bearerToken); // NEVER
log.info("SSN: {}", socialSecurityNumber); // NEVER
// DO log identifiers and outcomes
log.info("User login successful: userId={}", userId);
log.info("Payment processed: last4={}, status={}", last4Digits, status);
private static final using LoggerFactory.getLogger(ClassName.class)