Monitoring Spring Application using ELK and AspectJ — Part 2
Hey guys, for those of you who have not read the first part of this article here is the link. Check it out, that should help you get a basic understanding of what we are trying to accomplish here.
Time for the Spring project. A little overview on what we shall be doing here:
- Get a starter Spring project from here!
- Do basic configurations.
- Implement custom annotation, to be added to controllers/services that need monitoring.
- Use AspectJ to dynamically wrap those components with our implementation logic.
- Sending the logs to a logging handler.
II. Developing Spring-Based Monitoring Interface
The Spring Framework is an fast application development framework. It employs inversion of control container as its key defining feature. We’ll be taking the advantage of its ease of configuration to develop our monitoring interface. Let’s get started:
First, go here, and get yourself a project with the following configurations:
Group: com.ramizmehran
Artifact: monitoringInterface
Dependencies: Web, Aspects
Spring Boot Version: 1.5.* (Not Snapshot)
Let other details be the default.
Generate the project, extract and import in your favorite editor!
2. Configuring the Project
You will have a file by the name MonitoringInterfaceApplication.java, which acts as the starting point for a spring application. This class is not of much use to us, as our project is meant to be used as a dependency, and not a standalone project.
We will start by creating a configuration file , that shall be picked by the main application that we intend to add monitoring on. Create a class LoggingConfiguration.java at com.ramizmehran.monitoringInterface.configs package. To this class add the following annotations: @Configuration and @EnableAspectJAutoProxy.
This is to ensure, the application has AspectJ proxies enabled, as that is required for our implementation to work.
3. Custom Annotation — @Metered
Custom annotations are user defined annotations, used to define certain set of properties or values for an entity. That entity can be anything ranging from a class to a bean.
Let’s make an @interface file as described below:
This creates an annotation by the name Metered. This annotation has three properties:
- A String variable — value,
- A String array — logRequestBodyParamNames , and
- Another String array — logHeaderParamNames.
All three properties are nullable, as they have default empty values hence, it is not compulsory to pass these values with the annotation. An example on how to use the annotation is here:
4. AspectJ Wrapper
AspectJ is an aspect-oriented programming extension. Aspect-oriented programming is about dynamically weaving code into the existing source code, without having the alter it. To know more about it, try AspectJ In Action good book for beginners.
We’ll make an aspect named MeteredLoggingAspect. This aspect will look for all the controller methods that have the above annotation, and wrap them around with our code. The code for it is below:
Too much code!!! :D Don’t worry, I will give a gist of what I am doing here.
- We take the request parameters of the function being called.
- We initialize of logging request object with these parameters.
- Then, we let the function execution process by calling, joinPoint.proceed()
- After the function is executed, we take in the response as well to be logged
- We initiate an async hit to the logging service, and forget about it.
- Finally, we return the function’s return object back to the caller.
@Around method handles the case of a normal execution. But, in case of an exception the control will jump from joinPoint.proceed() to @AfterThrowing method. The logAfterThrowingAllMethods handles the exception scenarios, and thus help us log the error cases as well.
5. Log Polling Service
The service layer of our application will be responsible for accumulating all the data and then sending it forward. We will make a LoggingService, for this job. The code for it is below:
What it does:
- First initialize request is the one we called from the aspect, to get our request parameters ready.
- Then sendMeterLog function, polls the log for a happy scenario.
- While, sendExceptionLog function, polls log for exception cases.
Each function is quite simple, and does a small but specific job.
Finally, you might ask why am I storing the initial meterRequest in the httpRequest object. The reason for this is again exceptions. In case of an exception, I need the initial details to be sent forward. Hence, when stored in the httpRequest object I can easily fetch the attribute.
Conclusion
Now, you have a project that can be built into a jar, and then added as dependency to any project. The full project code is here, you can take it as a reference but I would strongly urge you not to, and try it out yourself.
For the people new to Spring, I will be posting a series of tutorial articles for that soon. Those articles will surely clear some of the doubts that you might have about the above application as a beginner.
The next article will have details about the application which shall take in requests and poll it to Logstash.