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 static java.util.stream.Collectors.*; 016import java.lang.reflect.*; 017import java.util.*; 018import java.util.concurrent.*; 019import org.apache.juneau.*; 020import org.apache.juneau.cp.*; 021import org.apache.juneau.internal.*; 022 023/** 024 * Method execution statistics database. 025 * 026 * <p> 027 * Used for tracking basic call statistics on Java methods. 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 MethodExecStore { 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 * Static creator. 051 * 052 * @return A new builder for this object. 053 */ 054 public static Builder create() { 055 return new Builder(BeanStore.INSTANCE); 056 } 057 058 //----------------------------------------------------------------------------------------------------------------- 059 // Builder 060 //----------------------------------------------------------------------------------------------------------------- 061 062 /** 063 * Builder class. 064 */ 065 @FluentSetters 066 public static class Builder extends BeanBuilder<MethodExecStore> { 067 068 ThrownStore thrownStore; 069 Class<? extends MethodExecStats> statsImplClass; 070 071 /** 072 * Constructor. 073 * 074 * @param beanStore The bean store to use for creating beans. 075 */ 076 protected Builder(BeanStore beanStore) { 077 super(MethodExecStore.class, beanStore); 078 } 079 080 @Override /* BeanBuilder */ 081 protected MethodExecStore buildDefault() { 082 return new MethodExecStore(this); 083 } 084 085 //------------------------------------------------------------------------------------------------------------- 086 // Properties 087 //------------------------------------------------------------------------------------------------------------- 088 089 /** 090 * Specifies a subclass of {@link MethodExecStats} to use for individual method statistics. 091 * 092 * @param value The new value for this setting. 093 * @return This object. 094 */ 095 public Builder statsImplClass(Class<? extends MethodExecStats> value) { 096 statsImplClass = value; 097 return this; 098 } 099 100 /** 101 * Specifies the store to use for gathering statistics on thrown exceptions. 102 * 103 * <p> 104 * Can be used to capture thrown exception stats across multiple {@link MethodExecStore} objects. 105 * 106 * <p> 107 * If not specified, one will be created by default for the {@link MethodExecStore} object. 108 * 109 * @param value The store to use for gathering statistics on thrown exceptions. 110 * @return This object. 111 */ 112 public Builder thrownStore(ThrownStore value) { 113 thrownStore = value; 114 return this; 115 } 116 117 /** 118 * Same as {@link #thrownStore(ThrownStore)} but only sets the new value if the current value is <jk>null</jk>. 119 * 120 * @param value The new value for this setting. 121 * @return This object. 122 */ 123 public Builder thrownStoreOnce(ThrownStore value) { 124 if (thrownStore == null) 125 thrownStore = value; 126 return this; 127 } 128 129 // <FluentSetters> 130 131 @Override /* GENERATED - org.apache.juneau.BeanBuilder */ 132 public Builder impl(Object value) { 133 super.impl(value); 134 return this; 135 } 136 137 @Override /* GENERATED - org.apache.juneau.BeanBuilder */ 138 public Builder type(Class<?> value) { 139 super.type(value); 140 return this; 141 } 142 143 // </FluentSetters> 144 } 145 146 //----------------------------------------------------------------------------------------------------------------- 147 // Instance 148 //----------------------------------------------------------------------------------------------------------------- 149 150 private final ThrownStore thrownStore; 151 private final BeanStore beanStore; 152 private final Class<? extends MethodExecStats> statsImplClass; 153 private final ConcurrentHashMap<Method,MethodExecStats> db = new ConcurrentHashMap<>(); 154 155 /** 156 * Constructor. 157 * 158 * @param builder The store to use for storing thrown exception statistics. 159 */ 160 protected MethodExecStore(Builder builder) { 161 this.beanStore = builder.beanStore(); 162 this.thrownStore = builder.thrownStore != null ? builder.thrownStore : beanStore.getBean(ThrownStore.class).orElseGet(ThrownStore::new); 163 this.statsImplClass = builder.statsImplClass; 164 } 165 166 /** 167 * Returns the statistics for the specified method. 168 * 169 * <p> 170 * Creates a new stats object if one has not already been created. 171 * 172 * @param m The method to return the statistics for. 173 * @return The statistics for the specified method. Never <jk>null</jk>. 174 */ 175 public MethodExecStats getStats(Method m) { 176 MethodExecStats stats = db.get(m); 177 if (stats == null) { 178 stats = MethodExecStats 179 .create(beanStore) 180 .type(statsImplClass) 181 .method(m) 182 .thrownStore(ThrownStore.create(beanStore).parent(thrownStore).build()) 183 .build(); 184 db.putIfAbsent(m, stats); 185 stats = db.get(m); 186 } 187 return stats; 188 } 189 190 /** 191 * Returns all the statistics in this store. 192 * 193 * @return All the statistics in this store. 194 */ 195 public Collection<MethodExecStats> getStats() { 196 return db.values(); 197 } 198 199 /** 200 * Returns timing information on all method executions on this class. 201 * 202 * @return A list of timing statistics ordered by average execution time descending. 203 */ 204 public List<MethodExecStats> getStatsByTotalTime() { 205 return getStats().stream().sorted(Comparator.comparingLong(MethodExecStats::getTotalTime).reversed()).collect(toList()); 206 } 207 208 /** 209 * Returns the timing information returned by {@link #getStatsByTotalTime()} in a readable format. 210 * 211 * @return A report of all method execution times ordered by . 212 */ 213 public String getReport() { 214 StringBuilder sb = new StringBuilder() 215 .append(" Method Runs Running Errors Avg Total \n") 216 .append("------------------------------ --------- --------- -------- ------------ -----------\n"); 217 getStatsByTotalTime() 218 .stream() 219 .sorted(Comparator.comparingDouble(MethodExecStats::getTotalTime).reversed()) 220 .forEach(x -> sb.append(String.format("%30s %9d %9d %9d %10dms %10dms\n", x.getMethod(), x.getRuns(), x.getRunning(), x.getErrors(), x.getAvgTime(), x.getTotalTime()))); 221 return sb.toString(); 222 223 } 224 225 /** 226 * Returns the thrown exception store being used by this store. 227 * 228 * @return The thrown exception store being used by this store. 229 */ 230 public ThrownStore getThrownStore() { 231 return thrownStore; 232 } 233}