10 Mistakes To Avoid For Effective Logging In Your Service
Logging in services is an essential part of software development lifecycle. It is expensive and requires argument processing, disk writes, and storage. Hence, it is crucial to log effectively to allow easier debugging for developers and save time and resources.
- Avoid log-and-throw anti-pattern. When catching an exception, either log an error, or throw an exception with an error message, but never both as otherwise same error will get logged multiple times. However, this would not apply at the app boundary (i.e. entry/exit-point of service) which might throw exception to the client by contract, but also serve as catch-all logger for any exceptions in the service.
- Avoid using DEBUG log level for production logs. They are often used to debug specific instances i.e. in development mode.
- Avoid using ERROR log level for expected errors that service need not alarm for. For example, 4xx errors where client sent a bad request/invalid input. In such cases, instead of logging exceptions, metric can be added to register failure.
- Avoid writing directly to the standard output stream (e.printStackTrace(), System.out.println etc.). It bypasses the logging system and any buffering/filtering that it does for performance optimization purposes. Replace them with appropriate calls to the logging system.
- Avoid spamming logs. Audit the disk space used by log files regularly and alarm in case logging causes disk space shortage.
- Avoid emitting log without any arguments. Logging is expensive, make every log event count by providing as much relevant context as possible to make debugging easy and reduce troubleshooting time.
- Avoid excessive error logging when system is down. Error logs tend to be large and errors happen at the same time so make sure it doesn’t worsen problem when your service is under duress by emitting more logs. Use BurstFilters to regulate logging traffic.
- Avoid using {} for the exception object in log. This leads to losing the stack trace as {} will print toString() of the exception which only has message. Also, keep exception as separate argument to print out the entire stack trace.
- Avoid logging at the wrong level as it can pollute the logs and make it harder to diagnose issues. This is the third time you’re reading it, but logging is expensive. Logging affects performance so be judicious about what to log and which level to enable.
- Avoid string concatenation or StringBuilder to concatenate arguments in log. Many toString() implementations are especially expensive due to object serialization. Use {} instead of %s when existing string value is to be applied at a placeholder. Remember: Even if the logging level is disabled all arguments passed into the logger get evaluated.
❌ log.warn(String.format(“Event %s occurred for request %s”, event, requestId));
✅ log.warn("Event {} occurred for request {}", event, requestId);
It takes 2 seconds longer to type a much more useful log message. Don’t be lazy! It’s extremely frustrating when you are paged and realize that someone forgot to log critical information that would help understand root cause.
Thank you for reading! If you found this helpful, here are some next steps you can take: