Search code examples
intellij-ideaaspectjperf4j

Enable perf4j profiled annotation in intellij


I'm trying to enable perf4j annotations in IntelliJ but I'm struggling to configure AspectJ correctly. More specifically, the log file is created correctly but lacks any data from the annotated method.

These are the relevant extracts of the configuration:

logback.xml

<configuration debug="true">

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <!-- encoders are assigned the type
         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
    <encoder>
        <pattern>%d{HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
</appender>

<appender name="statistics" class="ch.qos.logback.core.FileAppender">
    <file>./target/statisticsLogback.log</file>
    <append>false</append>
    <layout>
        <pattern>%msg%n</pattern>
    </layout>
</appender>

<appender name="coalescingStatistics" class="org.perf4j.logback.AsyncCoalescingStatisticsAppender">
    <!-- encoders are assigned the type
         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
    <timeSlice>1000</timeSlice>
    <appender-ref ref="statistics"/>
</appender>

<appender name="listAppender" class="ch.qos.logback.core.read.ListAppender">
    <!-- encoders are assigned the type
         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
    <timeSlice>1000</timeSlice>
</appender>

<logger name="org.perf4j.TimingLogger" level="info">
    <appender-ref ref="coalescingStatistics" />
    <appender-ref ref="listAppender"/>
</logger>

<root level="debug">
    <appender-ref ref="STDOUT" />
</root>

aop.xml

<?xml version="1.0" encoding="UTF-8"?>
<aspectj>
    <!--
     We only want to weave in the log4j TimingAspect into the @Profiled classes.
     Note that Perf4J provides TimingAspects for the most popular Java logging
     frameworks and facades: log4j, java.util.logging, Apache Commons Logging
     and SLF4J. The TimingAspect you specify here will depend on which logging
     framework you wish to use in your code.
    -->
    <aspects>
        <aspect name="org.perf4j.slf4j.aop.TimingAspect"/>
        <!-- if SLF4J/logback use org.perf4j.slf4j.aop.TimingAspect instead -->
    </aspects>
    <weaver options="-verbose -showWeaveInfo">
        <!--
         Here is where we specify the classes to be woven. You can specify package
         names like com.company.project.*
        -->
        <include within="com.mycode.myproject.mypackage.*"/>
        <include within="org.perf4j.slf4j.aop.*"/>
    </weaver>
</aspectj>

Finally, the related test method is tagged with the @Profiled annotation, this is part of the package defined in aop.xml.

This configuration results in the log file being produced (which suggests that the logback.xml is configured correctly), however, it only contains headers and no statistics from the tagged method.

The main question I have is where the AspectJ configuration should go within IntelliJ. I have included the aop.xml under a manually created META-INF folder in the src folder but I'm not sure this is detected by AspectJ at all.

Thanks in advance

UPDATE

I have made some progress on this since my initial post, specifically, introducing two changes: i) included -javaagent:lib\aspectjweaver.jar ii) moved aop.xml into the META-INF folder. The aop configuration is now being picked up as it logs the configuration details and it also mentions the method being profiled. The issue now is that the thread being profiled crashes, it doesn't log any exceptions but via debug the issue seems to be related to a ClassNotFoundException in org.aspectj.runtime.reflect.Factory when trying to instantiate org.aspectj.runtime.reflect.JoinPointImpl.

To isolate the issue, I have removed all the Maven imports of AspectJ and used the jars provided by the installation package but the issue persists, also the fact that the application crashes without any logging makes the issue tracking harder.

UPDATE

To clarify:

  • After reading more about this, including the manual in the wayback link (thanks for that), I realized I was mixing up load-time / compile-time approach. Since then I tried both methods as described in the guide but with the same results described in my earlier update.
  • As per above, I do start the application with the AspectJ weaver option (-javaagent)
  • The build is done via IDE, as per above at the moment I have removed the AspectJ / perf4j dependencies from Maven and linked to local jars
  • aop.xml does get picked up, as mentioned in the update, with no errors or warning, just a confirmation of the weaved method.

Solution

  • Okay, I have added a full Maven example to a GitHub repo which you can just clone and play around with.

    Some basic things to consider:

    • For compile-time weaving (CTW) you need aspectjrt.jar on the classpath when compiling and running the code. You also need to use the AspectJ compiler to build the project, a normal Java compiler is not enough.
    • For load-time weaving (LTW) you need aspectjweaver.jar as a Java agent on the command line when running the code: -javaagent:/path/to/aspectjweaver.jar. You also need to add it as a VM argument to your LTW run configuration in IDEA.
    • For LTW you also need META-INF/aop.xml in your resources folder. Please also note that in order to encompass subpackages you should use the ..* notation, not just .*, e.g. <include within="de.scrum_master..*"/>.

    You find more information in my project's read-me file.

    P.S.: The Perf4J documentation is outdated and the project unmaintained. Thus, it still mentions AspectJ 1.6.x as necessary dependencies. I built and ran everything with the latest AspectJ 1.8.10 and it runs just fine, both from Maven and IDEA.