View Javadoc
1   /*
2    * Licensed under the GPL License. You may not use this file except in compliance with the License.
3    * You may obtain a copy of the License at
4    *
5    *   https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
6    *
7    * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
8    * WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
9    * PURPOSE.
10   */
11  package psiprobe.beans.stats.collectors;
12  
13  import jakarta.inject.Inject;
14  import jakarta.servlet.ServletContext;
15  
16  import org.apache.catalina.Context;
17  import org.slf4j.Logger;
18  import org.slf4j.LoggerFactory;
19  import org.springframework.beans.factory.annotation.Value;
20  import org.springframework.web.context.ServletContextAware;
21  
22  import psiprobe.TomcatContainer;
23  import psiprobe.beans.ContainerWrapperBean;
24  import psiprobe.model.Application;
25  import psiprobe.tools.ApplicationUtils;
26  import psiprobe.tools.TimeExpression;
27  
28  /**
29   * Collects application statistics.
30   */
31  public class AppStatsCollectorBean extends AbstractStatsCollectorBean
32      implements ServletContextAware {
33  
34    /** The Constant logger. */
35    private static final Logger logger = LoggerFactory.getLogger(AppStatsCollectorBean.class);
36  
37    /** The container wrapper. */
38    @Inject
39    private ContainerWrapperBean containerWrapper;
40  
41    /** The servlet context. */
42    @Inject
43    private ServletContext servletContext;
44  
45    /** The self ignored. */
46    private boolean selfIgnored;
47  
48    /**
49     * Gets the container wrapper.
50     *
51     * @return the container wrapper
52     */
53    public ContainerWrapperBean getContainerWrapper() {
54      return containerWrapper;
55    }
56  
57    /**
58     * Sets the container wrapper.
59     *
60     * @param containerWrapper the new container wrapper
61     */
62    public void setContainerWrapper(ContainerWrapperBean containerWrapper) {
63      this.containerWrapper = containerWrapper;
64    }
65  
66    /**
67     * Checks if is self ignored.
68     *
69     * @return true, if is self ignored
70     */
71    public boolean isSelfIgnored() {
72      return selfIgnored;
73    }
74  
75    /**
76     * Sets the self ignored.
77     *
78     * @param selfIgnored the new self ignored
79     */
80    @Value("${psiprobe.beans.stats.collectors.app.selfIgnored}")
81    public void setSelfIgnored(boolean selfIgnored) {
82      this.selfIgnored = selfIgnored;
83    }
84  
85    /**
86     * Gets the servlet context.
87     *
88     * @return the servlet context
89     */
90    protected ServletContext getServletContext() {
91      return servletContext;
92    }
93  
94    @Override
95    public void setServletContext(ServletContext servletContext) {
96      this.servletContext = servletContext;
97    }
98  
99    @Override
100   public void collect() throws InterruptedException {
101 
102     long currentTime = System.currentTimeMillis();
103 
104     if (containerWrapper == null) {
105       logger.error("Cannot collect application stats. Container wrapper is not set.");
106     } else {
107       TomcatContainer tomcatContainer = getContainerWrapper().getTomcatContainer();
108 
109       // check if the containerWtapper has been initialized
110       if (tomcatContainer != null) {
111         long totalReqDelta = 0;
112         long totalErrDelta = 0;
113         long totalAvgProcTime = 0;
114         int participatingAppCount = 0;
115 
116         for (Context ctx : tomcatContainer.findContexts()) {
117           if (ctx != null && ctx.getName() != null) {
118             Application app = new Application();
119             ApplicationUtils.collectApplicationServletStats(ctx, app);
120 
121             String appName = ctx.getName().isEmpty() ? "/" : ctx.getName();
122 
123             long reqDelta =
124                 buildDeltaStats("app.requests." + appName, app.getRequestCount(), currentTime);
125             long errDelta = buildDeltaStats("app.errors." + appName, app.getErrorCount());
126             long procTimeDelta =
127                 buildDeltaStats("app.proc_time." + appName, app.getProcessingTime(), currentTime);
128 
129             long avgProcTime = reqDelta == 0 ? 0 : procTimeDelta / reqDelta;
130             buildAbsoluteStats("app.avg_proc_time." + appName, avgProcTime, currentTime);
131 
132             /*
133              * make sure applications that did not serve any requests do not participate in average
134              * response time equation thus diluting the value
135              */
136             if (reqDelta > 0 && !excludeFromTotal(ctx)) {
137               totalReqDelta += reqDelta;
138               totalErrDelta += errDelta;
139               totalAvgProcTime += avgProcTime;
140               participatingAppCount++;
141             }
142           }
143         }
144         // build totals for all applications
145         buildAbsoluteStats("total.requests", totalReqDelta, currentTime);
146         buildAbsoluteStats("total.errors", totalErrDelta, currentTime);
147         buildAbsoluteStats("total.avg_proc_time",
148             participatingAppCount == 0 ? 0 : totalAvgProcTime / participatingAppCount, currentTime);
149       }
150       logger.debug("app stats collected in {}ms", System.currentTimeMillis() - currentTime);
151     }
152   }
153 
154   /**
155    * Exclude from total.
156    *
157    * @param ctx the ctx
158    *
159    * @return true, if successful
160    */
161   private boolean excludeFromTotal(Context ctx) {
162     return isSelfIgnored() && getServletContext().equals(ctx.getServletContext());
163   }
164 
165   /**
166    * Reset.
167    */
168   public void reset() {
169     if (containerWrapper == null) {
170       logger.error("Cannot reset application stats. Container wrapper is not set.");
171     } else {
172       TomcatContainer tomcatContainer = getContainerWrapper().getTomcatContainer();
173       if (tomcatContainer != null) {
174         for (Context ctx : tomcatContainer.findContexts()) {
175           if (ctx != null && ctx.getName() != null) {
176             String appName = ctx.getName().isEmpty() ? "/" : ctx.getName();
177             reset(appName);
178           }
179         }
180       }
181     }
182     resetStats("total.requests");
183     resetStats("total.errors");
184     resetStats("total.avg_proc_time");
185   }
186 
187   /**
188    * Reset.
189    *
190    * @param appName the app name
191    */
192   public void reset(String appName) {
193     resetStats("app.requests." + appName);
194     resetStats("app.proc_time." + appName);
195     resetStats("app.errors." + appName);
196     resetStats("app.avg_proc_time." + appName);
197   }
198 
199   /**
200    * Sets the max series expression.
201    *
202    * @param period the period
203    * @param span the span
204    */
205   public void setMaxSeries(@Value("${psiprobe.beans.stats.collectors.app.period}") long period,
206       @Value("${psiprobe.beans.stats.collectors.app.span}") long span) {
207     super.setMaxSeries((int) TimeExpression.dataPoints(period, span));
208   }
209 
210 }