View Javadoc
1   /**
2    * This file Copyright (c) 2013-2018 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.debug;
35  
36  import info.magnolia.context.MgnlContext;
37  
38  import java.util.HashMap;
39  import java.util.Map;
40  
41  /**
42   * Invocation aggregator.
43   */
44  public class TrackingStatus {
45  
46      private static final String ATTRIBUTE_NAME = TrackingStatus.class.getName();
47  
48      private final Map<String, Long>[] traces = new Map[10];
49  
50      private final boolean trace;
51      private final Object testString;
52  
53      /**
54       * Gets instance of the status configured for current request or new one if none is configured
55       * yet.
56       */
57      public static TrackingStatus getInstance() {
58          return getInstance(true);
59      }
60  
61      /**
62       * Gets instance of the status configured for current request or new one if none is configured
63       * yet.
64       *
65       * @param trace used only for the very first invocation to enable/disable this status instance.
66       */
67      public static TrackingStatus getInstance(boolean trace) {
68          return getInstance(true, null);
69      }
70  
71      /**
72       * Gets instance of the status configured for current request or new one if none is configured
73       * yet.
74       *
75       * @param trace used only for the very first invocation to enable/disable this status instance.
76       * @param testString used to further filter the calls to trace. Only calls to
77       * {@link #track(String)} matching the {@link #testString} will be logged.
78       */
79      public static TrackingStatus getInstance(boolean trace, String testString) {
80          if (!MgnlContext.hasAttribute(ATTRIBUTE_NAME)) {
81              MgnlContext.setAttribute(ATTRIBUTE_NAME, new TrackingStatus(trace, testString));
82          }
83          return (TrackingStatus) MgnlContext.getAttribute(ATTRIBUTE_NAME);
84      }
85  
86      public TrackingStatus(boolean trace, String testString) {
87          for (int i = 0; i < traces.length; i++) {
88              traces[i] = new HashMap<String, Long>();
89          }
90          this.trace = trace;
91          this.testString = testString;
92      }
93  
94      /**
95       * Track all the callers and update aggregated stacktrace.
96       */
97      public void track() {
98          if (!trace) {
99              return;
100         }
101         StackTraceElement[] trace = new Exception().getStackTrace();
102         int count = 0;
103         for (StackTraceElement el : trace) {
104             String strTrace = el.toString();
105             Map<String, Long> traceMap = traces[count];
106             Long sum = traceMap.get(strTrace);
107             if (sum != null) {
108                 sum += 1;
109             } else {
110                 sum = 1L;
111             }
112             traceMap.put(strTrace, sum);
113             count++;
114             if (count >= traces.length) {
115                 break;
116             }
117         }
118     }
119 
120     @Override
121     /**
122      * Generates aggregated tracktrace from all captured calls including count of invocations.
123      */
124     public String toString() {
125         StringBuilder sb = new StringBuilder();
126         if (totalInits > 0) {
127             sb.append("inits: ").append(totalInits).append(",");
128         }
129         if (totalCalls > 0) {
130             sb.append(", total calls: ").append(totalCalls).append(",");
131 
132         }
133         if (totalTime > 0) {
134             sb.append("total time: ").append(totalTime);
135 
136         }
137         sb.append("\n");
138         String ind = "  ";
139         for (Map<String, Long> trace : traces) {
140             for (Map.Entry<String, Long> entry : trace.entrySet()) {
141                 sb.append(ind).append(entry.getValue()).append(";").append(entry.getKey()).append("\n");
142             }
143             ind += "  ";
144         }
145         return sb.toString();
146     }
147 
148     public long totalCalls;
149     public long totalInits;
150     public long totalTime;
151 
152     /**
153      * Track only calls where {@link #testString} matches the argument.
154      */
155     public void track(String str) {
156         if (testString == null || testString.equals(str)) {
157             track();
158         }
159     }
160 }