Logging
Formatted Message
Object Array
Exception Handling
Programming Tips

How to log formatted message, object array, exception?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

When a log statement needs both context and an exception, many developers end up with unreadable output or a missing stack trace. In Java logging frameworks such as SLF4J and Logback, the clean solution is to use placeholders for values, convert arrays explicitly when needed, and pass the exception as the final argument.

Logging a Formatted Message

Formatted logging lets you build readable messages without manual string concatenation. With SLF4J, the message template uses placeholder tokens and the logger fills them in only if the log level is enabled.

java
1import org.slf4j.Logger;
2import org.slf4j.LoggerFactory;
3
4public class UserService {
5    private static final Logger log = LoggerFactory.getLogger(UserService.class);
6
7    public void loadUser(String userId, String region) {
8        log.info("Loading user {} from region {}", userId, region);
9    }
10}

This style is preferable to:

java
log.info("Loading user " + userId + " from region " + region);

The placeholder version is clearer and avoids building the full string when the logger will not emit it.

Logging Arrays and Object Collections

Arrays need special handling in Java because calling toString() on an array usually prints a type-like value instead of the contents. For example, an Object[] may show something similar to [Ljava.lang.Object;..., which is rarely useful during debugging.

Use Arrays.toString for one-dimensional arrays:

java
1import java.util.Arrays;
2import org.slf4j.Logger;
3import org.slf4j.LoggerFactory;
4
5public class JobRunner {
6    private static final Logger log = LoggerFactory.getLogger(JobRunner.class);
7
8    public void runJob() {
9        Object[] values = {"alpha", 42, true};
10        log.debug("Job arguments: {}", Arrays.toString(values));
11    }
12}

For nested arrays, use Arrays.deepToString:

java
1Object[][] matrix = {
2    {"a", "b"},
3    {1, 2}
4};
5
6log.debug("Nested values: {}", Arrays.deepToString(matrix));

If the data is already a List, most implementations give a readable toString, so a direct placeholder is often enough.

Logging the Exception Correctly

The stack trace should not be flattened into the formatted string. Instead, pass the exception object as the final parameter to the logger call. That allows the framework to render the full trace in its native format.

java
1import java.util.Arrays;
2import org.slf4j.Logger;
3import org.slf4j.LoggerFactory;
4
5public class ImportService {
6    private static final Logger log = LoggerFactory.getLogger(ImportService.class);
7
8    public void importItems(Object[] items) {
9        try {
10            throw new IllegalStateException("Database is unavailable");
11        } catch (Exception ex) {
12            log.error("Import failed for items {}", Arrays.toString(items), ex);
13        }
14    }
15}

That one line does three things:

  1. It prints a formatted message.
  2. It includes a readable representation of the object array.
  3. It attaches the exception so the stack trace is preserved.

This is usually the answer behind the question “how do I log a formatted message, object array, and exception together?”

Why the Final Argument Matters

Frameworks in the SLF4J family inspect the last argument specially. If it is a Throwable, they render it as an exception rather than as another placeholder value.

That means this works well:

java
log.error("Request {} failed for payload {}", requestId, Arrays.toString(payload), ex);

But this is weaker:

java
log.error("Request {} failed: {}", requestId, ex.getMessage());

The second version loses the stack trace unless you log the exception separately. A short message may help humans, but the stack trace is what usually makes the failure diagnosable.

A Complete Example

java
1import java.util.Arrays;
2import org.slf4j.Logger;
3import org.slf4j.LoggerFactory;
4
5public class PaymentProcessor {
6    private static final Logger log = LoggerFactory.getLogger(PaymentProcessor.class);
7
8    public void process(Object[] paymentFields) {
9        try {
10            validate(paymentFields);
11            log.info("Processing payment with fields {}", Arrays.toString(paymentFields));
12        } catch (Exception ex) {
13            log.error("Payment processing failed for fields {}", Arrays.toString(paymentFields), ex);
14        }
15    }
16
17    private void validate(Object[] paymentFields) {
18        if (paymentFields.length < 3) {
19            throw new IllegalArgumentException("Expected at least 3 fields");
20        }
21    }
22}

This pattern scales well because the log line remains readable even as the application grows.

Common Pitfalls

One mistake is concatenating everything into one giant string. That removes structured placeholders and makes logs harder to search consistently. Another is logging an array directly without Arrays.toString or Arrays.deepToString, which produces output that is technically correct but operationally useless.

A more serious mistake is swallowing the exception details by logging only ex.getMessage(). Messages can be vague, while the stack trace shows exactly where the failure happened. In most cases, log the exception object itself as the final argument.

Summary

  • Use placeholder-based logging instead of manual string concatenation.
  • Convert arrays with Arrays.toString or Arrays.deepToString before logging.
  • Pass the exception as the final logger argument to preserve the stack trace.
  • Keep one log line responsible for the message, context data, and failure details.
  • Prefer readable, searchable logs over clever but opaque formatting.

Course illustration
Course illustration

All Rights Reserved.