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 static org.apache.juneau.commons.utils.CollectionUtils.*;
020import static org.apache.juneau.commons.utils.Utils.*;
021
022import java.lang.reflect.*;
023import java.util.*;
024import java.util.concurrent.atomic.*;
025
026import org.apache.juneau.*;
027import org.apache.juneau.commons.collections.*;
028import org.apache.juneau.cp.*;
029
030/**
031 * Method execution statistics.
032 *
033 * Keeps track of number of starts/finishes on tasks and keeps an average run time.
034 *
035 * <h5 class='section'>See Also:</h5><ul>
036 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/ExecutionStatistics">REST method execution statistics</a>
037 * </ul>
038 */
039public class MethodExecStats {
040   /**
041    * Builder class.
042    */
043   public static class Builder extends BeanBuilder<MethodExecStats> {
044
045      Method method;
046      ThrownStore thrownStore;
047
048      /**
049       * Constructor.
050       *
051       * @param beanStore The bean store to use for creating beans.
052       */
053      protected Builder(BeanStore beanStore) {
054         super(MethodExecStats.class, beanStore);
055      }
056
057      @Override /* Overridden from BeanBuilder */
058      public Builder impl(Object value) {
059         super.impl(value);
060         return this;
061      }
062
063      /**
064       * Specifies the Java method.
065       *
066       * @param value The new value for this setting.
067       * @return This object.
068       */
069      public Builder method(Method value) {
070         method = value;
071         return this;
072      }
073
074      /**
075       * Specifies the thrown store for tracking exceptions.
076       *
077       * @param value The new value for this setting.
078       * @return This object.
079       */
080      public Builder thrownStore(ThrownStore value) {
081         thrownStore = value;
082         return this;
083      }
084
085      @Override /* Overridden from BeanBuilder */
086      public Builder type(Class<?> value) {
087         super.type(value);
088         return this;
089      }
090
091      @Override /* Overridden from BeanBuilder */
092      protected MethodExecStats buildDefault() {
093         return new MethodExecStats(this);
094      }
095   }
096
097   /**
098    * Static creator.
099    *
100    * @param beanStore The bean store to use for creating beans.
101    * @return A new builder for this object.
102    */
103   public static Builder create(BeanStore beanStore) {
104      return new Builder(beanStore);
105   }
106
107   private final long guid;
108   private final Method method;
109   private final ThrownStore thrownStore;
110
111   private final AtomicInteger maxTime = new AtomicInteger();
112   private final AtomicInteger minTime = new AtomicInteger(-1);
113
114   private AtomicInteger starts = new AtomicInteger(), finishes = new AtomicInteger(), errors = new AtomicInteger();
115
116   private AtomicLong totalTime = new AtomicLong();
117
118   /**
119    * Constructor.
120    *
121    * @param builder The builder for this object.
122    */
123   protected MethodExecStats(Builder builder) {
124      this.guid = new Random().nextLong();
125      this.method = builder.method;
126      this.thrownStore = nn(builder.thrownStore) ? builder.thrownStore : new ThrownStore();
127   }
128
129   /**
130    * Call when an error occurs.
131    *
132    * @param e The exception thrown.  Can be <jk>null</jk>.
133    * @return This object.
134    */
135   public MethodExecStats error(Throwable e) {
136      errors.incrementAndGet();
137      thrownStore.add(e);
138      return this;
139   }
140
141   /**
142    * Call when task is finished.
143    *
144    * @param nanoTime The execution time of the task in nanoseconds.
145    * @return This object.
146    */
147   public MethodExecStats finished(long nanoTime) {
148      finishes.incrementAndGet();
149      int milliTime = (int)(nanoTime / 1_000_000);
150      totalTime.addAndGet(nanoTime);
151      int currentMin = minTime.get();
152      if (currentMin == -1) {
153         minTime.compareAndSet(-1, milliTime);
154      } else {
155         minTime.updateAndGet(x -> Math.min(x, milliTime));
156      }
157      maxTime.updateAndGet(x -> Math.max(x, milliTime));
158      return this;
159   }
160
161   /**
162    * Returns the average execution time.
163    *
164    * @return The average execution time in milliseconds.
165    */
166   public int getAvgTime() {
167      int runs = finishes.get();
168      return runs == 0 ? 0 : (int)(getTotalTime() / runs);
169   }
170
171   /**
172    * Returns the number of times the {@link #error(Throwable)} method was called.
173    *
174    * @return The number of times the {@link #error(Throwable)} method was called.
175    */
176   public int getErrors() { return errors.get(); }
177
178   /**
179    * Returns a globally unique ID for this object.
180    *
181    * <p>
182    * A random long generated during the creation of this object.
183    * Allows this object to be differentiated from other similar objects in multi-node environments so that
184    * statistics can be reliably stored in a centralized location.
185    *
186    * @return The globally unique ID for this object.
187    */
188   public long getGuid() { return guid; }
189
190   /**
191    * Returns the max execution time.
192    *
193    * @return The average execution time in milliseconds.
194    */
195   public int getMaxTime() { return maxTime.get(); }
196
197   /**
198    * Returns the method name of these stats.
199    *
200    * @return The method name of these stats.
201    */
202   public Method getMethod() { return method; }
203
204   /**
205    * Returns the max execution time.
206    *
207    * @return The average execution time in milliseconds.
208    */
209   public int getMinTime() { 
210      int value = minTime.get();
211      return value == -1 ? 0 : value;
212   }
213
214   /**
215    * Returns the number currently running method invocations.
216    *
217    * @return The number of currently running method invocations.
218    */
219   public int getRunning() { return starts.get() - finishes.get(); }
220
221   /**
222    * Returns the number of times the {@link #started()} method was called.
223    *
224    * @return The number of times the {@link #started()} method was called.
225    */
226   public int getRuns() { return starts.get(); }
227
228   /**
229    * Returns information on all stack traces of all exceptions encountered.
230    *
231    * @return Information on all stack traces of all exceptions encountered.
232    */
233   public ThrownStore getThrownStore() { return thrownStore; }
234
235   /**
236    * Returns the total execution time.
237    *
238    * @return The total execution time in milliseconds.
239    */
240   public long getTotalTime() { return totalTime.get() / 1_000_000; }
241
242   /**
243    * Call when task is started.
244    *
245    * @return This object.
246    */
247   public MethodExecStats started() {
248      starts.incrementAndGet();
249      return this;
250   }
251
252   protected FluentMap<String,Object> properties() {
253      // @formatter:off
254      return filteredBeanPropertyMap()
255         .a("avgTime", getAvgTime())
256         .a("errors", getErrors())
257         .a("guid", guid)
258         .a("maxTime", getMaxTime())
259         .a("method", method)
260         .a("minTime", getMinTime())
261         .a("running", getRunning())
262         .a("runs", getRuns())
263         .a("thrownStore", thrownStore)
264         .a("totalTime", getTotalTime());
265      // @formatter:on
266   }
267
268   @Override /* Overridden from Object */
269   public String toString() {
270      return r(properties());
271   }
272}