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