Master SLF4J Logging: Best Practices and Real‑World Java Examples

This article explains why SLF4J with Logback is the preferred logging façade for Java back‑ends, when to log, how to format messages using parameterized placeholders, and the correct usage of each log level with clear do‑and‑don’t code examples.

Java Backend Technology
Java Backend Technology
Java Backend Technology
Master SLF4J Logging: Best Practices and Real‑World Java Examples

Using SLF4J for Effective Logging

Adopting a façade‑based logging framework such as SLF4J standardises log handling across a Java backend and simplifies future changes of the underlying implementation.

Why Use SLF4J?

Facade pattern unifies logging APIs, making maintenance easier.

Common implementation choice: Logback.

When to Write Logs

During troubleshooting, logs help pinpoint problems when debugging is insufficient.

At the entry of each conditional branch (if/else, switch) to confirm the executed path.

Before committing code, ensure the entire workflow can be reconstructed from logs.

Basic Log Format

Always use parameterised messages to avoid unnecessary string concatenation and object creation.

logger.debug("Processing trade with id:[{}] and symbol:[{}]", id, symbol);

For DEBUG level, guard the call with logger.isDebugEnabled() when the message construction is expensive.

if (logger.isDebugEnabled()) {
    logger.debug("Processing trade with id: " + id + " symbol: " + symbol);
}

Avoid concatenating strings directly as it creates many temporary String objects.

Using Parameter Placeholders []

logger.debug("Processing trade with id:[{}] and symbol:[{}]", id, symbol);

This improves readability and aids troubleshooting.

Log Level Guidelines

ERROR

Log situations that prevent normal operation or indicate serious failures, such as configuration file loading errors or third‑party service failures (including SQLExceptions and unchecked exceptions).

log.error("Failed to get user [{}] info", userName, e);

Do not log and re‑throw the same exception as an error; let the outer handler manage it.

WARN

Log recoverable issues that do not affect the current request, e.g., missing optional config files that can be auto‑created, or resource usage approaching warning thresholds.

INFO

Record normal operational events such as service method entry/exit, key business state changes, request parameters, and third‑party call inputs/outputs.

log.info("Creating user and binding mobile. userId=[{}], openId=[{}], mobile=[{}]", user.getId(), user.getOpenId(), mobile);

Do not log trivial service calls; only complex or business‑critical flows need INFO points.

DEBUG

Include detailed information useful for developers, but disable in production. Use meaningful messages with relevant parameters.

logger.debug("Start fetching employee [{}] basic salary for year [{}]", employee, year);
logger.debug("Employee [{}] basic salary for year [{}] is [{}]", employee, year, basicSalary);

TRACE

Very fine‑grained messages; generally avoid in business code unless a special need exists, preferring DEBUG instead.

Sample Implementation

@Override
@Transactional
public void createUserAndBindMobile(@NotBlank String mobile, @NotNull User user) throws CreateConflictException {
    if (log.isDebugEnabled()) {
        log.debug("Start creating user and binding mobile. args[mobile=[{}], user=[{}]]", mobile, LogObjects.toString(user));
    }
    try {
        user.setCreateTime(new Date());
        user.setUpdateTime(new Date());
        userRepository.insertSelective(user);
        if (log.isDebugEnabled()) {
            log.debug("User created successfully. insertedUser=[{}]", LogObjects.toString(user));
        }
        UserMobileRelationship rel = new UserMobileRelationship();
        rel.setMobile(mobile);
        rel.setOpenId(user.getOpenId());
        rel.setCreateTime(new Date());
        rel.setUpdateTime(new Date());
        userMobileRelationshipRepository.insertOnDuplicateKey(rel);
        if (log.isDebugEnabled()) {
            log.debug("Mobile bound successfully. relationship=[{}]", LogObjects.toString(rel));
        }
        log.info("Created user and bound mobile. userId=[{}], openId=[{}], mobile=[{}]", user.getId(), user.getOpenId(), mobile);
    } catch (DuplicateKeyException e) {
        log.info("Failed to create user; duplicate exists. openId=[{}], mobile=[{}]", user.getOpenId(), mobile);
        throw new CreateConflictException("User creation conflict, openid=[%s]", user.getOpenId());
    }
}
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

DebuggingJavabest practiceslogginglogbackslf4j
Java Backend Technology
Written by

Java Backend Technology

Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.