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.util.*; 023import java.util.concurrent.atomic.*; 024 025import org.apache.juneau.commons.collections.*; 026import org.apache.juneau.cp.*; 027 028/** 029 * Represents an entry in {@link ThrownStore}. 030 * 031 * <h5 class='section'>See Also:</h5><ul> 032 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/ExecutionStatistics">REST method execution statistics</a> 033 * </ul> 034 */ 035public class ThrownStats implements Cloneable { 036 /** 037 * Builder class. 038 */ 039 public static class Builder { 040 041 final BeanStore beanStore; 042 Throwable throwable; 043 long hash; 044 List<String> stackTrace; 045 ThrownStats causedBy; 046 047 BeanCreator<ThrownStats> creator; 048 049 /** 050 * Constructor. 051 * 052 * @param beanStore The bean store to use for creating beans. 053 */ 054 protected Builder(BeanStore beanStore) { 055 this.beanStore = beanStore; 056 this.creator = beanStore.createBean(ThrownStats.class).builder(Builder.class, this); 057 } 058 059 /** 060 * Create a new {@link ThrownStats} using this builder. 061 * 062 * @return A new {@link ThrownStats} 063 */ 064 public ThrownStats build() { 065 return creator.run(); 066 } 067 068 /** 069 * Specifies the caused-by exception. 070 * 071 * @param value The new value for this setting. 072 * @return This object. 073 */ 074 public Builder causedBy(ThrownStats value) { 075 causedBy = value; 076 return this; 077 } 078 079 /** 080 * Specifies the calculated hash. 081 * 082 * @param value The new value for this setting. 083 * @return This object. 084 */ 085 public Builder hash(long value) { 086 hash = value; 087 return this; 088 } 089 090 /** 091 * Specifies the normalized stacktrace. 092 * 093 * @param value The new value for this setting. 094 * @return This object. 095 */ 096 public Builder stackTrace(List<String> value) { 097 stackTrace = value; 098 return this; 099 } 100 101 /** 102 * Specifies the thrown exception. 103 * 104 * @param value The new value for this setting. 105 * @return This object. 106 */ 107 public Builder throwable(Throwable value) { 108 throwable = value; 109 return this; 110 } 111 112 /** 113 * Specifies a subclass of {@link ThrownStats} to create when the {@link #build()} method is called. 114 * 115 * @param value The new value for this setting. 116 * @return This object. 117 */ 118 public Builder type(Class<? extends ThrownStats> value) { 119 creator.type(value == null ? ThrownStats.class : value); 120 return this; 121 } 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 long guid; 135 private final long hash; 136 private final Class<?> thrownClass; 137 private final String firstMessage; 138 private final List<String> stackTrace; 139 private final Optional<ThrownStats> causedBy; 140 141 private final AtomicInteger count; 142 private final AtomicLong firstOccurrence, lastOccurrence; 143 144 /** 145 * Copy constructor. 146 */ 147 private ThrownStats(ThrownStats x) { 148 this.guid = x.guid; 149 this.thrownClass = x.thrownClass; 150 this.firstMessage = x.firstMessage; 151 this.stackTrace = u(copyOf(x.stackTrace)); 152 this.causedBy = opt(x.causedBy.isPresent() ? x.causedBy.get().clone() : null); 153 this.hash = x.hash; 154 this.count = new AtomicInteger(x.count.get()); 155 this.firstOccurrence = new AtomicLong(x.firstOccurrence.get()); 156 this.lastOccurrence = new AtomicLong(x.lastOccurrence.get()); 157 } 158 159 /** 160 * Constructor. 161 * 162 * @param builder The builder for this object. 163 */ 164 protected ThrownStats(Builder builder) { 165 this.guid = new Random().nextLong(); 166 this.thrownClass = builder.throwable.getClass(); 167 this.firstMessage = builder.throwable.getMessage(); 168 this.stackTrace = u(copyOf(builder.stackTrace)); 169 this.causedBy = opt(builder.causedBy); 170 this.hash = builder.hash; 171 this.count = new AtomicInteger(0); 172 long ct = System.currentTimeMillis(); 173 this.firstOccurrence = new AtomicLong(ct); 174 this.lastOccurrence = new AtomicLong(ct); 175 } 176 177 @Override /* Overridden from Object */ 178 public ThrownStats clone() { 179 return new ThrownStats(this); 180 } 181 182 /** 183 * Returns the stats on the caused-by exception. 184 * 185 * @return The stats on the caused-by exception, never <jk>null</jk>. 186 */ 187 public Optional<ThrownStats> getCausedBy() { return causedBy; } 188 189 /** 190 * Returns the number of times this exception occurred at a specific location in code. 191 * 192 * @return The number of times this exception occurred at a specific location in code. 193 */ 194 public int getCount() { return count.intValue(); } 195 196 /** 197 * Returns the message of the first exception at a specific location in code. 198 * 199 * @return The message of the first exception at a specific location in code. 200 */ 201 public String getFirstMessage() { return firstMessage; } 202 203 /** 204 * Returns the UTC time of the first occurrence of this exception at a specific location in code. 205 * 206 * @return The UTC time of the first occurrence of this exception at a specific location in code. 207 */ 208 public long getFirstOccurrence() { return firstOccurrence.longValue(); } 209 210 /** 211 * Returns a globally unique ID for this object. 212 * 213 * <p> 214 * A random long generated during the creation of this object. 215 * Allows this object to be differentiated from other similar objects in multi-node environments so that 216 * statistics can be reliably stored in a centralized location. 217 * 218 * @return The globally unique ID for this object. 219 */ 220 public long getGuid() { return guid; } 221 222 /** 223 * Returns a hash of this exception that can typically be used to uniquely identify it. 224 * 225 * @return A hash of this exception. 226 */ 227 public long getHash() { return hash; } 228 229 /** 230 * Returns the UTC time of the last occurrence of this exception at a specific location in code. 231 * 232 * @return The UTC time of the last occurrence of this exception at a specific location in code. 233 */ 234 public long getLastOccurrence() { return lastOccurrence.longValue(); } 235 236 /** 237 * Returns the stack trace of the first exception at a specific location in code. 238 * 239 * @return The stack trace of the first exception at a specific location in code. 240 */ 241 public List<String> getStackTrace() { return stackTrace; } 242 243 /** 244 * Returns the exception class. 245 * 246 * @return The exception class. 247 */ 248 public Class<?> getThrownClass() { return thrownClass; } 249 250 /** 251 * Increments the occurrence count of this exception. 252 * 253 * @return This object. 254 */ 255 public ThrownStats increment() { 256 count.incrementAndGet(); 257 lastOccurrence.set(System.currentTimeMillis()); 258 causedBy.ifPresent(ThrownStats::increment); 259 return this; 260 } 261 262 protected FluentMap<String,Object> properties() { 263 // @formatter:off 264 return filteredBeanPropertyMap() 265 .a("causedBy", causedBy.orElse(null)) 266 .a("count", getCount()) 267 .a("firstMessage", firstMessage) 268 .a("firstOccurrence", getFirstOccurrence()) 269 .a("guid", guid) 270 .a("hash", hash) 271 .a("lastOccurrence", getLastOccurrence()) 272 .a("stackTrace", stackTrace) 273 .a("thrownClass", thrownClass); 274 // @formatter:on 275 } 276 277 @Override /* Overridden from Object */ 278 public String toString() { 279 return r(properties()); 280 } 281}