In previous posts, I wrote about how to create a spring boot REST API Part I and how to add swagger documentation for REST API Part II. In this post, we will add error handling and logging to our REST API. Error handling and Logging are two different ideas, so I will divide this post in two sections.
1. Logging
In most production applications, logging is critical and it is used for multiple purposes. Few of those uses are debugging the production issues or auditing for the application. Over the years, different logging libraries have evolved to use in java based applications. slf4j is the most popular framework as it provides a simple abstraction layer to any kind of logging framework.
In our tutorial for this application, we will be using log4j2 which is the most recent and advance logging library out there. It provides lot of useful features for performance, support for multiple APIs, advance filtering, automatic reloading of configurations etc. We will not cover any of these in this article, if you are interested to read about log4j2 libraries, read here.
Add log4j2 library in application –
To use log4j2, we will add the maven dependency to our project’s pom file. This should look like below
<dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> </dependency>
Add log4j2 configuration file
To enable logging, we will have to add a configuration file in our application. This configuration file can be XML, JSON or YAML file. We will be using a XML file log4j2.xml which will look like below
<?xml version="1.0" encoding="UTF-8"?> <Configuration status="INFO"> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" /> </Console> <File name="BenefitsFile" fileName="benefits.log" append="true"> <PatternLayout pattern="%d{yyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> </File> </Appenders> <Loggers> <Root level="debug"> <AppenderRef ref="Console" /> <AppenderRef ref="BenefitsFile"/> </Root> </Loggers> </Configuration>
So we are using Console
and BenefitsFile
as two loggers which will log into a console and file respectively. We are setting log level to DEBUG. If you log any messages with a level lower than DEBUG, they will be logged into console or file. We will have to add a file benefits.log in classpath to achieve this logging in file. Log pattern is with date time, log level, class from which log is originating and log message.
Add logging in application code
Once we have required logging libraries and logging configuration adjusted, we can add logging in our code to capture this logging during runtime execution. In one of the managers CompanyManagerImpl, we will add a logger.
public static final Logger LOGGER = LogManager.getLogger(CompanyManagerImpl.class); @Override public List<Company> getAllCompanies() { LOGGER.info(" Enter >> getAllCompanies() "); List<Company> cList = (List<Company>) companyRepository.findAll(); LOGGER.info(" Exit << getAllCompanies() "); return cList; }
Now once we execute our spring boot application, we can capture the logs in console or file. The file will be benefits.log.
2. Error Handling
We will not write about exceptions in detail as it has been covered in this post Exceptions. We will create our own custom exception which will be extended from WebApplicationException
which jersey library provides.
This will look like below:
package com.betterjavacode.benefits.utilities; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; public class InvalidRequestException extends WebApplicationException { /** * */ private static final long serialVersionUID = 1L; private int errorcode = 00; // 00 indicates - no error public InvalidRequestException() { } public InvalidRequestException(int errorcode, String message) { super(Response.status(Response.Status.BAD_REQUEST).entity(message).build()); this.errorcode = errorcode; } public InvalidRequestException(int errorcode, String message, Throwable cause) { super(cause, Response.status(Response.Status.BAD_REQUEST).entity(message).build()); this.errorcode = errorcode; } }
Now we can use this custom exception in our managers when we want to throw an error message to indicate if there is anything wrong with client request. Similarly we can build another exception to show if there is anything wrong on server side. Following snippet shows from CompanyManagerImpl
where we have shown how to throw this exception.
@Override public Company getCompany(int guid) { LOGGER.info(" Enter >> getCompany() "); Company company = companyRepository.findOne(guid); if (company == null) { LOGGER.info(" Exit << createCompany() "); throw new InvalidRequestException(400, "Company not found"); } LOGGER.info(" Exit << getCompany() "); return company; }
In this post, we showed how to handle logging and errors in a REST API. The code for this is available on github repository.