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