001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.rest.stats;
014
015import java.lang.reflect.*;
016import java.util.*;
017import java.util.concurrent.atomic.*;
018
019import org.apache.juneau.*;
020import org.apache.juneau.cp.*;
021import org.apache.juneau.internal.*;
022import org.apache.juneau.marshaller.*;
023
024/**
025 * Method execution statistics.
026 *
027 * Keeps track of number of starts/finishes on tasks and keeps an average run time.
028 *
029 * <h5 class='section'>See Also:</h5><ul>
030 *    <li class='link'><a class="doclink" href="../../../../../index.html#jrs.ExecutionStatistics">REST method execution statistics</a>
031 * </ul>
032 */
033public class MethodExecStats {
034
035   //-----------------------------------------------------------------------------------------------------------------
036   // Static
037   //-----------------------------------------------------------------------------------------------------------------
038
039   /**
040    * Static creator.
041    *
042    * @param beanStore The bean store to use for creating beans.
043    * @return A new builder for this object.
044    */
045   public static Builder create(BeanStore beanStore) {
046      return new Builder(beanStore);
047   }
048
049   //-----------------------------------------------------------------------------------------------------------------
050   // Builder
051   //-----------------------------------------------------------------------------------------------------------------
052
053   /**
054    * Builder class.
055    */
056   @FluentSetters
057   public static class Builder extends BeanBuilder<MethodExecStats> {
058
059      Method method;
060      ThrownStore thrownStore;
061
062      /**
063       * Constructor.
064       *
065       * @param beanStore The bean store to use for creating beans.
066       */
067      protected Builder(BeanStore beanStore) {
068         super(MethodExecStats.class, beanStore);
069      }
070
071      @Override /* BeanBuilder */
072      protected MethodExecStats buildDefault() {
073         return new MethodExecStats(this);
074      }
075
076      //-------------------------------------------------------------------------------------------------------------
077      // Properties
078      //-------------------------------------------------------------------------------------------------------------
079
080      /**
081       * Specifies the Java method.
082       *
083       * @param value The new value for this setting.
084       * @return This object.
085       */
086      @FluentSetter
087      public Builder method(Method value) {
088         method = value;
089         return this;
090      }
091
092      /**
093       * Specifies the thrown store for tracking exceptions.
094       *
095       * @param value The new value for this setting.
096       * @return This object.
097       */
098      @FluentSetter
099      public Builder thrownStore(ThrownStore value) {
100         thrownStore = value;
101         return this;
102      }
103
104      // <FluentSetters>
105
106      @Override /* GENERATED - org.apache.juneau.BeanBuilder */
107      public Builder impl(Object value) {
108         super.impl(value);
109         return this;
110      }
111
112      @Override /* GENERATED - org.apache.juneau.BeanBuilder */
113      public Builder type(Class<?> value) {
114         super.type(value);
115         return this;
116      }
117
118      // </FluentSetters>
119   }
120
121   //-----------------------------------------------------------------------------------------------------------------
122   // Instance
123   //-----------------------------------------------------------------------------------------------------------------
124
125   private final long guid;
126   private final Method method;
127   private final ThrownStore thrownStore;
128
129   private volatile int minTime = -1, maxTime;
130
131   private AtomicInteger
132      starts = new AtomicInteger(),
133      finishes = new AtomicInteger(),
134      errors = new AtomicInteger();
135
136   private AtomicLong
137      totalTime = new AtomicLong();
138
139   /**
140    * Constructor.
141    *
142    * @param builder The builder for this object.
143    */
144   protected MethodExecStats(Builder builder) {
145      this.guid = new Random().nextLong();
146      this.method = builder.method;
147      this.thrownStore = builder.thrownStore != null ? builder.thrownStore : new ThrownStore();
148   }
149
150
151   /**
152    * Call when task is started.
153    *
154    * @return This object.
155    */
156   public MethodExecStats started() {
157      starts.incrementAndGet();
158      return this;
159   }
160
161   /**
162    * Call when task is finished.
163    *
164    * @param nanoTime The execution time of the task in nanoseconds.
165    * @return This object.
166    */
167   public MethodExecStats finished(long nanoTime) {
168      finishes.incrementAndGet();
169      int milliTime = (int)(nanoTime/1_000_000);
170      totalTime.addAndGet(nanoTime);
171      minTime = minTime == -1 ? milliTime : Math.min(minTime, milliTime);
172      maxTime = Math.max(maxTime, milliTime);
173      return this;
174   }
175
176   /**
177    * Call when an error occurs.
178    *
179    * @param e The exception thrown.  Can be <jk>null</jk>.
180    * @return This object.
181    */
182   public MethodExecStats error(Throwable e) {
183      errors.incrementAndGet();
184      thrownStore.add(e);
185      return this;
186   }
187
188   /**
189    * Returns a globally unique ID for this object.
190    *
191    * <p>
192    * A random long generated during the creation of this object.
193    * Allows this object to be differentiated from other similar objects in multi-node environments so that
194    * statistics can be reliably stored in a centralized location.
195    *
196    * @return The globally unique ID for this object.
197    */
198   public long getGuid() {
199      return guid;
200   }
201
202   /**
203    * Returns the method name of these stats.
204    *
205    * @return The method name of these stats.
206    */
207   public Method getMethod() {
208      return method;
209   }
210
211   /**
212    * Returns the number of times the {@link #started()} method was called.
213    *
214    * @return The number of times the {@link #started()} method was called.
215    */
216   public int getRuns() {
217      return starts.get();
218   }
219
220   /**
221    * Returns the number currently running method invocations.
222    *
223    * @return The number of currently running method invocations.
224    */
225   public int getRunning() {
226      return starts.get() - finishes.get();
227   }
228
229   /**
230    * Returns the number of times the {@link #error(Throwable)} method was called.
231    *
232    * @return The number of times the {@link #error(Throwable)} method was called.
233    */
234   public int getErrors() {
235      return errors.get();
236   }
237
238   /**
239    * Returns the max execution time.
240    *
241    * @return The average execution time in milliseconds.
242    */
243   public int getMinTime() {
244      return minTime == -1 ? 0 : minTime;
245   }
246
247   /**
248    * Returns the max execution time.
249    *
250    * @return The average execution time in milliseconds.
251    */
252   public int getMaxTime() {
253      return maxTime;
254   }
255
256   /**
257    * Returns the average execution time.
258    *
259    * @return The average execution time in milliseconds.
260    */
261   public int getAvgTime() {
262      int runs = finishes.get();
263      return runs == 0 ? 0 : (int)(getTotalTime() / runs);
264   }
265
266   /**
267    * Returns the total execution time.
268    *
269    * @return The total execution time in milliseconds.
270    */
271   public long getTotalTime() {
272      return totalTime.get() / 1_000_000;
273   }
274
275   /**
276    * Returns information on all stack traces of all exceptions encountered.
277    *
278    * @return Information on all stack traces of all exceptions encountered.
279    */
280   public ThrownStore getThrownStore() {
281      return thrownStore;
282   }
283
284   @Override /* Object */
285   public String toString() {
286      return Json5.of(this);
287   }
288}