Skip to content
CMO & CTO
CMO & CTO

Closing the Bridge Between Marketing and Technology, By Luis Fernandez

  • Digital Experience
    • Experience Strategy
    • Experience-Driven Commerce
    • Multi-Channel Experience
    • Personalization & Targeting
    • SEO & Performance
    • User Journey & Behavior
  • Marketing Technologies
    • Analytics & Measurement
    • Content Management Systems
    • Customer Data Platforms
    • Digital Asset Management
    • Marketing Automation
    • MarTech Stack & Strategy
    • Technology Buying & ROI
  • Software Engineering
    • Software Engineering
    • Software Architecture
    • General Software
    • Development Practices
    • Productivity & Workflow
    • Code
    • Engineering Management
    • Business of Software
    • Code
    • Digital Transformation
    • Systems Thinking
    • Technical Implementation
  • About
CMO & CTO

Closing the Bridge Between Marketing and Technology, By Luis Fernandez

Logging Best Practices in Java

Posted on May 17, 2016 By Luis Fernandez

Logging is that quiet teammate who remembers everything you forget in production.

Why logging still wins

If you build Java services for the web, logs are your second debugger. You can not attach a profiler to every box, and sometimes the bug shows up only under load or at three in the morning. Good logs tell a story. Bad logs mumble. With Spring Boot getting love and Docker on every laptop, our apps move around a lot, so the story needs to travel with them. Java 8 is the default in most new projects, and with I O around the corner, everyone is tuning builds and talking about microservices again. Logs keep that noise grounded.

What to log and what to skip

What should you record. Start with the path through the code. Events matter more than chatter. Record who did what and when. Think request start, key decision points, calls to outside systems, and the result. Keep it short, consistent, and searchable. Skip private data, card numbers, tokens, and passwords. If you must log an identifier, mask it. You will thank yourself when an audit lands on your desk. Keep payloads out of INFO unless they are tiny and safe. Use DEBUG when you need the details, then turn it off cleanly.

Write clean messages with SLF4J

Use SLF4J with Logback or Log4j 2 and write parameterized messages. String concat in hot loops burns CPU and fills your heap with garbage. Placeholders are cheap and clear. The bonus is you can swap the backend without touching your code. Here is a tiny sample that shows the happy path and the failure with a stack trace.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class OrderService {

  private static final Logger log = LoggerFactory.getLogger(OrderService.class);

  public Order create(String customerId, String sku, int qty) {
    long t0 = System.currentTimeMillis();
    log.info("create order start customerId={} sku={} qty={}", customerId, sku, qty);
    try {
      // business logic
      Order order = payment.charge(customerId, sku, qty);
      log.info("create order ok orderId={} elapsedMs={}", order.getId(), System.currentTimeMillis() - t0);
      return order;
    } catch (PaymentException e) {
      log.error("create order failed customerId={} reason={}", customerId, e.getMessage(), e);
      throw e;
    }
  }
}

Do not build messages with StringBuilder in your code. Let the logger do the work. Keep verbs up front and keep the same shape across services. That helps grep and dashboard tools. Think action subject key=value pairs, not a novel.

Pick the right levels

ERROR means users felt pain or data could be wrong. WARN signals a smell that could grow. INFO marks milestones in the run of your app. DEBUG is you talking to you. TRACE is the microscope. Do not print stack traces at INFO. Do not hide a real failure under WARN. When in doubt, pick the quieter level in code and turn it up with config during a live issue. Let config be the volume knob.

MDC for request tracing

Give every request a tag. MDC is your friend here. Stick a correlation id in the context when a request enters your app, and print it with the thread name and the logger name. Now you can follow one user across nodes and services. This is gold when you route traffic through a queue or a gateway.

import org.slf4j.MDC;

// at request entry
MDC.put("requestId", reqId);
MDC.put("userId", userId);

// later in your flow
log.info("fetch profile");

// at exit
MDC.clear();
<configuration>
  <property name="PATTERN" value="%d{HH:mm:ss.SSS} %level %logger{36} [%thread] req=%X{requestId} user=%X{userId} %msg%n"/>
  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>${PATTERN}</pattern>
    </encoder>
  </appender>
  <root level="info">
    <appender-ref ref="CONSOLE"/>
  </root>
