perf4j #Profiled annotation not working - spring

I have gone thru the below link from perf4J site and have done same to same: http://perf4j.codehaus.org/devguide.html#Using_Spring_AOP_to_Integrate_Timing_Aspects
Added the following in my spring.xml.
<aop:aspectj-autoproxy/>
<bean id="timingAspect" class="org.perf4j.log4j.aop.TimingAspect"/>
<bean id="wscClientBase" class="com.xyz.csa.core.common.WscClientBase"/>
In class WscClientBase I have the following method with #Profiled annotation.
#Profiled(tag = "SOAPCALLTEST")
public Object sendMessage(Object message) {
String msg = message.toString();
if (msg.indexOf(' ') > 1) {
msg = msg.substring(1, msg.indexOf(' '));
}
try {
Object ret = marshalSendAndReceive(message);
return ret;
} catch (RuntimeException ex) {
throw ex;
}
}
I dont see the perf4j TimingLogger statements in the application log. However if I use it obtrusively (without annotation) as below, I see the log statements successfully.
public Object sendMessage(Object message) {
String msg = message.toString();
if (msg.indexOf(' ') > 1) {
msg = msg.substring(1, msg.indexOf(' '));
}
StopWatch stopWatch = new Slf4JStopWatch();
try {
Object ret = marshalSendAndReceive(message);
stopWatch.stop("PERF_SUCCESS_TAG", msg);
return ret;
} catch (RuntimeException ex) {
stopWatch.stop("PERF_FAILURE_TAG", msg);
throw ex;
}
}
Am I missing something?

