View Javadoc
1   package at.rseiler.spbee.demo;
2   
3   import org.apache.log4j.Logger;
4   
5   import java.io.IOException;
6   import java.io.InputStream;
7   import java.util.Arrays;
8   import java.util.Map;
9   import java.util.Properties;
10  import java.util.concurrent.ConcurrentHashMap;
11  import java.util.concurrent.atomic.AtomicLong;
12  
13  /**
14   * Logs the stored procedure execution durations.
15   * <p>
16   * If the stored procedure call exceed 200 ms (default value) then the call is logged as WARNING like:
17   * <code>n ms sp_name[sp_arguments]#sp_call_count</code>.
18   * Example: <code>17 ms sp_get_user[3]#1</code>
19   * <p>
20   * The default value can be changed with the <code>interceptor.splogger.slow.query.duration</code> property in <code>/spbee.properties</code>.
21   * <p>
22   * The logger name is: <code>spLogger</code>
23   * If TRACE is enabled then every stored procedure call is logged.
24   *
25   * @author Reinhard Seiler {@literal <rseiler.developer@gmail.com>}
26   */
27  public final class SpLogger {
28  
29      private static final int SLOW_QUERY_DURATION;
30      private static final int SLOW_QUERY_DURATION_DEFAULT = 200;
31      private static final AtomicLong ID = new AtomicLong(1);
32      private static final String CONFIG = "/spbee.properties";
33      private static final Logger LOG = Logger.getLogger("spLogger");
34  
35      private static final Map<Object, Long> idTimestampMap = new ConcurrentHashMap<>();
36  
37      static {
38          int slowQueryDuration = SLOW_QUERY_DURATION_DEFAULT;
39          InputStream spLoggerInputStream = null;
40  
41          try {
42              spLoggerInputStream = SpLogger.class.getResourceAsStream(CONFIG);
43              Properties config = new Properties();
44  
45              if (spLoggerInputStream != null) {
46                  config.load(spLoggerInputStream);
47                  String property = config.getProperty("interceptor.splogger.slow.query.duration");
48  
49                  if (property.matches("^\\d+$")) {
50                      slowQueryDuration = Integer.parseInt(property);
51                  } else {
52                      LOG.error("interceptor.splogger.slow.query.duration in spbee.properties is not an integer.");
53                  }
54              } else {
55                  LOG.info("spbee.properties doesn't exist. Using default value.");
56              }
57          } catch (IOException e) {
58              LOG.error("Failed to read spbee.properties. Using default value.");
59          } finally {
60              closeStream(spLoggerInputStream);
61          }
62  
63          SLOW_QUERY_DURATION = slowQueryDuration;
64      }
65  
66      /**
67       * Logs the invocation of the stored procedure call.
68       * This method will be called before the invocation of the stored procedure.
69       *
70       * @param spName    the name of the stored procedure
71       * @param arguments the arguments of the stored procedure
72       * @return the ID object to identify the call in the {@link #after} method
73       */
74      public static Object before(String spName, Object... arguments) {
75          long id = ID.getAndIncrement();
76          idTimestampMap.put(id, System.currentTimeMillis());
77  
78          if (LOG.isTraceEnabled()) {
79              LOG.trace(getSpInfo(id, spName, arguments));
80          }
81  
82          return id;
83      }
84  
85      /**
86       * Logs the execution duration of the stored procedure call.
87       * This method will be called after the invocation of the stored procedure.
88       *
89       * @param id        the ID object
90       * @param spName    the name of the stored procedure
91       * @param arguments the arguments of the stored procedure
92       */
93      public static void after(Object id, String spName, Object... arguments) {
94          long executionTime = System.currentTimeMillis() - idTimestampMap.get(id);
95  
96          if (LOG.isTraceEnabled()) {
97              LOG.trace(getSpInfoWithExecutionTime(id, spName, executionTime, arguments));
98          }
99  
100         if (executionTime > SLOW_QUERY_DURATION) {
101             LOG.warn(getSpInfoWithExecutionTime(id, spName, executionTime, arguments));
102         }
103 
104         idTimestampMap.remove(id);
105     }
106 
107     private static String getSpInfoWithExecutionTime(Object id, String spName, long executionTime, Object[] arguments) {
108         return executionTime + " ms " + getSpInfo(id, spName, arguments);
109     }
110 
111     private static String getSpInfo(Object id, String spName, Object[] arguments) {
112         return spName + Arrays.toString(arguments) + "#" + id;
113     }
114 
115     private static void closeStream(InputStream spLoggerInputStream) {
116         if (spLoggerInputStream != null) {
117             try {
118                 spLoggerInputStream.close();
119             } catch (IOException e) {
120                 LOG.error("Failed to close spLoggerInputStream.", e);
121             }
122         }
123     }
124 
125     private SpLogger() {
126     }
127 
128 }