Coverage Report - at.ipsquare.commons.servlet.PerformanceLogFilter
 
Classes in this File Line Coverage Branch Coverage Complexity
PerformanceLogFilter
72%
43/59
75%
15/20
4
PerformanceLogFilter$DefaultLogFormatter
100%
3/3
N/A
4
 
 1  
 /**
 2  
  * Copyright (C) 2013 Matthias Langer
 3  
  *
 4  
  * Licensed under the Apache License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  *         http://www.apache.org/licenses/LICENSE-2.0
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package at.ipsquare.commons.servlet;
 17  
 
 18  
 import java.io.IOException;
 19  
 
 20  
 import javax.servlet.Filter;
 21  
 import javax.servlet.FilterChain;
 22  
 import javax.servlet.FilterConfig;
 23  
 import javax.servlet.ServletException;
 24  
 import javax.servlet.ServletRequest;
 25  
 import javax.servlet.ServletResponse;
 26  
 
 27  
 import org.apache.commons.lang3.StringUtils;
 28  
 import org.slf4j.Logger;
 29  
 import org.slf4j.LoggerFactory;
 30  
 
 31  
 import at.ipsquare.commons.core.util.Classes;
 32  
 import at.ipsquare.commons.core.util.PerformanceLogFormatter;
 33  
 import at.ipsquare.commons.core.util.PerformanceLogger;
 34  
 
 35  
 /**
 36  
  * This filter logs the execution time of incoming web requests using a {@link PerformanceLogger}.
 37  
  * 
 38  
  * @since 2.1.0
 39  
  * @author Matthias Langer
 40  
  */
 41  24
 public class PerformanceLogFilter implements Filter
 42  
 {
 43  4
     private static final Logger log = LoggerFactory.getLogger(PerformanceLogFilter.class);
 44  4
     private static final PerformanceLogFilterMessageFormatter DEFAULT_LOG_FILTER_MESSAGE_FORMATTER = new DefaultPerformanceLogFilterMessageFormatter();
 45  
     
 46  24
     private enum DefaultLogFormatter implements PerformanceLogFormatter
 47  
     {
 48  4
         INSTANCE;
 49  
         
 50  
         @Override
 51  
         public String format(StackTraceElement from, StackTraceElement to, long millis, String message)
 52  
         {
 53  28
             return new StringBuilder()
 54  
               .append(millis)
 55  
               .append("ms <<")
 56  
               .append(message)
 57  
               .append(">>")
 58  
               .toString();
 59  
         }
 60  
     }
 61  
     
 62  
     
 63  
     /**
 64  
      * Init parameter name for the threshold to use (see {@link PerformanceLogger#PerformanceLogger(long)}).
 65  
      */
 66  
     public final static String INIT_PARAM_THRESHOLD = "threshold";
 67  
     
 68  
     /**
 69  
      * Init parameter name for an optional prefix to use.
 70  
      */
 71  
     public final static String INIT_PARAM_PREFIX = "prefix";
 72  
     
 73  
     /**
 74  
      * Init parameter name for the {@link PerformanceLogFormatter} to use.
 75  
      */
 76  
     public final static String INIT_PARAM_PERFORMANCE_LOG_FORMATTER = "performanceLogFormatter";
 77  
     
 78  
     /**
 79  
      * Init parameter name for the {@link PerformanceLogFilterMessageFormatter} to use.
 80  
      */
 81  
     public final static String INIT_PARAM_PERFORMANCE_LOG_FILTER_MESSAGE_FORMATTER = "performanceLogFilterMessageFormatter";
 82  
     
 83  
     private long threshold;
 84  
     private RequestMatcher requestMatcher;
 85  
     private String prefix;
 86  
     private Class<? extends PerformanceLogFormatter> logFormatterClass;
 87  
     private Class<? extends PerformanceLogFilterMessageFormatter> logFilterMessageFormatterClass;
 88  
     
 89  
     @Override
 90  
     public void init(FilterConfig filterConfig) throws ServletException
 91  
     {
 92  24
         String thresholdString = filterConfig.getInitParameter(INIT_PARAM_THRESHOLD);
 93  24
         if(thresholdString != null)
 94  
         {
 95  
             try
 96  
             {
 97  16
                 threshold = Long.parseLong(thresholdString);
 98  
             }
 99  0
             catch(NumberFormatException e)
 100  
             {
 101  0
                 throw new ServletConfigurationError(
 102  
                         "Not a legal value for " + INIT_PARAM_THRESHOLD + ": '" + thresholdString + "'", e); 
 103  16
             }
 104  
         }
 105  
         
 106  24
         requestMatcher = PathPatternRequestMatcher.fromFilterConfig(filterConfig);
 107  24
         prefix = StringUtils.defaultString(filterConfig.getInitParameter(INIT_PARAM_PREFIX));
 108  24
         logFormatterClass = logFormatterClassFromConfig(filterConfig);
 109  20
         logFilterMessageFormatterClass = logFilterMessageFormatterClassFromConfig(filterConfig);
 110  20
     }
 111  
     
 112  
     private Class<? extends PerformanceLogFilterMessageFormatter> logFilterMessageFormatterClassFromConfig(FilterConfig filterConfig)
 113  
     {
 114  20
         return typeFromConfig(filterConfig, INIT_PARAM_PERFORMANCE_LOG_FILTER_MESSAGE_FORMATTER, PerformanceLogFilterMessageFormatter.class);
 115  
     }
 116  
     
 117  
     private static Class<? extends PerformanceLogFormatter> logFormatterClassFromConfig(FilterConfig filterConfig)
 118  
     {
 119  24
         return typeFromConfig(filterConfig, INIT_PARAM_PERFORMANCE_LOG_FORMATTER, PerformanceLogFormatter.class);
 120  
     }
 121  
 
 122  
     private static <T> Class<? extends T> typeFromConfig(FilterConfig filterConfig, String parameterName, Class<T> type)
 123  
     {
 124  44
         String className = filterConfig.getInitParameter(parameterName);
 125  44
         if(StringUtils.isEmpty(className))
 126  32
             return null;
 127  
         
 128  
         try
 129  
         {
 130  12
             return Classes.forName(className, type);
 131  
         }
 132  0
         catch(ClassCastException e)
 133  
         {
 134  0
             throw new ServletConfigurationError(e.getMessage());
 135  
         }
 136  4
         catch(ClassNotFoundException e)
 137  
         {
 138  4
             throw new ServletConfigurationError("Cannot load formatter class '" + className + "'.");
 139  
         }
 140  
     }
 141  
     
 142  
     private PerformanceLogFormatter performanceLogFormatter()
 143  
     {
 144  40
         if(logFormatterClass == null)
 145  32
             return DefaultLogFormatter.INSTANCE;
 146  
         
 147  
         try
 148  
         {
 149  8
             return logFormatterClass.newInstance();
 150  
         }
 151  0
         catch(Exception e)
 152  
         {
 153  0
             log.error("Could not create an instance of " + logFormatterClass.getName() + ".", e);
 154  0
             return DefaultLogFormatter.INSTANCE;
 155  
         }
 156  
     }
 157  
     
 158  
     private PerformanceLogFilterMessageFormatter filterMessageFormatter()
 159  
     {
 160  40
         if(logFilterMessageFormatterClass == null)
 161  36
             return DEFAULT_LOG_FILTER_MESSAGE_FORMATTER;
 162  
         
 163  
         try
 164  
         {
 165  4
             return logFilterMessageFormatterClass.newInstance();
 166  
         }
 167  0
         catch(Exception e)
 168  
         {
 169  0
             log.error("Could not create an instance of " + logFilterMessageFormatterClass.getName() + ".", e);
 170  0
             return DEFAULT_LOG_FILTER_MESSAGE_FORMATTER;
 171  
         }
 172  
     }
 173  
 
 174  
     @Override
 175  
     public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException
 176  
     {
 177  44
         if(!requestMatcher.matches(req))
 178  4
             chain.doFilter(req, res);
 179  
         else
 180  
         {
 181  40
             Throwable th = null;
 182  40
             PerformanceLogger plog = new PerformanceLogger(threshold, performanceLogFormatter());
 183  
             try
 184  
             {
 185  40
                 chain.doFilter(req, res);
 186  
             }
 187  4
             catch(Throwable e)
 188  
             {
 189  4
                 th = e;
 190  
             }
 191  
             finally
 192  
             {
 193  40
                 plog.logElapsed(toLogString(req, res, th));
 194  40
             }
 195  
             
 196  40
             if(th != null)
 197  
             {
 198  4
                 if(th instanceof IOException)
 199  0
                     throw (IOException) th;
 200  
                 
 201  4
                 if(th instanceof ServletException)
 202  0
                     throw (ServletException) th;
 203  
                 
 204  4
                 if(th instanceof RuntimeException)
 205  4
                     throw (RuntimeException) th;
 206  
                 
 207  0
                 if(th instanceof Error)
 208  0
                     throw (Error) th;
 209  
                 
 210  0
                 throw new RuntimeException(th);
 211  
             }
 212  
         }
 213  40
     }
 214  
     
 215  
     private String toLogString(ServletRequest req, ServletResponse res, Throwable th)
 216  
     {
 217  40
         return prefix + filterMessageFormatter().format(req, res, th);
 218  
     }
 219  
     
 220  
     @Override
 221  
     public void destroy()
 222  
     {
 223  
         
 224  0
     }
 225  
 }