</configuration>

Config that does not get in your way

On the config side, sane defaults help new teammates. In Spring Boot you can keep a logback.xml and ship a console appender that prints one line per event with your MDC keys. For production, roll files by date and size, keep a short retention, and compress old logs. Async appenders keep your threads from waiting on disk. In Log4j 2 the async option is fast and easy to enable.

<Configuration status="WARN">
  <Appenders>
    <Async name="Async">
      <File name="File" fileName="app.log">
        <PatternLayout pattern="%d %p %c [%t] req=%X{requestId} %m%n"/>
      </File>
    </Async>
  </Appenders>
  <Loggers>
    <Root level="info">
      <AppenderRef ref="Async"/>
    </Root>
    <Logger name="org.springframework" level="warn"/>
  </Loggers>
</Configuration>

Searchable and structured

Search is where logs earn their keep. People keep saying ELK stack and for good reason. If you plan to ship to Logstash then structured logging is a win. You can print JSON from Logback or Log4j 2 with a layout and skip regex games later. Your index likes keys like service, env, requestId, userId, and outcome. A little planning now pays off when you build a simple dashboard in Kibana.

Kill noise and protect users

Noise is the enemy. Cut chatty logs in libraries by raising their level in your config. Sample repeating errors so you keep one message and a counter for the last minute. Throttle that famous can not connect to host line. Trim stack traces to the frames you own. And always log once near the decision, not in a catch and then again at the caller. Double prints slow you down and confuse the feed.

A quick word on privacy. Logs live longer than memory. If you would not paste it in a chat room, do not print it. Replace values with hashes if you need to match later. For user supplied text, sanitize and cap length. If you are in the EU, talk to your counsel about retention. The safe bet is to keep less and know exactly where it goes.

Last tip. Name your loggers by class and keep it stable. Do not build the logger name with data. Do not use System out for anything other than a simple local check. When a bug hits at night, you want grep friendly files and a clear voice from your code. That is what good logging gives you.

Logs tell the truth when everyone else guesses.

Make them readable and you will sleep better.

Code Development Practices Software Engineering coding-practicesjavaspring

Post navigation

Previous post
Next post
  • Digital Experience (94)
    • Experience Strategy (19)
    • Experience-Driven Commerce (5)
    • Multi-Channel Experience (9)
    • Personalization & Targeting (21)
    • SEO & Performance (10)
  • Marketing Technologies (92)
    • Analytics & Measurement (14)
    • Content Management Systems (45)
    • Customer Data Platforms (4)
    • Digital Asset Management (8)
    • Marketing Automation (6)
    • MarTech Stack & Strategy (10)
    • Technology Buying & ROI (3)
  • Software Engineering (310)
    • Business of Software (20)
    • Code (30)
    • Development Practices (52)
    • Digital Transformation (21)
    • Engineering Management (25)
    • General Software (82)
    • Productivity & Workflow (30)
    • Software Architecture (85)
    • Technical Implementation (23)
  • 2025 (12)
  • 2024 (8)
  • 2023 (18)
  • 2022 (13)
  • 2021 (3)
  • 2020 (8)
  • 2019 (8)
  • 2018 (23)
  • 2017 (17)
  • 2016 (40)
  • 2015 (37)
  • 2014 (25)
  • 2013 (28)
  • 2012 (24)
  • 2011 (30)
  • 2010 (42)
  • 2009 (25)
  • 2008 (13)
  • 2007 (33)
  • 2006 (26)

Ab Testing Adobe Adobe Analytics Adobe Target AEM agile-methodologies Analytics architecture-patterns CDP CMS coding-practices content-marketing Content Supply Chain Conversion Optimization Core Web Vitals customer-education Customer Data Platform Customer Experience Customer Journey DAM Data Layer Data Unification documentation DXP Individualization java Martech metrics mobile-development Mobile First Multichannel Omnichannel Personalization product-strategy project-management Responsive Design Search Engine Optimization Segmentation seo spring Targeting Tracking user-experience User Journey web-development

©2025 CMO & CTO | WordPress Theme by SuperbThemes