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