LogUtil.java
/*
* Copyright Hyperledger Besu Contributors.
*
* Licensed 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.util.log;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
/** Utility class for logging. */
public class LogUtil {
static ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
static final String BESU_NAMESPACE = "org.hyperledger.besu";
static final int MAX_SUMMARY_DEPTH = 20;
/**
* Throttles logging to a given logger.
*
* @param logger logger as a String consumer
* @param logMessage message to log
* @param shouldLog AtomicBoolean to track whether the message should be logged
* @param logRepeatDelay delay in seconds between repeated logs
*/
public static void throttledLog(
final Consumer<String> logger,
final String logMessage,
final AtomicBoolean shouldLog,
final int logRepeatDelay) {
if (shouldLog.compareAndSet(true, false)) {
logger.accept(logMessage);
final Runnable runnable = () -> shouldLog.set(true);
executor.schedule(runnable, logRepeatDelay, TimeUnit.SECONDS);
}
}
/**
* Summarizes the stack trace of a throwable to the first class in the namespace. Useful for
* limiting exceptionally deep stack traces to the last relevant point in besu code.
*
* @param contextMessage message to prepend to the summary
* @param throwable exception to summarize
* @param namespace namespace to summarize to
* @return summary of the StackTrace
*/
public static String summarizeStackTrace(
final String contextMessage, final Throwable throwable, final String namespace) {
StackTraceElement[] stackTraceElements = throwable.getStackTrace();
List<String> stackTraceSummary = new ArrayList<>();
int depth = 0;
for (StackTraceElement element : stackTraceElements) {
stackTraceSummary.add(String.format("\tat: %s", element));
if (element.getClassName().startsWith(namespace) || ++depth >= MAX_SUMMARY_DEPTH) {
break;
}
}
return String.format(
"%s\nThrowable summary: %s\n%s",
contextMessage, throwable, String.join("\n", stackTraceSummary));
}
/**
* Summarizes the stack trace of a throwable to the first besu class in the namespace. Useful for
* limiting exceptionally deep stack traces to the last relevant point in besu code.
*
* @param contextMessage message to prepend to the summary
* @param throwable exception to summarize
* @return summary of the StackTrace
*/
public static String summarizeBesuStackTrace(
final String contextMessage, final Throwable throwable) {
return summarizeStackTrace(contextMessage, throwable, BESU_NAMESPACE);
}
}