Issue
I'm wasting a lot of time right now with AOP logging setup. I don't know why AOP isn't working in my project. I think I've done all the settings I can. Please let me know if you guys have a solutions. Thank you.
- application.java
@EnableAspectJAutoProxy
@SpringBootApplication
@ComponentScan(basePackages = "com.demo.apiservice")
@MapperScan("com.demo.apiservice.mapper")
public class ApiServiceApplication extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(ApiServiceApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(ApiServiceApplication.class, args);
}
@Bean
public ModelMapper modelMapper() {
return new CustmizedModelMapper();
}
@Bean
public AopLoggingConfig loggingAspect(){
return new AopLoggingConfig();
}
}
build.gradle
configurations { all { exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' } } dependencies { //implementation 'org.springframework.boot:spring-boot-starter-security:2.5.5' implementation 'org.springframework.boot:spring-boot-starter-web:2.5.5' implementation 'org.springframework.boot:spring-boot-starter-log4j2:2.5.5' implementation 'org.springframework.boot:spring-boot-starter-mail:2.5.5' implementation 'org.springframework.boot:spring-boot-starter-aop:2.5.5' implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.3' testImplementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter-test:2.1.3' implementation 'org.springframework.boot:spring-boot-starter-data-rest:2.5.5' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.projectlombok:lombok:1.18.8' implementation 'org.modelmapper:modelmapper:2.3.8' implementation 'org.springframework.boot:spring-boot-starter-data-jpa:2.5.5' implementation 'org.springframework.boot:spring-boot-starter-jdbc:2.5.5' implementation 'com.h2database:h2:1.4.200' implementation 'org.springframework.boot:spring-boot-configuration-processor:2.5.5' implementation 'org.springframework.security:spring-security-core:5.4.2' implementation 'com.fasterxml.jackson.core:jackson-core:2.12.3' implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.12.3' implementation 'com.fasterxml.jackson.core:jackson-databind:2.12.3' implementation 'org.apache.httpcomponents:httpclient:4.5.13' implementation 'com.nimbusds:nimbus-jose-jwt:9.7' implementation 'io.springfox:springfox-swagger2:3.0.0' implementation 'io.springfox:springfox-swagger-ui:2.9.2' implementation 'joda-time:joda-time:2.10.10' implementation 'io.jsonwebtoken:jjwt-api:0.11.1' implementation 'javax.inject:javax.inject:1' implementation 'com.googlecode.json-simple:json-simple:1.1' implementation 'de.mkammerer:argon2-jvm:2.7' implementation 'org.bouncycastle:bcprov-jdk15on:1.68' implementation 'org.apache.maven.plugins:maven-surefire-plugin:2.22.0' implementation 'javax.validation:validation-api:2.0.1.Final' implementation 'org.postgresql:postgresql:42.1.4' implementation 'org.hibernate:hibernate-gradle-plugin:5.6.1.Final' implementation 'com.jayway.jsonpath:json-path:2.6.0' compileOnly 'org.projectlombok:lombok:1.18.8' testCompileOnly 'org.projectlombok:lombok:1.18.8' runtimeOnly 'org.springframework.boot:spring-boot-devtools:2.5.5' annotationProcessor 'org.projectlombok:lombok:1.18.8' testAnnotationProcessor 'org.projectlombok:lombok:1.18.8' testImplementation 'org.springframework.boot:spring-boot-starter-test:2.5.5' testImplementation 'org.springframework.security:spring-security-test:5.5.2' }
AopLoggingComponent.java
package com.demo.apiservice.config; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component @Aspect public class AopLoggingConfig { Logger logger = LoggerFactory.getLogger(AopLoggingConfig.class); static String name = ""; static String type = ""; /** * AspectJ is applied only to a specific class/method in package. */ @Around("execution(* com.demo.apiservice.customer.*Controller.*(..))") public Object logPrint(ProceedingJoinPoint joinPoint) throws Throwable { type = joinPoint.getSignature().getDeclaringTypeName(); if (type.indexOf("Controller") -1) { name = "Controller \t: "; } else if (type.indexOf("Service") -1) { name = "ServiceImpl \t: "; } else if (type.indexOf("DAO") -1) { name = "DAO \t\t: "; } logger.debug(name + type + ".@@@@@@@@@@@@@@@@@@@@@@@@@ " + joinPoint.getSignature().getName() + "()"); return joinPoint.proceed(); } }
controller.java
package com.demo.apiservice.customer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import com.demo.apiservice.constant.Constants; import com.demo.apiservice.customer.service.CustomerService; import com.demo.apiservice.customer.service.impl.CustomerServiceImpl; import com.demo.apiservice.request.CustomerRequest; import com.demo.apiservice.request.LoginRequest; import com.demo.apiservice.response.GetCustomerResponse; import com.demo.apiservice.response.SuccessResponse; import com.demo.apiservice.utils.ResponseHandler; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; import lombok.extern.slf4j.Slf4j; import java.util.List; import javax.validation.Valid; @Slf4j @RestController @Api(tags = "Customers APIs") @RequestMapping("/apiservice/v1/customers") public class CustomerController { @Autowired private CustomerService customerService; @GetMapping @ApiOperation(value = "List of Customers API") @ApiResponses(value = { @ApiResponse(code = 400, message = Constants.BAD_REQUEST), @ApiResponse(code = 403, message = Constants.ACCESS_DENIED)}) public ResponseEntity<Object retrieveAll() { log.info("Start of CustomerController::retrieveAll method"); return customerService.retrieveAll(); } }
application.yml
logging: level: org: springframework.web: DEBUG hibernat: DEBUG com: atoz_develop: mybatissample: repository: TRACE mybatis: mapper-locations: classpath:/mappers/*.xml type-aliases-package: com.demo.apiservice.entity configuration: map-underscore-to-camel-case: 'true' debug: 'true' spring: datasource: driver-class-name: org.postgresql.Driver username: postgres url: jdbc:postgresql://localhost:5432/postgres platform: postgres password: postgres jpa: generate-ddl: 'false' properties: hibernate: dialect: org.hibernate.dialect.PostgreSQL10Dialect format_sql: 'true' hibernate: ddl-auto: update show-sql: 'true'
stack trace log
2021-11-17 16:05:19.992 DEBUG 23300 --- [nio-8080-exec-8] o.s.w.s.DispatcherServlet : GET /apiservice/v1/customers, parameters={} 2021-11-17 16:05:19.992 DEBUG 23300 --- [nio-8080-exec-8] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.demo.apiservice.customer.CustomerController#retrieveAll() 2021-11-17 16:05:19.993 INFO 23300 --- [nio-8080-exec-8] c.l.a.c.CustomerController : Start of CustomerController::retrieveAll method 2021-11-17 16:05:19.993 INFO 23300 --- [nio-8080-exec-8] c.l.a.c.s.i.CustomerServiceImpl : Inside of the CustomerServiceImpl :: retrieveAll method 2021-11-17 16:05:19.996 INFO 23300 --- [nio-8080-exec-8] c.l.a.c.s.i.CustomerServiceImpl : End of the CustomerServiceImpl :: retrieveAll method 2021-11-17 16:05:19.996 DEBUG 23300 --- [nio-8080-exec-8] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Using application/xhtml+xml, given [text/html, application/xhtml+xml, image/avif, image/webp, image/apng, application/xml;q=0.9, application/signed-exchange;v=b3;q=0.9, */*;q=0.8] and supported [application/json, application/*+json, application/json, application/*+json, application/xml;charset=UTF-8, text/xml;charset=UTF-8, application/*+xml;charset=UTF-8, application/xml;charset=UTF-8, text/xml;charset=UTF-8, application/*+xml;charset=UTF-8] 2021-11-17 16:05:19.996 DEBUG 23300 --- [nio-8080-exec-8] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Writing [{data=[Customer([email protected], password=null, customerId=null, storeName=eos, firstname=test1 (truncated)...] 2021-11-17 16:05:19.998 DEBUG 23300 --- [nio-8080-exec-8] o.s.w.s.DispatcherServlet : Completed 200 OK
Solution
Your aspect is triggered. I added an explicit controller method call in order to check:
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(ApiServiceApplication.class, args);
context.getBean(CustomerController.class).retrieveAll();
}
Then I fixed some typos in your code like the one mentioned in my previous comment.
Probably your problem was that you simply did not see the log output because you forgot the log configuration for your application package com.demo.apiservice
:
logging:
level:
org:
springframework.web: DEBUG
hibernate: DEBUG
com:
atoz_develop:
mybatissample:
repository: TRACE
demo.apiservice: DEBUG
BTW, I also corrected your typo hibernat
to hibernate
, but that is unrelated to the problem at hand.
Then I see this in the log:
[ restartedMain] o.s.b.w.e.t.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
[ restartedMain] c.d.a.ApiServiceApplication : Started ApiServiceApplication in 5.101 seconds (JVM running for 6.117)
[ restartedMain] c.d.a.c.AopLoggingConfig : Controller : com.demo.apiservice.customer.CustomerController.@@@@@@@@@@@@@@@@@@@@@@@@@ retrieveAll()
[ restartedMain] c.d.a.c.AopLoggingConfig : Controller : com.demo.apiservice.customer.CustomerController.@@@@@@@@@@@@@@@@@@@@@@@@@ retrieveAll()
[ restartedMain] c.d.a.c.CustomerController : Start of CustomerController::retrieveAll method
Do you see the problem? You get duplicate logging, because the aspect is picked up once by component scan and instantiated one more time as a bean in your application configuration. So you need to remove this part from ApiServiceApplication
:
@Bean
public AopLoggingConfig loggingAspect() {
return new AopLoggingConfig();
}
Now the duplicate logging is gone.
Next, maybe you want to simplify your aspect a bit and simply log joinPoint
or joinPoint.getSignature()
. You also want to make name
and type
local variables, because the static fields are not thread-safe. Instead, probably you want a static logger in the aspect.
@Component
@Aspect
public class AopLoggingConfig {
private static Logger logger = LoggerFactory.getLogger(AopLoggingConfig.class);
@Around("execution(* com.demo.apiservice.customer.*Controller.*(..))")
public Object logPrint(ProceedingJoinPoint joinPoint) throws Throwable {
String type = joinPoint.getSignature().getDeclaringTypeName();
String name = "";
if (type.contains("Controller")) {
name = "Controller \t: ";
}
else if (type.contains("Service")) {
name = "ServiceImpl \t: ";
}
else if (type.contains("DAO")) {
name = "DAO \t\t: ";
}
logger.debug(name + joinPoint.getSignature());
return joinPoint.proceed();
}
}
The log line becomes:
Controller : ResponseEntity com.demo.apiservice.customer.CustomerController.retrieveAll()
But actually, both the package name and the class name indicate that we are dealing with a controller, DAO or service. So why bother with the if-else
stuff in the first place? Why make a simple matter complicated and the aspect slower? Besides, if you only want to log something and not influence the control flow, the method parameters or the return value, a simple @Before
advice would do, the expensive @Around
is unnecessary.
@Component
@Aspect
public class AopLoggingConfig {
private static Logger logger = LoggerFactory.getLogger(AopLoggingConfig.class);
@Before("execution(* com.demo.apiservice.customer.*Controller.*(..))")
public void logPrint(JoinPoint joinPoint) {
logger.debug(joinPoint.getSignature().toString());
}
}
The log line becomes:
ResponseEntity com.demo.apiservice.customer.CustomerController.retrieveAll()
Isn't that enough?
Or even simpler:
logger.debug(joinPoint.toString());
Log:
execution(ResponseEntity com.demo.apiservice.customer.CustomerController.retrieveAll())
Keep it simple!
Answered By - kriegaex