All Downloads are FREE. Search and download functionalities are using the official Maven repository.

hm.binkley.inject.TraceModule Maven / Gradle / Ivy

The newest version!
/*
 * This is free and unencumbered software released into the public domain.
 *
 * Please see https://github.com/binkley/binkley/blob/master/LICENSE.md.
 */

package hm.binkley.inject;

import com.google.inject.AbstractModule;
import com.google.inject.Module;
import com.google.inject.matcher.AbstractMatcher;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.kohsuke.MetaInfServices;
import org.slf4j.ext.XLogger;

import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

import static com.google.inject.matcher.Matchers.any;
import static org.slf4j.ext.XLoggerFactory.getXLogger;

/**
 * {@code TraceModule} is a Guice module to enable tracing via SLF4J of method entry/exit and
 * exceptions thrown (exception {@code Error}s).  Use {@link ServicesModule} to automatically
 * enable. Use {@link Trace @Trace} to mark classes or individual methods for tracing. Use 
 * {@code -ea} to enable/disable at runtime.
 *
 * @author B. K. Oxley (binkley)
 */
@MetaInfServices(Module.class)
public final class TraceModule
        extends AbstractModule {
    @Override
    protected void configure() {
        final ShouldTrace matcher = new ShouldTrace();
        final Tracer interceptor = new Tracer();
        bindInterceptor(matcher, any(), interceptor);
        bindInterceptor(any(), matcher, interceptor);
    }

    private static final class Tracer
            implements MethodInterceptor {
        private static XLogger logger(final MethodInvocation invocation)
                throws IllegalAccessException {
            final Class declaring = invocation.getMethod().getDeclaringClass();
            for (final Field field : declaring.getDeclaredFields())
                if (XLogger.class.isAssignableFrom(field.getType())) {
                    field.setAccessible(true);
                    return XLogger.class.cast(field.get(invocation.getThis()));
                }
            return getXLogger(declaring);
        }

        @Override
        public Object invoke(final MethodInvocation invocation)
                throws Throwable {
            final XLogger logger = logger(invocation);
            logger.entry(invocation.getArguments());
            try {
                final Object proceed = invocation.proceed();
                if (void.class == invocation.getMethod().getReturnType())
                    logger.exit();
                else
                    logger.exit(proceed);
                return proceed;
            } catch (final Error e) {
                // Pass Error straight through, no logging - state of VM unknown
                throw e;
            } catch (final Throwable e) {
                throw logger.throwing(e);
            }
        }
    }

    private static final class ShouldTrace
            extends AbstractMatcher {
        @Override
        public boolean matches(final AnnotatedElement element) {
            final Class type;
            if (element instanceof Method)
                type = Method.class.cast(element).getDeclaringClass();
            else if (element instanceof Class)
                type = Class.class.cast(element);
            else
                return false;

            final Trace trace = element.getAnnotation(Trace.class);
            return null != trace && (trace.ignoreAssertions() || type.desiredAssertionStatus());
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy