Best Practices for Using SLF4J Logging in Java

This guide explains why to use the SLF4J façade with Logback, when to log, proper parameterized message formats, level‑specific conventions for ERROR, WARN, INFO, DEBUG, and TRACE, and provides concrete code examples for clean, performant logging in Java applications.

Java Captain
Java Captain
Java Captain
Best Practices for Using SLF4J Logging in Java

Using SLF4J

Using a façade logging framework helps maintain consistency across classes.

The implementation is standardized on the Logback framework.

Correct Way to Log

When to Log

When debugging a problem, add logs so the issue can be diagnosed from log files.

At the start of each conditional branch (if/else, switch) to identify which path is taken.

Before committing code, ensure logs can show the entire functional flow.

Basic Format

Always use parameterized logging:

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

For DEBUG level, check if debug is enabled before logging:

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

Avoid string concatenation in log statements because it creates many temporary String objects and hurts performance.

Using [] for Parameter Isolation

When there are parameters, write the log as:

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

This format improves readability and aids troubleshooting.

Usage of Different Log Levels

ERROR

Basic Concept

Errors that affect normal program execution or the current request:

Failure to open configuration files.

Third‑party integration errors, including error codes returned by external services.

All exceptions that impact functionality, such as SQLException, RuntimeException, and generic Exception (excluding business exceptions).

Do not log situations that are not truly errors, e.g., Azure image upload timeout when Azure does not respond.

If a Throwable is present, log the full stack trace:

log.error("Error retrieving user [{}] information", userName, e);

Note

If the exception is re‑thrown, avoid logging it here; let the final handler record it.

WARN

Basic Concept

Situations that do not affect program execution but indicate potential problems:

Errors that are handled by fault‑tolerance mechanisms.

Missing configuration files that the system can auto‑create.

Approaching resource limits, e.g., cache pool reaching a warning threshold.

Business exceptions that should be recorded when an API throws them.

INFO

Basic Concept

System operational information:

State changes in service methods (system or business).

Step‑by‑step progress of major logic.

External interface details:

Client request parameters (REST/WS).

Parameters and results when calling third‑party services.

Note

Not every simple service needs entry/exit logs; only complex services or jobs (which need start and end logs).

Complex business flows (e.g., order creation) should have detailed logs.

All API endpoints should log input parameters.

When the service follows an SOA architecture, treat it as an external provider and log inputs.

Calls to third‑party services must log both request and response for traceability.

DEBUG

Basic Concept

Log all relevant information needed for debugging, with meaningful context and parameters.

Disable DEBUG logs in production.

If DEBUG must be enabled in production, control it with a switch.

Note

Example of sub‑optimal logging:

//1. Get user basic salary
//2. Get user leave information
//3. Calculate user payable salary

Optimized version with parameterized logs:

logger.debug("Start fetching basic salary for employee [{}] year [{}]", employee, year);
logger.debug("Fetched basic salary for employee [{}] year [{}]: [{}]", employee, year, basicSalary);
logger.debug("Start fetching leave info for employee [{}] year [{}] month [{}]", employee, year, month);
logger.debug("Employee [{}][{}] year [{}] month leave: annual/[{}] sick/[{}] unpaid/[{}]", employee, year, month, annualLeaveDays, sickLeaveDays, noPayLeaveDays);
logger.debug("Start calculating salary for employee [{}] year [{}] month [{}]", employee, year, month);
logger.debug("Calculated salary for employee [{}] year [{}] month [{}]: [{}]", employee, year, month, actualSalary);

TRACE

Basic Concept

Very detailed system execution information; generally avoid using TRACE in business code unless there is a special need—use DEBUG instead.

Standard Example

@Override
@Transactional
public void createUserAndBindMobile(@NotBlank String mobile, @NotNull User user) throws CreateConflictException {
    boolean debug = log.isDebugEnabled();
    if (debug) {
        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 (debug) {
            log.debug("User creation succeeded. insertedUser=[{}]", LogObjects.toString(user));
        }
        UserMobileRelationship relationship = new UserMobileRelationship();
        relationship.setMobile(mobile);
        relationship.setOpenId(user.getOpenId());
        relationship.setCreateTime(new Date());
        relationship.setUpdateTime(new Date());
        userMobileRelationshipRepository.insertOnDuplicateKey(relationship);
        if (debug) {
            log.debug("Mobile binding succeeded. relationship=[{}]", LogObjects.toString(relationship));
        }
        log.info("Created user and bound mobile. userId=[{}],openId=[{}],mobile=[{}]", user.getId(), user.getOpenId(), mobile); // mask mobile if needed
    } catch (DuplicateKeyException e) {
        log.info("Failed to create user and bind mobile; 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.

JavaBackend Developmentlogginglogbackslf4j
Java Captain
Written by

Java Captain

Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java 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.