View Javadoc
1   package edu.internet2.middleware.grouper.util;
2   
3   import java.util.Collections;
4   import java.util.HashMap;
5   import java.util.LinkedHashMap;
6   import java.util.Map;
7   
8   import org.apache.commons.logging.Log;
9   
10  import edu.internet2.middleware.grouper.cfg.GrouperConfig;
11  
12  public class PerformanceLogger {
13  
14    public PerformanceLogger() {
15    }
16    
17    /** logger */
18    private static final Log LOG = GrouperUtil.getLog(PerformanceLogger.class);
19  
20    /**
21     * performance timing of a group create
22     */
23    private static Map<String, ThreadLocal<Map<String, Object>>> performanceTiming = Collections.synchronizedMap(new HashMap<String, ThreadLocal<Map<String, Object>>>());
24      
25    /**
26     * get performance timing map
27     * @param label
28     * @param createIfNotThere
29     * @return the map
30     */
31    private static Map<String, Object> performanceTimingMap(String label, boolean createIfNotThere, boolean multiThreaded) {
32      ThreadLocal<Map<String, Object>> threadLocal = performanceTimingThreadLocal(label);
33      Map<String, Object> result = threadLocal.get();
34      if (result == null && createIfNotThere) {
35        result = new LinkedHashMap<String, Object>();
36        if (multiThreaded) {
37          result = Collections.synchronizedMap(result);
38        }
39        threadLocal.set(result);
40      }
41      return result;
42    }
43    
44    /**
45     * get a reference to this logger so the log config happens in one place
46     * @return the logger
47     */
48    public static Log performanceLog() {
49      return LOG;
50    }
51  
52    /**
53     * add a performance timer
54     * @param label in config and refer to this
55     * @param multiThreaded
56     */
57    public static void performanceTimingStart(String label, boolean multiThreaded) {
58      if (performanceTimingEnabled(label)) {
59        performanceTimingMap(label, true, multiThreaded);
60        performanceTimingData(label, "performanceLogFor", label);
61        performanceTimingData(label, "startNanos", System.nanoTime());
62      } else {
63        performanceTimingDelete(label);
64      }
65    }
66  
67    /**
68     * thread local for this label
69     * @param label
70     * @return the threadlocal
71     */
72    private static ThreadLocal<Map<String, Object>> performanceTimingThreadLocal(String label) {
73      ThreadLocal<Map<String, Object>> threadLocal = performanceTiming.get(label);
74      if (threadLocal == null) {
75        synchronized (PerformanceLogger.class) {
76          threadLocal = performanceTiming.get(label);
77          if (threadLocal == null) {
78            threadLocal = new InheritableThreadLocal<Map<String, Object>>();
79            performanceTiming.put(label, threadLocal);
80          }        
81        }
82      }
83      return threadLocal;
84    }
85    
86    /**
87     * add a performance timer
88     * @param label in config and refer to this
89     */
90    public static void performanceTimingDelete(String label) {
91      ThreadLocal<Map<String, Object>> threadLocal = performanceTimingThreadLocal(label);
92      threadLocal.remove();
93    }
94  
95    /**
96     * add a performance gate.  This measures from start until now, not a duration
97     * @param key something that will end up having ElapsedMs on end
98     */
99    public static void performanceTimingGate(String label, String key) {
100     try {
101       Map<String, Object> performanceMap = performanceTimingMap(label, false, false);
102       if (performanceMap == null) {
103         return;
104       }
105       Long startNanos = (Long)performanceMap.get("startNanos");
106       if (startNanos == null) {
107         throw new RuntimeException("Performance timeer is not started!");
108       }
109       String newKey = key + "_elapsedMs";
110       String keyCount = key + "_count";
111       
112       Long originalMillis = (Long)performanceMap.get(newKey);
113       Integer count = (Integer)performanceMap.get(keyCount);
114       
115       if (originalMillis != null) {
116         if (count == null) {
117           count = 1;
118         }
119         count++;
120         performanceMap.put(keyCount, count);
121         // this is just as something passes, so we cant measure duration...  just get a count and use the first one
122         return;
123         
124       }
125       originalMillis = (System.nanoTime()-startNanos)/1000000;
126       
127       performanceMap.put(newKey, originalMillis);
128     } catch (Exception e) {
129       LOG.error("Error with " + key, e);
130     }
131       
132   }
133   
134   /**
135    * add a performance gate for all performance timers, for a duration from start to finish.  Cannot overlap.  Will count all and keep a count if more than 1
136    * @param key something that will end up having _durationMs on end
137    * @param durationNanos
138    */
139   public static void performanceTimingAllDuration(String key, long durationNanos) {
140     
141     try {
142       for (String label : performanceTiming.keySet()) {
143         
144         performanceTimingDuration(label, key, durationNanos);
145         
146       }
147     } catch (Exception e) {
148       LOG.error("Error with " + key, e);
149     }
150   }
151 
152   /**
153    * use this for performance log label for sql queries
154    */
155   public static final String PERFORMANCE_LOG_LABEL_SQL = "sqlQueries";
156 
157   
158   /**
159    * add a performance gate for a duration from start to finish.  Cannot overlap.  Will count all and keep a count if more than 1
160    * @param durationNanos something that will end up having ElapsedMs on end
161    */
162   public static void performanceTimingDuration(String label, String key, long durationNanos) {
163     try {
164       Map<String, Object> performanceMap = performanceTimingMap(label, false, false);
165       if (performanceMap == null) {
166         return;
167       }
168       
169       String durationKey = key + "_durationNanos";
170       String keyCount = key + "_count";
171       
172       Long currentDuration = (Long)performanceMap.get(durationKey);
173       Integer count = (Integer)performanceMap.get(keyCount);
174   
175       if (count != null) {
176         count++;
177         performanceMap.put(keyCount, count);
178       } else if (currentDuration != null) {
179         count = 2;
180         performanceMap.put(keyCount, count);
181       }
182   
183       if (currentDuration != null) {
184         durationNanos += currentDuration;
185       }
186       
187       performanceMap.put(durationKey, durationNanos);
188     } catch (Exception e) {
189       LOG.error("Error with " + key, e);
190     }
191       
192   }
193   
194   /**
195    * add a performance gate
196    * @param key
197    * @param value
198    */
199   public static void performanceTimingData(String label, String key, Object value) {
200     try {
201       Map<String, Object> performanceMap = performanceTimingMap(label, false, false);
202       if (performanceMap == null) {
203         return;
204       }
205       performanceMap.put(key, value);
206     } catch (Exception e) {
207       LOG.error("Error with " + label + ", " +  key, e);
208     }
209   }
210 
211   /**
212    * see if enabled
213    * @param label
214    * @return ture if enabled
215    */
216   public static boolean performanceTimingEnabled(String label) {
217     boolean performanceLog = GrouperConfig.retrieveConfig().propertyValueBoolean("grouper.log.performance.info.on." + label, false);
218     return performanceLog;
219   }
220   
221   /**
222    * add a performance gate
223    * @param key
224    * @param value
225    */
226   public static void performanceTimingDataRemoveKey(String label, String key) {
227     try {
228       Map<String, Object> performanceMap = performanceTimingMap(label, false, false);
229       if (performanceMap == null) {
230         return;
231       }
232       performanceMap.remove(key);
233     } catch (Exception e) {
234       LOG.error("Error with " + label + ", " + key, e);
235     }
236   }
237   
238   /**
239    * performance string
240    * @param label
241    */
242   public static String performanceTimingDataResult(String label) {
243     try {
244       Map<String, Object> performanceMap = performanceTimingMap(label, false, false);
245       if (performanceMap == null) {
246         return null;
247       }
248       PerformanceLogger.performanceTimingGate(label, "took");
249       performanceTimingDataRemoveKey(label, "startNanos");
250       
251       // take out nanos and put in millis
252       Map<String, Object> performanceMapNew = new LinkedHashMap<String, Object>();
253       for (String key : performanceMap.keySet()) {
254         Object value = performanceMap.get(key);
255         if (value instanceof Long && key.endsWith("_durationNanos")) {
256           Long valueLong = (Long)value;
257           key = GrouperUtil.prefixOrSuffix(key, "_durationNanos", true) + "_durationMs";
258           valueLong /= 1000000;
259           value = valueLong;
260         }
261         performanceMapNew.put(key, value);
262       }
263       
264       String result = GrouperUtil.mapToString(performanceMapNew);
265       PerformanceLogger.performanceTimingDelete(label);
266       return result;
267     } catch (Exception e) {
268       LOG.error("Error with " + label, e);
269       return null;
270     }
271 
272   }
273 
274 
275   
276 }