mirror of https://github.com/hyperledger/besu
info level log for pipeline abort (#6459)
* info level log for pipeline abort * add depth protection Signed-off-by: garyschulte <garyschulte@gmail.com>pull/6500/head
parent
6d77d58006
commit
8972d6707c
@ -1,39 +0,0 @@ |
||||
/* |
||||
* 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.ethereum.util; |
||||
|
||||
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; |
||||
|
||||
public class LogUtil { |
||||
static ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); |
||||
|
||||
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); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,92 @@ |
||||
/* |
||||
* 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); |
||||
} |
||||
} |
@ -0,0 +1,127 @@ |
||||
/* |
||||
* 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 static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
import java.util.Optional; |
||||
import java.util.function.Supplier; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
public class LogUtilTest { |
||||
|
||||
static final String exceptionMessage = "test message"; |
||||
static final String contextMessage = "context message"; |
||||
|
||||
@Test |
||||
public void testMessageSummarizeStackTrace() { |
||||
Throwable throwable = new Throwable(exceptionMessage); |
||||
String namespace = "org.hyperledger.besu"; |
||||
String result = LogUtil.summarizeStackTrace(contextMessage, throwable, namespace); |
||||
var lines = |
||||
assertFoundInTrace(result, LogUtil.BESU_NAMESPACE, contextMessage, exceptionMessage, 1); |
||||
assertThat(lines.size()).isEqualTo(3); |
||||
} |
||||
|
||||
@Test |
||||
public void testSummarizeStackTraceToArbitrary() { |
||||
Throwable throwable = new Throwable("test message"); |
||||
String namespace = "java.lang"; |
||||
String result = LogUtil.summarizeStackTrace(contextMessage, throwable, namespace); |
||||
var lines = assertFoundInTrace(result, namespace, contextMessage, exceptionMessage, 1); |
||||
assertThat(lines.size()).isGreaterThan(3); |
||||
} |
||||
|
||||
@Test |
||||
public void testSummarizeLambdaStackTrace() { |
||||
String result = ""; |
||||
try { |
||||
Supplier<Object> lambda = () -> besuStackHelper(null); |
||||
assertThat(lambda.get()).isNotNull(); |
||||
} catch (Exception e) { |
||||
String namespace = "org.hyperledger.besu"; |
||||
result = LogUtil.summarizeStackTrace(contextMessage, e, namespace); |
||||
} |
||||
var lines = |
||||
assertFoundInTrace( |
||||
result, LogUtil.BESU_NAMESPACE, contextMessage, "NullPointerException", 1); |
||||
assertThat(lines).hasSize(5); |
||||
} |
||||
|
||||
@Test |
||||
public void assertSummarizeStopsAtFirstBesu() { |
||||
String result = ""; |
||||
try { |
||||
Supplier<Object> lambda = () -> besuStackHelper((null)); |
||||
Supplier<Object> lambda2 = lambda::get; |
||||
assertThat(lambda2.get()).isNotNull(); |
||||
} catch (Exception e) { |
||||
result = LogUtil.summarizeBesuStackTrace("besuSummary", e); |
||||
} |
||||
var lines = |
||||
assertFoundInTrace( |
||||
result, LogUtil.BESU_NAMESPACE, "besuSummary", "NullPointerException", 1); |
||||
assertThat(lines).hasSize(5); |
||||
assertThat(lines.get(0)).contains("besuSummary"); |
||||
} |
||||
|
||||
@Test |
||||
public void assertMaxDepth() { |
||||
String result = ""; |
||||
try { |
||||
recurseTimesAndThrow(30); |
||||
} catch (Exception ex) { |
||||
result = LogUtil.summarizeStackTrace("besuSummary", ex, "java.lang"); |
||||
} |
||||
System.out.println(result); |
||||
List<String> lines = Arrays.asList(result.split("\n")); |
||||
assertThat(lines).hasSize(22); |
||||
assertThat(lines.get(0)).contains("besuSummary"); |
||||
} |
||||
|
||||
private List<String> assertFoundInTrace( |
||||
final String result, |
||||
final String namespace, |
||||
final String ctxMessage, |
||||
final String excMessage, |
||||
final int times) { |
||||
System.out.println(result); |
||||
assertThat(result).containsOnlyOnce(ctxMessage); |
||||
assertThat(result).containsOnlyOnce(excMessage); |
||||
|
||||
List<String> lines = Arrays.asList(result.split("\n")); |
||||
assertThat( |
||||
lines.stream() |
||||
.filter(s -> s.contains("at: ")) |
||||
.filter(s -> s.contains(namespace)) |
||||
.count()) |
||||
.isEqualTo(times); |
||||
return lines; |
||||
} |
||||
|
||||
private void recurseTimesAndThrow(final int times) { |
||||
if (times < 1) { |
||||
throw new RuntimeException("FakeStackOverflowError"); |
||||
} |
||||
recurseTimesAndThrow(times - 1); |
||||
} |
||||
|
||||
private Optional<Object> besuStackHelper(final Object o) { |
||||
return Optional.of(o); |
||||
} |
||||
} |
Loading…
Reference in new issue