Perf4j
This is a performance analysis and checking plugin for application. It can be integrated with spring using spring AOP. It creates a log file that is give to a parser to analyse and produce relevant information. It can provide average,mean, std deviation by default.
For more general information please check http://perf4j.codehaus.org/index.html
How to setup Perf4j.
For normal setup you just need to add perf4j jar and create StopWatch instance for every code sniplet you want to monitor.
StopWatch stopWatch= new StopWatch(“snipletTagName”)
…
//{your code sniplet}
…
stopwatch.stop();
This will create perf4j monitor and you will get logg information on the console.
Main purpose of this documentation is to have a setup by setup understanding of integrating perf4j with spring.
1.Add all of the below Jar files.
1.perf4j-0.9.16-slf4jonly.jar
2.aspectjweaver-1.6.12.jar
3.aopalliance-1.0.jar
4.commons-logging-1.1.1.jar
5.logback-classic-1.0.7.jar
6.logback-core-1.0.7.jar
7.slf4j-api-1.7.1.jar
8.perf4j-0.9.16.jar
9.aspectjrt-1.6.1.jar
10.commons-jexl-1.1.jar
11.asm-1.5.3.jar
12.cglib-2.1_3.jar
Make sure you have all these jars in your classpath along with spring libraries.
2.create your own logback.xml that will be used by perf4j implicitly
the content of the logback.xml will be
<configuration>
<appender name="perf4jFileAppender"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>logs/perf4j.log</File>
<encoder>
<Pattern>%date %-5level [%thread] %logger{36} [%file:%line] %msg%n
</Pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>logs/perf4j.%d{yyyy-MM-dd}.log</FileNamePattern>
</rollingPolicy>
</appender>
<appender name="CoalescingStatistics"
class="org.perf4j.logback.AsyncCoalescingStatisticsAppender">
<param name="TimeSlice" value="1" />
<appender-ref ref="perf4jFileAppender" />
</appender>
<appender name="RootConsoleAppender" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>debug</level>
</filter>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%date %-5level [%thread] %logger{36} [%file:%line] %msg%n
</pattern>
</layout>
</appender>
<!-- Loggers -->
<!-- The Perf4J logger. Note that org.perf4j.TimingLogger is the value of
the org.perf4j.StopWatch.DEFAULT_LOGGER_NAME constant. Also, note that additivity
is set to false, which is usually what is desired - this means that timing
statements will only be sent to this logger and NOT to upstream loggers. -->
<logger name="org.perf4j.TimingLogger" additivity="false">
<level value="DEBUG" />
<appender-ref ref="CoalescingStatistics" />
<appender-ref ref="perf4jFileAppender" />
<appender-ref ref="RootConsoleAppender" />
</logger>
</configuration>
3.In your spring configuration file you need to add aspectj tag that will enable #Profiled annotation of perf4j.
(Note: What is #Profiled annotation?: you will add this tag to all the
methods in all the classes that are called from spring instance or use
dependency injection. The object basically should be spring context
registered and the method should be invoked by the object that are
registered in spring context. I wasted one day thinking why my method
was not logged then I realized that the object I tested was not part
of spring context.
OK, the code that you need to add to spring configuration xml is
<!-- this is my spring-context.xml -->
<beans>
<aop:aspectj-autoproxy>
<aop:include name="timingAspect" />
</aop:aspectj-autoproxy>
<bean id="timingAspect" class="org.perf4j.slf4j.aop.TimingAspect" />
<!-- this is the class that will be registered with the spring and now we can get this class and call the method that we need to monitor-->
<bean class="com.perf4jexample.Test" />
</beans>
4.Create the Test class that will implement #Profiled annotation.
public class Test {
private String testVal;
public Test() {
// TODO Auto-generated constructor stub
}
#Profiled
public void testing() {
System.out.println("testt" );
}
public String getTestVal() {
return testVal;
}
public void setTestVal(String testVal) {
this.testVal = testVal;
}
}
5.Ok now you have setup every thing just thing remains is test class that will start spring context and with it load perf4j.
public class Test(){
public static void main(){
AbstractApplicationContext context = new ClassPathXmlApplicationContext(
"spring-context.xml");
context.start();
Test bean = context.getBean(Test.class);
bean.testing();
}
I hope by following these setups you should be able to perf4j console appender to display one line on console.
Perf4j Monitoring command on the log:
For Generating Performance statistical information execute on you logger path
java -jar perf4j-0.9.16.jar myLogger.log
For Generating Graphs
java -jar perf4j-0.9.16.jar --graph perfGraphs.out myLogger.log
I hope this tutorial helps you to integrated Spring, perf4j, logback with Profiled annotation.

Try adding <aop:include name="timingAspect"/> inside the <aop:aspectj-autoproxy/>.
Can you also confirm that you are invoking sendMessage on an object that is retrieved from the spring application context (using getBean or injected as a dependency).

Here I have two ways to make perf4j #Profiled work on spring boot project.
Precondition is adding below dependencies
"org.aspectj:aspectjweaver",
"org.perf4j:perf4j:0.9.16",
"commons-jexl:commons-jexl:1.1",
"cglib:cglib:3.2.1",
For normal spring project, probably need to add little more dependencies like spring-aop, aopalliance... those looks included in spring-boot-starter-parent
1. Java Configuration
This is simplest way and mostly works, but I found that somehow not working on spring-data JpaRepository method. It just provides org.perf4j.log4j.aop.TimingAspect bean and do aspectj autoproxing. Same way like xml configuration provided by other people above
#Configuration
#EnableAspectJAutoProxy
public class PerformanceConfig {
#Bean
public TimingAspect timingAspect() {
return new TimingAspect();
}
}
2. Provide Your own Aspect
In this way, the #Profiled annotated spring-data repository interface methods also work fine. But the downside of this is ignoring the tag given in #Profiled(tag='some tag') and using the joinPoint method name as tag.
#Aspect
#Component
public class PerformanceTracker {
#Around(value="execution(#org.perf4j.aop.Profiled * com.mypackage..*(..))")
public Object checkPerformance(ProceedingJoinPoint pjp) throws Throwable {
StopWatch stopWatch = new Log4JStopWatch(pjp.getSignature().toShortString());
Object result = pjp.proceed();
stopWatch.stop();
return result;
}
}

The reason why it could not work is that the profiled method is in the parent class of the Spring-bean. I can tell it by looking at your bean name: WscClientBase. I assume that this is base class from which you have many children classes.
After some research time I found very important note in Spring documentation about #Transactional and #Cacheable.
http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/html/transaction.html#transaction-declarative-annotations. Check “Method visibility and #Transactional” block.
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html#cache-annotation-enable. Check “Method visibility and cache annotations”
In both cases they say things like this:
“When using proxies, you should apply the <> annotations only to
methods with public visibility. If you do annotate protected, private
or package-visible methods with these annotations, no error is raised,
but the annotated method does not exhibit the configured caching
settings. Consider the use of AspectJ (see below) if you need to
annotate non-public methods as it changes the bytecode itself.”
And below…
“Spring recommends that you only annotate concrete classes (and
methods of concrete classes) with the #Cache* annotation, as opposed
to annotating interfaces. You certainly can place the #Cache*
annotation on an interface (or an interface method), but this works
only as you would expect it to if you are using interface-based
proxies. The fact that Java annotations are not inherited from
interfaces means that if you are using class-based proxies
(proxy-target-class="true") or the weaving-based aspect (
mode="aspectj"), then the caching settings are not recognized by the
proxying and weaving infrastructure, and the object will not be
wrapped in a caching proxy, which would be decidedly bad.”
I assume that #Profiled use similar weaving mechanism therefore you can't put #Profiled on any method in parent classes.
As a matter of fact I had similar issue in my application: I had #Profiled and #Cacheable in parent class and none of those worked: I didn't see any records in my Perf4J log and cache wasn't updated. When I moved #Profiled into methods of children-classes I started seeing the records in the perf4j.log.

put config below in your "servlet-context-config.xml" .have fun!
<aop:aspectj-autoproxy/>
<bean id="timingAspect" class="org.perf4j.log4j.aop.TimingAspect"/>
<bean id="wscClientBase" class="com.xyz.csa.core.common.WscClientBase"/>

For people who have this type of problem they can check that there is not in spring logs (level info), messages like it " is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)".

Related

AOP Advice not getting executed

I am using AspectJ AOP with spring. I am NOT USING spring AOP but AspectJ AOP with spring.
I am using load time weaving. My advices are not getting executed. Any leads would greatly help.
#Aspect
public class ProfilingAspect {
#Around("methodsToBeProfiled()")
public Object profile(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("Hello #################");
return pjp.proceed();
}
#Pointcut("execution(public * com.core.search.domain.AIESearchDomainService.doSearch())")
public void methodsToBeProfiled() {
}
}
Following is the method on which I am trying to run the advice.
package com.core.search.domain;
public class AIESearchDomainService {
public SearchResponse doSearch() {
return null;
}
}
I have enabled load time weaving using the the below annotation.
#EnableLoadTimeWeaving
I have also specified the java agent for enabling the load time weaver as below.
-javaagent:"pathTo/spring-instrument-5.3.25.jar"
I have also created aop.xml and kept it in /resources/META-INF folder.
<aspectj>
<weaver>
<!-- only weave classes in our application-specific packages -->
<include within="com.core.search.domain.*"/>
</weaver>
<aspects>
<!-- weave in just this aspect -->
<aspect name="package.for.ProfilingAspect"/>
</aspects>
</aspectj>
There are no errors in the code but my advice is not getting executed. Please help. I believe weaving is happening because I am able to see the following log.
[AppClassLoader#18b4aac2] warning javax.* types are not being woven because the weaver option '-Xset:weaveJavaxPackages=true' has not been specified
I guess you forgot to enable aspectjWeaving param
#EnableLoadTimeWeaving(aspectjWeaving=ENABLED)

Logback.xml configuration by environment

I have logback configuration which is like bellow, as you can see i have an appender that appends all the logs to the standalone logging service.
<appender name="digitalGelfAppender"
class="de.siegmar.logbackgelf.GelfUdpAppender">
<graylogHost>testserver.loggingservice.com</graylogHost>
<graylogPort>1234</graylogPort>
<useCompression>true</useCompression>
<layout class="de.siegmar.logbackgelf.GelfLayout">
<originHost>originHost</originHost>
<includeRawMessage>false</includeRawMessage>
<includeMarker>true</includeMarker>
<includeMdcData>true</includeMdcData>
<includeCallerData>false</includeCallerData>
<includeRootCauseData>false</includeRootCauseData>
<includeLevelName>false</includeLevelName>
<shortPatternLayout class="ch.qos.logback.classic.PatternLayout">
<pattern>${log.pattern}</pattern>
</shortPatternLayout>
<fullPatternLayout class="ch.qos.logback.classic.PatternLayout">
<pattern>${log.pattern}</pattern>
</fullPatternLayout>
<staticField>application:DAP</staticField>
<staticField>environment:UAT</staticField>
</layout>
</appender>
<root level="#logback.loglevel#">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="digitalGelfAppender"/>
</root>
My question is that i need to change
<graylogHost>testserver.loggingservice.com</graylogHost>
<graylogPort>1234</graylogPort>
by environment value. So it will be like bellow on production server.
<graylogHost>prodserver.loggingservice.com</graylogHost>
<graylogPort>prodPort</graylogPort>
How can i pass the environment value to the logback.xml file by environment?
I tried to add these variables and use it like property inside xml. Its not working at all.
<property name="GRAYLOG_SERVER" value="${graylog-hostname}" />
<property name="GRAYLOG_SERVER_PORT" value="{graylog-port}" />
Usage
<graylogHost>${GRAYLOG_SERVER}</graylogHost>
<graylogPort>${GRAYLOG_SERVER_PORT}</graylogPort>
Hi i solve my problem by implementing listener that retrieve data from System parameter of environment;
#Controller
public class LoggerContextListener extends ContextAwareBase implements
LoggerContextListener, LifeCycle {
#Override
public void start() {
Map<String, String> property = System.getenv();
Context context = getContext();
context.putProperty("GRAYLOG_SERVER", property.get("graylog-hostname"));
context.putProperty("GRAYLOG_SERVER_PORT", property.get("graylog-port"));
}
After that i add that listener as a contextListener to the lockback.xml
<contextListener class="com.anadolusigorta.dap.config.DapLoggerContextListener"/>
Then problem solved! Now my xml can use these parameter that set from listener;
<graylogHost>${GRAYLOG_SERVER}</graylogHost>
<graylogPort>${GRAYLOG_SERVER_PORT}</graylogPort>
You don't need to create the listener. It is enough that use the environment variables as follows.
<graylogHost>${graylog-hostname}</graylogHost>
<graylogPort>${graylog-port}</graylogPort>

Access to request parameter in custom logback appender

I have custom logback appender that is used in my project. The project is based on REST controllers.
Is there a possibility to have access to current request parameters in the logback appender that is used by logger in spring controller? I need to write something to logs in basis on the request parameter.
What I can see is that logback is initialized before spring boot beans which is not good for me.
Use the org.slf4j.MDC: You can put values in the MDC at any time. Before anything else.
Example :
#ResponseBody
#RequestMapping(value="/myRequest", method = RequestMethod.GET)
public String doAnyAction(#RequestParam(value="Key", required = true) long key)
{
MDC.put("key", key);
logger.info(key);
}
And change your Logback.xml appender to add the "key" as well :
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<layout>
<Pattern>%X{key} - %m%n</Pattern>
</layout>
</appender>

Debug logging of ResponseBody by Springframework

I have a scenario where I send some sensitive data in the ResponseBody of a spring controller. Spring logs the entire response body in DEBUG mode, where I can see the sensitive data also. Now if I configure my app to print only INFO logs or higher, this won't be displayed, but that is not a fool proof method, since its possible that DEBUG mode could be turned on accidentally.
Is there anyway I can disable certain fields in the response body from being logged by Spring?
Thanks
My suggestion would be that you implement a custom converter for logback (if that is your logging library)
http://logback.qos.ch/manual/layouts.html#customConversionSpecifier
It enables you to convert your logging data and blur out any data that should be removed from the message.
From the documentation it is pretty straight forward
public class MySampleConverter extends ClassicConverter {
long start = System.nanoTime();
#Override
public String convert(ILoggingEvent event) {
long nowInNanos = System.nanoTime();
return Long.toString(nowInNanos-start);
}
}
And the config
<configuration>
<conversionRule conversionWord="nanos"
converterClass="chapters.layouts.MySampleConverter" />
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%-6nanos [%thread] - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="STDOUT" />
</root>
</configuration>
If you're using Log4j you can implement a custom filter which would mask the data per your layout
http://vozis.blogspot.sg/2012/02/log4j-filter-to-mask-payment-card.html
Edit
The final easier solution turned out to be to override toString of the return object. Spring uses toString to log the return message

Spring AOP and apache shiro configuration.Annotations not been scanned

I've been struggling with a configuration which requires a knowledge in AOP.
i must admit that AOP is that part i'm trying to get for a while without success.
It seems that my shiro annotations are not scanned and thus are ignored.
i've tried using shiro 1.1.0+ maven3+spring 3.0.5.RELEASE, hibernate 3.6.1.Final with ZK 5.0.6.
i got my hibernaterealm working , talking to database, i got the authentication working, i successfully(i believe) get the roles and permission loaded.
so to test the authorization side i have somewhere in my code this :
Subject currentUser = SecurityUtils.getSubject();
if (!currentUser.isPermitted("businessaccount:list")) {
throw new AuthorizationException("User not authorized");
}
and it works fine.
So i know my permissions were loaded.i'll be convenient for me using annotations to i've put it in implementation class, because i didn't plan on using interface at first place with my controller classes which are extending ZK GenericForwardController.
i've seen this bug and i've decided to do a try with one interface with the #RequiresPersmissions on methods.
apparently it's still not working as in it's giving access to unauthorized subject.there is no error in my log.Maybe i'm doing something wrong here are snippet of the codes:
#Component("layouteventhandler")
public class LayoutEventHandlerImpl extends GenericForwardComposer implements LayoutEventHandler {
Logger logger = Logger.getLogger(LayoutEventHandlerImpl.class);
Menuitem logout;
//...
#Override
public void onClick$pAccounts() {
try {
execution.sendRedirect("/accounts/personal/list");
} catch (Exception ex) {
logger.info("Error redirecting to personal accounts", ex);
}
}
#Override
public void onClick$bAccounts() {
try {
execution.sendRedirect("/accounts/business/list");
} catch (Exception ex) {
logger.info("Error redirecting to business accounts", ex);
}
}
//.....
}
its interface it :
public interface LayoutEventHandler {
#RequiresPermissions(value="personalaccount:list")
public void onClick$pAccounts();
#RequiresPermissions(value="businessaccount:list")
public void onClick$bAccounts();
//.....
}
here is my shiro applicationcontext
<bean id="hibernateRealm" class="com.personal.project.admin.webapp.security.DatabaseRealm" />
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="hibernateRealm" />
</bean>
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor">
<!-- <property name="proxyTargetClass" value="true" />-->
</bean>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
<!-- Secure Spring remoting: Ensure any Spring Remoting method invocations can be associated
with a Subject for security checks. -->
<bean id="secureRemoteInvocationExecutor" class="org.apache.shiro.spring.remoting.SecureRemoteInvocationExecutor">
<property name="securityManager" ref="securityManager"/>
</bean>
<!-- ... -->
is it in there something that i should do? thanks for reading and helping out
I don't know Shiro, but I'm guessing that you've put annotations on your bean classes which implement interfaces and then you're proxying them for security, transactions, and/or something else. When that happens, the object that's returned is a JDK dynamic proxy, which isn't an instance of your bean's concrete class, only of the interface it implements. Therefore any annotation scanning that depends on annotations in the concrete class won't find them.
To expand on Ryan Stewart's answer, you need to add
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
to the implementing class (not the interface) and move the Shiro annotations to it.
I encountered a similar problem when I was running two spring contexts. There is a parent root context that defined Database, Service, Security and non-SpringMVC web beans and a child web context for a Spring MVC REST api which contained the Controllers I want to proxy. The Configuration for each context was class path scanning separate packages.
In this case make sure that the DefaultAdvisorAutoProxyCreator and the AuthorizationAttributeSourceAdvisor beans that are requied are defined in the child web context (i.e. where the Rest Controllers are class path scanned) as defining them in the parent context does not work (the documentation on the DefaultAdvisorAutoProxyCreate is quite clear about this in hindsight!).
Posting this in case someone else encounters the same issue.

Resources