| name | logging-patterns |
| description | Use when implementing structured logging, configuring log levels, adding observability, or establishing logging standards in Spring Boot applications. |
Logging Patterns Skill
You are an expert in structured logging and observability for Spring Boot applications. You implement logging that is useful for debugging, monitoring, and auditing.
SLF4J Logger Setup
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Service
public class PaymentService {
private static final Logger log = LoggerFactory.getLogger(PaymentService.class);
}
Log Levels
@Service
public class OrderService {
private static final Logger log = LoggerFactory.getLogger(OrderService.class);
@Transactional
public Order processOrder(Long orderId) {
log.trace("Entering processOrder with orderId={}", orderId);
log.debug("Looking up order: {}", orderId);
var order = orderRepository.findById(orderId)
.orElseThrow(() -> {
log.warn("Order not found: {}", orderId);
return new ResourceNotFoundException("Order", orderId);
});
log.info("Processing order: id={}, status={}, total={}",
order.getId(), order.getStatus(), order.getTotal());
try {
paymentGateway.charge(order);
} catch (PaymentGatewayException e) {
log.error("Payment failed for order: id={}, amount={}",
order.getId(), order.getTotal(), e);
throw new PaymentProcessingException("Payment failed", e);
}
order.setStatus(OrderStatus.CONFIRMED);
var saved = orderRepository.save(order);
log.info("Order processed successfully: id={}, newStatus={}",
saved.getId(), saved.getStatus());
return saved;
}
}
Parameterized Logging
log.debug("Processing item: id={}, price={}", item.getId(), item.getPrice());
log.debug("Processing item: id=" + item.getId() + ", price=" + item.getPrice());
if (log.isDebugEnabled()) {
log.debug("Full order details: {}", order.toDetailedString());
}
log.info("Order summary: id={}, customer={}, items={}, total={}, status={}",
order.getId(), order.getCustomerId(), order.getItems().size(),
order.getTotal(), order.getStatus());
Structured Logging with MDC
import org.slf4j.MDC;
@Component
public class RequestContextFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
try {
MDC.put("requestId", UUID.randomUUID().toString());
MDC.put("userId", extractUserId(request));
MDC.put("clientIp", request.getRemoteAddr());
MDC.put("method", request.getMethod());
MDC.put("path", request.getRequestURI());
filterChain.doFilter(request, response);
} finally {
MDC.clear();
}
}
}
Request/Response Logging
@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: {} {}", request.getMethod(), request.getRequestURI());
return true;
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
var startTime = (Long) request.getAttribute("startTime");
var duration = System.currentTimeMillis() - startTime;
log.info("Request completed: {} {} status={} duration={}ms",
request.getMethod(), request.getRequestURI(),
response.getStatus(), duration);
}
}
Service Method Logging Pattern
@Aspect
@Component
public class ServiceLoggingAspect {
private static final Logger log = LoggerFactory.getLogger(ServiceLoggingAspect.class);
@Around("@within(org.springframework.stereotype.Service)")
public Object logServiceMethod(ProceedingJoinPoint joinPoint) throws Throwable {
var methodName = joinPoint.getSignature().toShortString();
var args = joinPoint.getArgs();
log.debug("Calling {} with args: {}", methodName, args);
var startTime = System.nanoTime();
try {
var result = joinPoint.proceed();
var duration = (System.nanoTime() - startTime) / 1_000_000;
log.debug("{} completed in {}ms", methodName, duration);
return result;
} catch (Exception e) {
var duration = (System.nanoTime() - startTime) / 1_000_000;
log.error("{} failed after {}ms: {}", methodName, duration, e.getMessage());
throw e;
}
}
}
What NOT to Log
log.info("User authenticated: password={}", password);
log.info("Payment processed: cardNumber={}", cardNumber);
log.info("Token issued: jwt={}", jwtToken);
log.info("User authenticated: userId={}", userId);
log.info("Payment processed: last4={}, amount={}", last4Digits, amount);
log.info("Token issued for user: {}", userId);
for (var item : items) {
log.info("Processing item: {}", item.getId());
}
log.info("Processing {} items", items.size());
for (var item : items) {
log.debug("Processing item: {}", item.getId());
}
Application Properties
logging:
level:
root: INFO
pl.piomin.services: DEBUG
org.springframework.web: INFO
org.hibernate.SQL: DEBUG
org.hibernate.type.descriptor.sql.BasicBinder: TRACE
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{requestId}] %-5level %logger{36} - %msg%n"