I have a JBoss EAP server on a OpenShift. And occassionaly, a situation occurs which makes the app start throwing exceptions with lenghty stack traces on each request. That causes the logs to reach dozens of MB per day, and after some time, the hosting runs out of disk space. 

 

I couldn't find something which would take the exception and tell me if the same exception was already seen or not. Application wide. Could be expiring cache.

I recall I've seen some RepeatedExceptionsHandler or something such for Log4j which I use, or maybe it was Hibernate, but can't find it in a quick google search.

 

So here's my solution. Just a first sketch but works.

 

Usage

    catch( Exception ex ){
        int count = repeatedExDetector.countException( ex );
        if( count < 2 )
            log.error("Error rendering: " + path, ex);
        else
            log.error("Recurring rendering error: " + path);
        add( new TexyDocumentErrorPanel("document", "Error occured when loading document.", "Couldn't load: " + path, 500));
    }

 

Results

 

02:23:18,324 DEBUG Characteristic: 46d21ef0{8a5b1c7c@293{b0d929ae@276{b0d929ae@182{de8e612a@241
02:23:18,324 ERROR Error rendering: /programovani/artificial_intelligence/covering_ai_theory.texy:
    java.lang.UnsupportedOperationException: Definition lists not supported yet.
        at cz.dynawest.jtexy.modules.ListModule$DefListPatternHandler.handle(ListModule.java:293)
        ...

02:23:19,956 DEBUG Characteristic: 46d21ef0{8a5b1c7c@293{b0d929ae@276{b0d929ae@182{de8e612a@241
02:23:19,957 ERROR Recurring rendering error: /programovani/artificial_intelligence/covering_ai_theory.texy

 

Source

 

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *  Counts the occurrences of given exception.
 *  Uses messages hashes and first 3 stack elements class name hashes to determine "same" exception.
 * 
 *  @author Ondrej Zizka, ozizka at redhat.com
 */
public class RepeatedExceptionsDetector {
    private static final Logger log = LoggerFactory.getLogger( RepeatedExceptionsDetector.class );

    private final Map<String, Integer> exCount = new ConcurrentHashMap();

    private static final int STACKTRACE_SCAN_DEPTH = 3;

    /**
     *  Counts how many times given exception was seen.
     */
    public int countException( Exception ex ){
        String chara = buildCharacteristicsString( ex );
        log.info("Characteristic: " + chara);
        Integer count = this.exCount.get( chara );
        if( count == null )
            count = new Integer(1);
        else
            count++;
        this.exCount.put( chara, count );
        return count;
    }


    /**
     *  Builds a string which can be considered as a hash of given exception.
     */
    private static String buildCharacteristicsString( Exception ex ) {
        StringBuilder sb = new StringBuilder();

        Throwable curEx = ex;
        do{
            sb.append( Integer.toHexString( curEx.getMessage().hashCode() ) );
            StackTraceElement[] stackTrace = curEx.getStackTrace();
            // For each stack trace element, append "cFooClass@72".
            for( int i = 0; i < stackTrace.length & i <= STACKTRACE_SCAN_DEPTH; i++ ) {
                StackTraceElement ste = stackTrace[i];
                sb.append('{');
                sb.append( Integer.toHexString( ste.getClassName().hashCode() ) );
                sb.append('@');
                sb.append( ste.getLineNumber() );
            }
            sb.append("\n");
            curEx = curEx.getCause();
        }while( curEx != null );

        return sb.toString();
    }

}// class