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}