Description
Problem description
We would like to collect metrics which counts which Objects and methods are called by templates.
Why?
We have lots of FM-templates which are provided by users (not developers). Collecting metrics about method invocations would allow us to track what users are doing in templates, find out about about which methods are used how often (e.g. to determine potential for performance optimizations or detect unused methods (potential dead code)). Furthermore it allows to detect malicious usage (e.g. attempts to call "dangerous" methods on objects which were are accessible in templates etc.).
Example of possible metrics output
com.company.MyClass#getA()=1 com.company.MyClass#getB(String)=567 com.company.Otherclass#getA(String, String)=134
etc.
For example Hibernate has a similar in-built mechanism which collects metrics.(https://docs.jboss.org/hibernate/orm/3.2/api/org/hibernate/stat/Statistics.html)
This is a bit too much for freemarker to have this built-in, that's why a hook would be enough.
Suggestion
It would be great if we e.g. could just override a method / hook in a Custom ObjectWrapper.
Could be specified in BeansWrapper.java and called from freemarker.ext.beans.BeansWrapper.invokeMethod(Object, Method, Object[]) just before the actual method invocation. /** * This hook can be used by subclasses * e.g. for statistics about method invokation * (which methods get called on which objects how often). * * @param object * @param method * @param args */ protected void logMethodInvocation(Object object, Method method, Object[] args) { // TODO Auto-generated method stub }
See example https://github.com/chrisrueger/freemarker/commit/804b3d99188e1dbb00fd03f8fe120515b17bbbca
Possible example implementation which uses this hook to collect metrics
public class MyObjectWrapper extends SimpleObjectWrapper{ private AtomicLongMap<String> methodStats = AtomicLongMap.create(); public MyObjectWrapper(Version version) { super(version); @Override protected void logMethodInvocation(Object object, Method method, Object[] args) { methodStats.incrementAndGet(object.getClass().getName()+"#"+method.getName()); } }