Our next-gen architecture is built to help you make sense of your ever-growing data.

Watch a 4-min demo video!

Java Logging Guide: How To Do It Right

  • Ariel Assaraf
  • February 24, 2016
Share article
Java logging how to

Log monitoring is something you want to plan and standardize before you start writing your code, especially if it involves different teams or separate locations.

During the last couple of years, we witnessed the strong connection between quality and standardized logging and the ability to track and resolve production problems.

In this post, we will focus on a few lessons we’ve learned about Java logging and how to do it right.

What is Java logging? 

Java logging, commonly known as logging, serves a crucial role in undetstanding system performance and identifying the root causes of failures. It aids the analysis of program executions by keeping a record of events, which makes it invaluable for tasks such as auditing and debugging.

However, it’s important to note that logging does not occur automatically. Developers must proactively implement logging rules to ensure they can effectively manage the logging process.

9 tips for effective Java logging Setting up your logging correctly is crucial for the future and can help you get the most from your logging. Here are some tips and Java logging best practices to get you started:

9 tips for effective Java logging

Setting up your logging correctly is crucial for the future and can help you get the most from your logging. Here are some tips and Java logging best practices to get you started:

1) Set your log severity right

Many times, too often actually,  we see a complete log file written with the same log severity. This makes your logs harder to understand and hides the important logs you want to notice.

To make it easier for you to decide what severity to set for each log, here are some simple log severity guidelines:

  • Debug/Verbose: Logs that are mainly used by the developers and contain data such as response times, health checks, queues status etc. An example for a debug log would be “Number of messages in the user creation queue = 3482”
  • Info: Business processes and transactions, these logs should be readable for QA, Support and even advanced users to understand the system’s behavior. An example of an info log will contain data on a product purchase on your e-commerce platform, a user creation on your social media or a successful batch process on your data analytics solution.
  • Warning: These logs mean something unusual happened or something isn’t right, but it does not necessarily mean that anything failed or the user will notice a problem. An example of a warning would be “Received illegal character for username – “Jame$” , ignoring char”  
  • Error: A problem that must be investigated; use the Error severity to log Disconnections, failed tasks or failures that reflect to your users. If you see an error in your log that does not require immediate investigation, you should probably lower its severity.
  • Critical/Fatal: Something terrible happened, stop everything and handle it,. This could beCrashes, Serious latency or performance issues, security problems. All these must be logged with the log severity Critical.

2) Remember you will not be the only one reading these logs

When writing your application logs, remember that besides you, other people will read these logs. Whether it’s programmers, QA or support consuming the logs you wrote, they better be clear and informative. 

On the other hand, logs that are long and detailed can be hard to parse automatically (grep, awk, etc.), so either you find a way to write a clear log that can also be parsed easily, or you can simply print two logs, one for humans and one for computers:

E.g – Print these two logs together, the first log for humans and the second for computers:

  • “transaction was completed successfully” + transactionID “total time for transaction =” + TimeElapsed
  • “success” + transactionID  “time” + TimeElapsed

3) Track your communication with other systems

Integration issues can be the hardest to debug; our suggestion is that you log every event that comes in/out of your system to an external system, whether it is HTTP headers, authentications, keep alive, etc.

In complex and high scale systems this can be a performance overhead, but in case you experience performance issues, you can always switch off the logging for that particular log level (usually Debug or Trace) and use it when something goes wrong with your production.

4) Add metadata to your logs

Often, programmers write great log text and severity but forget to add the log metadata such as Category, Class, method or threadID. 

Adding metadata to your logs can significantly enhance your capability of pinpointing production problems as you can search and identify problematic categories, classes or methods or follow a thread to understand the root cause of an error you see. The more metadata you add, the better your log is.

5) Use a logging API

Logging APIs make it much easier to add log destinations and integrate with logging tools seamlessly and without any code modifications. In addition, they make your logs more clear by standardizing them and enriching them with metadata fields such as thread ID.

