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