In the process of designing software, we encounter certain instances where we need to choose between different approaches to solve a problem. I face this issue all the time. Here I’ll share one such scenario, when I started to develop a solution to log the time taken to execute certain methods which are executed.
The conventional method to achieve this task would be to add logs to each and every important method. The negative aspect I saw in the conventional approach, was the addition of the unnecessary amount of lines to the code base. Another problem with the conventional approach was the inconvenience when adding new methods that needs to log the time, where we have to write the same time logging logic once again in the new method. And finally and obviously, the conventional approach will introduce a large amount of code duplication as well.
Therefore, I was determined to take a rather different approach. Once I realized this, I started looking for other approaches to take. That is when I came across the concept of Aspect Oriented Programming (AOP). Personally, I’m not a fan of quoting wikipedia, but this is the definition it gave me,
In computing, aspect-oriented programming (AOP) is a programming paradigm that aims to increase modularity by allowing the separation of cross-cutting concerns. It does so by adding additional behavior to existing code (an advice) without modifying the code itself, instead separately specifying which code is modified via a “pointcut” specification, such as “log all function calls when the function’s name begins with ‘set’”. This allows behaviors that are not central to the business logic (such as logging) to be added to a program without cluttering the code, core to the functionality. AOP forms a basis for aspect-oriented software development.
Once I saw this definition, I thought to myself, “Yess!! This is what I wanted”. So, I dug deep to find out more about it. As the definition suggests, AOP allows the separation of cross cutting concerns which includes logging across the application as well.
Once I figured out this approach to be taken to solve the problem, I looked for a way to integrate this into the product I was working on, which was the WSO2 API Manager. As some of you might know, the WSO2 Api Manager is a java based platform with maven and OSGI framework utilized to package it. Therefore, the next challenge I faced was to find a framework to achieve this.
This is where I came across AspectJ. AspectJ is an AOP extension written for java by PARC (Palo Alto Research Center) and can be found in Eclipse Foundation open source projects.
AspectJ takes instructions provided via aspects and generates the final implementation code. This process is called Aspect Weaving. AspectJ comprises of two types of weaving,
- Compile Time Weaving
- Run Time Weaving
Compile time weaving involves weaving at the compile time and Run time weaving involves just-in-time weaving by adding the weaver as a java agent. I utilized a performance test in order to decide between the two approaches, and compile time weaving had a high API gateway throughput than the runtime weaving option. Therefore I went ahead with compile time weaving.
AspectJ gives the ability to define certain pointcuts. These pointcuts give the programmer the ability to mention where to apply the given advices appropriately. It can also be seen as a filtering mechanism on applying the advices. There is a wide range of advices that can be given in defining pointcut expressions.
Implementation of Solution
First of all I added the functionality to log all methods of gateway handlers, without any logic to filtering out methods.
With this approach, I did a performance test to determine the overhead produced by utilizing aspectJ for my implementation. Then I noticed a pretty significant overhead (~20%), which was a significant overhead for our API Gateway.
Then I decided to limit the number of methods by adding a filtering logic. The following example shows a final pointcut expression I have utilized to achieve the method time logging for the API Gateway.
@Pointcut("execution(* org.wso2.carbon.apimgt.gateway.handlers..*(..)) " +
"&& (@annotation(MethodStats) || @target(MethodStats))")
As you can notice, I have added the following part to the point cut expression:
@annotation(MethodStats) || @target(MethodStats)
Basically what I did was, adding an annotation named @MethodStats and marking every method and class that should be included in time logging with this annotation. The additional logic I applied to the pointcut ensures this. This helped me to log method execution times only for the important methods which helped me to reduce the overhead to a minute amount (~0.4%).
In the future if someone implements new methods or classes that needs to be included in time logging, all they have to do is to mark the respective method or class with the annotation, @MethodStats.
Aspect Oriented Programming is an awesome mechanism to consider when implementing a solution if you utilize it properly. What you can achieve using AOP totally depends on your creativity. But be mindful about the performance overhead introduced by it.