The two most common logging API’s for Java are Log4J and Logback (slf4j).

Note that one of the greatest benefits of Log4J and Logback is that they allow you to send logs from any Java-based Apache project easily! (Kafka, Hazelcast, etc.)

A Logback log will be written in the following structure:

log.warning(“Retried {} times before succeeding to create user: ‘{}’”, retries, username);

The same log in Log4J would be:

log.warning(“retried” + retries + “times before succeeding to create user” + username);

6) Make sure you know what you are logging

When writing logs, especially when calling functions and variables within that log, make sure you understand what will be the outcome of that print. Bad logs can be:

  • inconsistent – values that arrive NULL or with different data types
  • Too long – Printing a list of URLs that is impossible to read or printing HEX values, for instance
  • Null – printing logs that rely on a variable that may or may not have content, for instance: log.error(monitor.get_ERR_reason)

7) Don’t write huge logs

It’s great to write detailed and descriptive logs, but many times we see single log entries with an enormous amount of characters (20K+) as the logs are used to store data that is completely unrelated to logs and should be managed separately. This can cause serious performance issues when writing logs to your disk and create bandwidth issues when using hosted logging solutions.  

Remember what the is the main purpose of your logs and stick to it. You want clear logs that tell the story of your software for you to understand its behavior and be able to debug it.

8) Log exceptions correctly

We all probably agree that reporting exceptions is a crucial part of the logging process. On that basis, some tend to both report the exception and then wrap it with their own custom exception and throw it again. This will probably cause the stack trace to be printed twice, a fact that will most likely cause confusion. We suggest never to report and re-throw, decide what works for you best and stick to it. 

We generally recommend throwing the exception with your own custom exception and catching them all in a centralized handler which will log them and handle any other activities that are needed.

Here are some examples of Java exception logging:

BAD:

try {

   Integer x = null;

   ++x;

} catch (Exception e) {

log.error(“IO exception”, e);

throw new MyCustomException(e);

}

BETTER:

try {

   Integer x = null;

   ++x;

} catch (Exception e) {

log.error(“IO exception”, e);

}

BEST:

try {

   Integer x = null;

   ++x;

} catch (Exception e) {

throw new MyCustomException(e);

}

As a rule of thumb, let the logging framework you are using help you log exceptions and don’t do it yourself. Remember, the first argument is always the text message; write something about the nature of the problem. 

Don’t include the exception message, as it will be printed automatically after the log statement preceding the stack trace. But to do so, you must pass the exception itself as the second argument; other logging standards will most likely cause the message to be printed wrongly.

BAD:

log.error(e);

log.error(e, e);  

log.error(“” + e);

log.error(e.toString());  

log.error(e.getMessage());

log.error(null, e);

log.error(“”, e);

log.error(“{}”, e);

log.error(“{}”, e.getMessage());

log.error(“Error reading configuration file: ” + e);

log.error(“Error reading configuration file: ” + e.getMessage());

GOOD:

log.error(“Error reading configuration file”, e);

9) Use an ID to track your events

This method will allow you to easily filter or search for a specific event that you want to track. The idea is that whoever is responsible for creating an event (e.g. client, worker etc) generates a unique ID that is passed through all functions and service calls that are used to process that event. Then once an exception or error occurs, it is simple to take the event ID from that error and query for its history throughout the different functions, services and components.

Closing thoughts

In the Java logging community, there are many logging methods that exist, which presents developers with a plethora of options to choose based on their specific needs and requirements. 

The selection of an appropriate logging approach can massively impact the effectiveness and efficiency of the logging process. The choice ultimately depends on the complexity of the project, the desired level of customisation, and the need for compatiability with existing systems. 

By following our tips above, you can create a well-executed logging strategy that can factilitate system monitoring and debugging, whilst enhancing the maintainability and staibility of Java applications.

(This blog post was updated August 2023)

Observability and Security
that Scale with You.