Issue
I've got an Spring Boot 2.2 Application which publishes and consumes spring application events in different packages. Now I want to log every time an event has been published by ApplicationEventPublisher.publishEvent()
.
One solution could be to write my own event publisher like:
public class LoggableApplicationEventPublisher implements ApplicationEventPublisher {
private final ApplicationEventPublisher eventPublisher;
private final Logger logger;
public ApplicationEventLogger(ApplicationEventPublisher eventPublisher, Logger logger) {
this.eventPublisher = eventPublisher;
this.logger = logger;
}
@Override
public void publishEvent(ApplicationEvent event) {
eventPublisher.publishEvent(event);
logger.info("--> Emitting {}", event);
}
}
Another solution could be to use aspect oriented programming and write an Aspect which is triggered everytime publishEvent()
has been triggered:
@Aspect
@Component
public class EventPublishAspect {
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@Pointcut("execution(* org.springframework.context.ApplicationEventPublisher.*(..))")
public void logPublishEvent() {
}
@After("logPublishEvent()")
public void log(JoinPoint point) {
Object[] lArgs = point.getArgs();
LOG.info("Triggered", lArgs[0]);
}
}
I've set up all correctly (dependencies aswell) and this example is working for other pointcuts (like for a call of specific method of my services).
However, this aspect is not working with the declared pointcut for the ApplicationEventPublisher
-Interface. Do you know why not? It seems like spring boot injects AbstractApplicationContext
on runtime, which is actually implementing this interface.
Solution
Solution that does not require aspects (and has faster startup time?)
@Primary
@Bean
DelegatingApplicationEventPublisher applicationEventPublisher(ApplicationContext applicationContext) {
new DelegatingApplicationEventPublisher(applicationContext)
}
@Slf4j
@RequiredArgsConstructor
public class DelegatingApplicationEventPublisher implements ApplicationEventPublisher {
private final ApplicationContext context;
@Override
public void publishEvent(ApplicationEvent event) {
logEvent(event);
context.publishEvent(event);
}
@Override
public void publishEvent(Object event) {
logEvent(event);
context.publishEvent(event);
}
private void logEvent(Object event) {
if (event instanceof PayloadApplicationEvent payloadApplicationEvent) {
log.debug(markers("eventName", payloadApplicationEvent.getPayload().getClass(), "event", payloadApplicationEvent.getPayload()), "publishing...");
} else {
log.debug(markers("eventName", event.getClass(), "event", event), "publishing ...");
}
}
}
Answered By - agdula
Answer Checked By - David Goodson (JavaFixing Volunteer)