blob: 2e54e22f338ea0b6ee2863c106edc8f1865c4d3a [file] [log] [blame]
// ***************************************************************************************************************************
// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file *
// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file *
// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance *
// * with the License. You may obtain a copy of the License at *
// * *
// * http://www.apache.org/licenses/LICENSE-2.0 *
// * *
// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an *
// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
// * specific language governing permissions and limitations under the License. *
// ***************************************************************************************************************************
package org.apache.juneau.utils;
import static org.apache.juneau.utils.StackTraceUtils.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.*;
/**
* An in-memory cache of stack traces.
*
* <p>
* Used for preventing duplication of stack traces in log files and replacing them with small hashes.
*/
public class StackTraceDatabase {
private final ConcurrentHashMap<Integer,StackTraceInfo> db = new ConcurrentHashMap<>();
private final String stopClass;
private final long cacheTimeout;
/**
* Constructor.
*/
public StackTraceDatabase() {
this(-1, null);
}
/**
* Constructor.
*
* @param cacheTimeout
* The amount of time in milliseconds to cache stack trace info in this database before discarding.
* <br>If <c>-1</c>, never discard.
* @param stopClass
* When this class is encountered in a stack trace, stop calculating the hash.
* <br>Can be <jk>null</jk>.
*/
public StackTraceDatabase(long cacheTimeout, Class<?> stopClass) {
this.stopClass = stopClass == null ? "" : stopClass.getName();
this.cacheTimeout = cacheTimeout;
}
/**
* Adds the specified throwable to this database.
*
* @param e The exception to add.
* @return This object (for method chaining).
*/
public StackTraceDatabase add(Throwable e) {
find(e).increment();
return this;
}
/**
* Retrieves the stack trace information for the specified exception.
*
* @param e The exception.
* @return A clone of the stack trace info, never <jk>null</jk>.
*/
public StackTraceInfo getStackTraceInfo(Throwable e) {
return find(e).clone();
}
/**
* Clears out the stack trace cache.
*/
public void reset() {
db.clear();
}
/**
* Returns the list of all stack traces in this database.
*
* @return The list of all stack traces in this database, cloned and sorted by count descending.
*/
public List<StackTraceInfo> getClonedStackTraceInfos() {
return db.values().stream().map(x -> x.clone()).sorted().collect(Collectors.toList());
}
private StackTraceInfo find(Throwable e) {
int hash = hash(e, stopClass);
StackTraceInfo stc = db.get(hash);
long time = System.currentTimeMillis();
if (stc != null && stc.timeout > time) {
return stc;
}
String n = e == null ? null : e.getClass().getSimpleName();
long t = cacheTimeout == -1 ? Long.MAX_VALUE : time + cacheTimeout;
stc = new StackTraceInfo(n, t, hash);
db.put(hash, stc);
return db.get(hash);
}
}