From 49f32ca22d1bccb7eea2686daa59c028ee02b812 Mon Sep 17 00:00:00 2001 From: Daniel Lehrner Date: Tue, 22 Nov 2022 14:41:06 +0100 Subject: [PATCH] Prints configuration overview (#4451) * print configuration overview at startup Signed-off-by: Daniel Lehrner --- CHANGELOG.md | 1 + besu/build.gradle | 1 + .../org/hyperledger/besu/cli/BesuCommand.java | 54 +++++-- .../cli/ConfigurationOverviewBuilder.java | 140 ++++++++++++++++++ .../besu/cli/NetworkDeprecationMessage.java | 45 ++---- .../besu/cli/config/NetworkName.java | 2 +- .../options/stable/DataStorageOptions.java | 5 + .../hyperledger/besu/cli/BesuCommandTest.java | 2 +- .../cli/ConfigurationOverviewBuilderTest.java | 118 +++++++++++++++ .../cli/NetworkDeprecationMessageTest.java | 2 +- ethereum/eth/build.gradle | 1 + .../besu/ethereum/eth/sync/SyncMode.java | 11 ++ .../ethereum/p2p/peers/StaticNodesParser.java | 8 +- gradle/check-licenses.gradle | 3 +- gradle/verification-metadata.xml | 21 +++ gradle/versions.gradle | 2 + .../storage/rocksdb/RocksDBPlugin.java | 4 + .../configuration/RocksDBCLIOptions.java | 4 + util/build.gradle | 1 + .../besu/util/log/FramedLogMessage.java | 72 +++++++++ .../besu/util/platform/PlatformDetector.java | 55 +++++++ 21 files changed, 500 insertions(+), 52 deletions(-) create mode 100644 besu/src/main/java/org/hyperledger/besu/cli/ConfigurationOverviewBuilder.java create mode 100644 besu/src/test/java/org/hyperledger/besu/cli/ConfigurationOverviewBuilderTest.java create mode 100644 util/src/main/java/org/hyperledger/besu/util/log/FramedLogMessage.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ea127394d..2f8f150b80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - Shanghai implementation of EIP-3540 and EIP-3670 Ethereum Object Format and Code Validation [#4644](https://github.com/hyperledger/besu/pull/4644) - Remove some log statements that are keeping some objects live in heap for a long time, to reduce the amount of memory required during initial sync [#4705](https://github.com/hyperledger/besu/pull/4705) - Add field `type` to Transaction receipt object (eth_getTransactionReceipt) [#4505](https://github.com/hyperledger/besu/issues/4505) +- Print an overview of configuration and system information at startup [#4451](https://github.com/hyperledger/besu/pull/4451) ### Bug Fixes diff --git a/besu/build.gradle b/besu/build.gradle index 5b2e621180..05aa98f182 100644 --- a/besu/build.gradle +++ b/besu/build.gradle @@ -62,6 +62,7 @@ dependencies { implementation 'com.fasterxml.jackson.core:jackson-databind' implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8' + implementation 'com.github.oshi:oshi-core' implementation 'com.google.guava:guava' implementation 'com.graphql-java:graphql-java' implementation 'info.picocli:picocli' diff --git a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java index 9369d9d77b..eba9ae7bf2 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -317,6 +317,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable { private final PkiBlockCreationConfigurationProvider pkiBlockCreationConfigProvider; private GenesisConfigOptions genesisConfigOptions; + private RocksDBPlugin rocksDBPlugin; + // CLI options defined by user at runtime. // Options parsing is done with CLI library Picocli https://picocli.info/ @@ -1414,13 +1416,14 @@ public class BesuCommand implements DefaultCommandValues, Runnable { setMergeConfigOptions(); instantiateSignatureAlgorithmFactory(); - configureNativeLibs(); - logger.info("Starting Besu version: {}", BesuInfo.nodeName(identityString)); + + logger.info("Starting Besu"); // Need to create vertx after cmdline has been parsed, such that metricsSystem is configurable vertx = createVertx(createVertxOptions(metricsSystem.get())); validateOptions(); configure(); + configureNativeLibs(); initController(); besuPluginContext.beforeExternalServices(); @@ -1544,7 +1547,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable { besuPluginContext.addService(RpcEndpointService.class, rpcEndpointServiceImpl); // register built-in plugins - new RocksDBPlugin().register(besuPluginContext); + rocksDBPlugin = new RocksDBPlugin(); + rocksDBPlugin.register(besuPluginContext); new InMemoryStoragePlugin().register(besuPluginContext); besuPluginContext.registerPlugins(pluginsDir()); @@ -1724,7 +1728,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable { private void configureNativeLibs() { if (unstableNativeLibraryOptions.getNativeAltbn128() && AbstractAltBnPrecompiledContract.isNative()) { - logger.info("Using LibEthPairings native alt bn128"); + logger.info("Using the native implementation of alt bn128"); } else { AbstractAltBnPrecompiledContract.disableNative(); logger.info("Using the Java implementation of alt bn128"); @@ -2014,8 +2018,6 @@ public class BesuCommand implements DefaultCommandValues, Runnable { permissioningConfiguration = permissioningConfiguration(); staticNodes = loadStaticNodes(); - logger.info("Connecting to {} static nodes.", staticNodes.size()); - logger.trace("Static Nodes = {}", staticNodes); final List enodeURIs = ethNetworkConfig.getBootNodes(); permissioningConfiguration .flatMap(PermissioningConfiguration::getLocalConfig) @@ -2026,8 +2028,12 @@ public class BesuCommand implements DefaultCommandValues, Runnable { .ifPresent(p -> ensureAllNodesAreInAllowlist(staticNodes, p)); metricsConfiguration = metricsConfiguration(); - logger.info("Security Module: {}", securityModuleName); instantiateSignatureAlgorithmFactory(); + + logger.info(generateConfigurationOverview()); + logger.info("Connecting to {} static nodes.", staticNodes.size()); + logger.trace("Static Nodes = {}", staticNodes); + logger.info("Security Module: {}", securityModuleName); } private JsonRpcIpcConfiguration jsonRpcIpcConfiguration( @@ -2186,7 +2192,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable { && java.nio.file.Files.exists(engineRPCOptionGroup.engineJwtKeyFile)) { engineConfig.setAuthenticationPublicKeyFile(engineRPCOptionGroup.engineJwtKeyFile.toFile()); } else { - logger.info( + logger.warn( "Engine API authentication enabled without key file. Expect ephemeral jwt.hex file in datadir"); } } @@ -3084,7 +3090,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable { final String staticNodesFilename = "static-nodes.json"; staticNodesPath = dataDir().resolve(staticNodesFilename); } - logger.info("Static Nodes file = {}", staticNodesPath); + logger.debug("Static Nodes file = {}", staticNodesPath); return StaticNodesParser.fromPath(staticNodesPath, getEnodeDnsConfiguration()); } @@ -3331,4 +3337,34 @@ public class BesuCommand implements DefaultCommandValues, Runnable { ? SyncMode.FAST : SyncMode.FULL); } + + private String generateConfigurationOverview() { + final ConfigurationOverviewBuilder builder = new ConfigurationOverviewBuilder(); + + if (network != null) { + builder.setNetwork(network.normalize()); + } + + builder + .setDataStorage(dataStorageOptions.normalizeDataStorageFormat()) + .setSyncMode(syncMode.normalize()); + + if (jsonRpcConfiguration != null && jsonRpcConfiguration.isEnabled()) { + builder + .setRpcPort(jsonRpcConfiguration.getPort()) + .setRpcHttpApis(jsonRpcConfiguration.getRpcApis()); + } + + if (engineJsonRpcConfiguration != null && engineJsonRpcConfiguration.isEnabled()) { + builder + .setEnginePort(engineJsonRpcConfiguration.getPort()) + .setEngineApis(engineJsonRpcConfiguration.getRpcApis()); + } + + if (rocksDBPlugin.isHighSpecEnabled()) { + builder.setHighSpecEnabled(); + } + + return builder.build(); + } } diff --git a/besu/src/main/java/org/hyperledger/besu/cli/ConfigurationOverviewBuilder.java b/besu/src/main/java/org/hyperledger/besu/cli/ConfigurationOverviewBuilder.java new file mode 100644 index 0000000000..ea1ccd5df3 --- /dev/null +++ b/besu/src/main/java/org/hyperledger/besu/cli/ConfigurationOverviewBuilder.java @@ -0,0 +1,140 @@ +/* + * 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.cli; + +import org.hyperledger.besu.BesuInfo; +import org.hyperledger.besu.util.log.FramedLogMessage; +import org.hyperledger.besu.util.platform.PlatformDetector; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import oshi.PlatformEnum; +import oshi.SystemInfo; +import oshi.hardware.HardwareAbstractionLayer; + +public class ConfigurationOverviewBuilder { + private String network; + private String dataStorage; + private String syncMode; + private Integer rpcPort; + private Collection rpcHttpApis; + private Integer enginePort; + private Collection engineApis; + private boolean isHighSpec = false; + + public ConfigurationOverviewBuilder setNetwork(final String network) { + this.network = network; + return this; + } + + public ConfigurationOverviewBuilder setDataStorage(final String dataStorage) { + this.dataStorage = dataStorage; + return this; + } + + public ConfigurationOverviewBuilder setSyncMode(final String syncMode) { + this.syncMode = syncMode; + return this; + } + + public ConfigurationOverviewBuilder setRpcPort(final Integer rpcPort) { + this.rpcPort = rpcPort; + return this; + } + + public ConfigurationOverviewBuilder setRpcHttpApis(final Collection rpcHttpApis) { + this.rpcHttpApis = rpcHttpApis; + return this; + } + + public ConfigurationOverviewBuilder setEnginePort(final Integer enginePort) { + this.enginePort = enginePort; + return this; + } + + public ConfigurationOverviewBuilder setEngineApis(final Collection engineApis) { + this.engineApis = engineApis; + return this; + } + + public ConfigurationOverviewBuilder setHighSpecEnabled() { + isHighSpec = true; + return this; + } + + public String build() { + final List lines = new ArrayList<>(); + lines.add("Besu " + BesuInfo.class.getPackage().getImplementationVersion()); + lines.add(""); + lines.add("Configuration:"); + + if (network != null) { + lines.add("Network: " + network); + } + + if (dataStorage != null) { + lines.add("Data storage: " + dataStorage); + } + + if (syncMode != null) { + lines.add("Sync mode: " + syncMode); + } + + if (rpcHttpApis != null) { + lines.add("RPC HTTP APIs: " + String.join(",", rpcHttpApis)); + } + if (rpcPort != null) { + lines.add("RPC HTTP port: " + rpcPort); + } + + if (engineApis != null) { + lines.add("Engine APIs: " + String.join(",", engineApis)); + } + if (enginePort != null) { + lines.add("Engine port: " + enginePort); + } + + if (isHighSpec) { + lines.add("High spec configuration enabled"); + } + + lines.add(""); + lines.add("Host:"); + + lines.add("Java: " + PlatformDetector.getVM()); + lines.add("Maximum heap size: " + normalizeSize(Runtime.getRuntime().maxMemory())); + lines.add("OS: " + PlatformDetector.getOS()); + + if (SystemInfo.getCurrentPlatform() == PlatformEnum.LINUX) { + final String glibcVersion = PlatformDetector.getGlibc(); + if (glibcVersion != null) { + lines.add("glibc: " + glibcVersion); + } + } + + final HardwareAbstractionLayer hardwareInfo = new SystemInfo().getHardware(); + + lines.add("Total memory: " + normalizeSize(hardwareInfo.getMemory().getTotal())); + lines.add("CPU cores: " + hardwareInfo.getProcessor().getLogicalProcessorCount()); + + return FramedLogMessage.generate(lines); + } + + private String normalizeSize(final long size) { + return String.format("%.02f", (double) (size) / 1024 / 1024 / 1024) + " GB"; + } +} diff --git a/besu/src/main/java/org/hyperledger/besu/cli/NetworkDeprecationMessage.java b/besu/src/main/java/org/hyperledger/besu/cli/NetworkDeprecationMessage.java index d8e5932952..cc771dd745 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/NetworkDeprecationMessage.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/NetworkDeprecationMessage.java @@ -15,49 +15,24 @@ package org.hyperledger.besu.cli; import org.hyperledger.besu.cli.config.NetworkName; +import org.hyperledger.besu.util.log.FramedLogMessage; -import org.apache.commons.lang3.StringUtils; +import java.util.List; public class NetworkDeprecationMessage { - private static final int MAX_LINE_LENGTH = 80; public static String generate(final NetworkName network) { if (network.getDeprecationDate().isEmpty()) { throw new AssertionError("Deprecation date is not set. Cannot print a deprecation message"); } - final StringBuilder messageBuilder = new StringBuilder("\n"); - messageBuilder - .append("#".repeat(MAX_LINE_LENGTH)) - .append(emptyLine()) - .append( - String.format( - "#%s#", - StringUtils.center( - deprecationDetails( - network.humanReadableNetworkName(), network.getDeprecationDate().get()), - MAX_LINE_LENGTH - 2))) - .append(emptyLine()) - .append( - String.format( - "#%s#\n", StringUtils.center("For more details please go to", MAX_LINE_LENGTH - 2))) - .append( - String.format( - "#%s#", - StringUtils.center( - "https://blog.ethereum.org/2022/06/21/testnet-deprecation/", - MAX_LINE_LENGTH - 2))) - .append(emptyLine()) - .append("#".repeat(MAX_LINE_LENGTH)); - - return messageBuilder.toString(); - } - - private static String deprecationDetails(final String networkName, final String deprecationDate) { - return networkName + " is deprecated and will be shutdown " + deprecationDate; - } - - private static String emptyLine() { - return String.format("\n#%s#\n", StringUtils.center("", MAX_LINE_LENGTH - 2)); + return FramedLogMessage.generateCentered( + List.of( + network.normalize() + + " is deprecated and will be shutdown " + + network.getDeprecationDate().get(), + "", + "For more details please go to", + "https://blog.ethereum.org/2022/06/21/testnet-deprecation/")); } } diff --git a/besu/src/main/java/org/hyperledger/besu/cli/config/NetworkName.java b/besu/src/main/java/org/hyperledger/besu/cli/config/NetworkName.java index b28d52de3b..550a5c073e 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/config/NetworkName.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/config/NetworkName.java @@ -76,7 +76,7 @@ public enum NetworkName { return canFastSync; } - public String humanReadableNetworkName() { + public String normalize() { return StringUtils.capitalize(name().toLowerCase()); } diff --git a/besu/src/main/java/org/hyperledger/besu/cli/options/stable/DataStorageOptions.java b/besu/src/main/java/org/hyperledger/besu/cli/options/stable/DataStorageOptions.java index 35c88ac92a..1a97bb8377 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/options/stable/DataStorageOptions.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/options/stable/DataStorageOptions.java @@ -26,6 +26,7 @@ import org.hyperledger.besu.ethereum.worldstate.ImmutableDataStorageConfiguratio import java.util.List; +import org.apache.commons.lang3.StringUtils; import picocli.CommandLine.Option; public class DataStorageOptions implements CLIOptions { @@ -85,4 +86,8 @@ public class DataStorageOptions implements CLIOptions BONSAI_STORAGE_FORMAT_USE_SNAPSHOTS, bonsaiUseSnapshots.toString()); } + + public String normalizeDataStorageFormat() { + return StringUtils.capitalize(dataStorageFormat.toString().toLowerCase()); + } } diff --git a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java index 302d6e3e50..47cf9e159b 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java @@ -5287,7 +5287,7 @@ public class BesuCommandTest extends CommandTestAbstract { verify(mockLogger).info("Using the native implementation of the signature algorithm"); assertThat(AbstractAltBnPrecompiledContract.isNative()).isTrue(); - verify(mockLogger).info("Using LibEthPairings native alt bn128"); + verify(mockLogger).info("Using the native implementation of alt bn128"); } @Test diff --git a/besu/src/test/java/org/hyperledger/besu/cli/ConfigurationOverviewBuilderTest.java b/besu/src/test/java/org/hyperledger/besu/cli/ConfigurationOverviewBuilderTest.java new file mode 100644 index 0000000000..46cac150d8 --- /dev/null +++ b/besu/src/test/java/org/hyperledger/besu/cli/ConfigurationOverviewBuilderTest.java @@ -0,0 +1,118 @@ +/* + * 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.cli; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.ArrayList; +import java.util.Collection; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class ConfigurationOverviewBuilderTest { + private ConfigurationOverviewBuilder builder; + + @BeforeEach + void setUp() { + builder = new ConfigurationOverviewBuilder(); + } + + @Test + void setNetwork() { + final String noNetworkSet = builder.build(); + assertThat(noNetworkSet).doesNotContain("Network:"); + + builder.setNetwork("foobar"); + final String networkSet = builder.build(); + assertThat(networkSet).contains("Network: foobar"); + } + + @Test + void setDataStorage() { + final String noDataStorageSet = builder.build(); + assertThat(noDataStorageSet).doesNotContain("Data storage:"); + + builder.setDataStorage("bonsai"); + final String dataStorageSet = builder.build(); + assertThat(dataStorageSet).contains("Data storage: bonsai"); + } + + @Test + void setSyncMode() { + final String noSyncModeSet = builder.build(); + assertThat(noSyncModeSet).doesNotContain("Sync mode:"); + + builder.setSyncMode("fast"); + final String syncModeSet = builder.build(); + assertThat(syncModeSet).contains("Sync mode: fast"); + } + + @Test + void setRpcPort() { + final String noRpcPortSet = builder.build(); + assertThat(noRpcPortSet).doesNotContain("RPC HTTP port:"); + + builder.setRpcPort(42); + final String rpcPortSet = builder.build(); + assertThat(rpcPortSet).contains("RPC HTTP port: 42"); + } + + @Test + void setRpcHttpApis() { + final String noRpcApisSet = builder.build(); + assertThat(noRpcApisSet).doesNotContain("RPC HTTP APIs:"); + + final Collection rpcApis = new ArrayList<>(); + rpcApis.add("api1"); + rpcApis.add("api2"); + builder.setRpcHttpApis(rpcApis); + final String rpcApisSet = builder.build(); + assertThat(rpcApisSet).contains("RPC HTTP APIs: api1,api2"); + } + + @Test + void setEnginePort() { + final String noEnginePortSet = builder.build(); + assertThat(noEnginePortSet).doesNotContain("Engine port:"); + + builder.setEnginePort(42); + final String enginePortSet = builder.build(); + assertThat(enginePortSet).contains("Engine port: 42"); + } + + @Test + void setEngineApis() { + final String noEngineApisSet = builder.build(); + assertThat(noEngineApisSet).doesNotContain("Engine APIs:"); + + final Collection engineApis = new ArrayList<>(); + engineApis.add("api1"); + engineApis.add("api2"); + builder.setEngineApis(engineApis); + final String engineApisSet = builder.build(); + assertThat(engineApisSet).contains("Engine APIs: api1,api2"); + } + + @Test + void setHighSpecEnabled() { + final String highSpecNotEnabled = builder.build(); + assertThat(highSpecNotEnabled).doesNotContain("High spec configuration enabled"); + + builder.setHighSpecEnabled(); + final String highSpecEnabled = builder.build(); + assertThat(highSpecEnabled).contains("High spec configuration enabled"); + } +} diff --git a/besu/src/test/java/org/hyperledger/besu/cli/NetworkDeprecationMessageTest.java b/besu/src/test/java/org/hyperledger/besu/cli/NetworkDeprecationMessageTest.java index a84c8853fe..fbd4a5c962 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/NetworkDeprecationMessageTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/NetworkDeprecationMessageTest.java @@ -30,7 +30,7 @@ class NetworkDeprecationMessageTest { names = {"RINKEBY", "ROPSTEN", "KILN"}) void shouldGenerateDeprecationMessageForDeprecatedNetworks(final NetworkName network) { assertThat(NetworkDeprecationMessage.generate(network)) - .contains(network.humanReadableNetworkName() + " is deprecated and will be shutdown"); + .contains(network.normalize() + " is deprecated and will be shutdown"); } @ParameterizedTest diff --git a/ethereum/eth/build.gradle b/ethereum/eth/build.gradle index 89544c4391..c34a969a98 100644 --- a/ethereum/eth/build.gradle +++ b/ethereum/eth/build.gradle @@ -54,6 +54,7 @@ dependencies { implementation 'com.google.guava:guava' implementation 'io.vertx:vertx-core' + implementation 'org.apache.commons:commons-lang3' implementation 'org.apache.tuweni:tuweni-bytes' implementation 'org.apache.tuweni:tuweni-units' implementation 'org.apache.tuweni:tuweni-rlp' diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/SyncMode.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/SyncMode.java index c3b8d336c4..fd3aa264b8 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/SyncMode.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/SyncMode.java @@ -16,6 +16,8 @@ package org.hyperledger.besu.ethereum.eth.sync; import java.util.EnumSet; +import org.apache.commons.lang3.StringUtils; + public enum SyncMode { // Fully validate all blocks as they sync FULL, @@ -26,6 +28,15 @@ public enum SyncMode { // Perform snapsync but starting from a checkpoint instead of starting from genesis X_CHECKPOINT; + public String normalize() { + if (this.toString().startsWith("X_")) { + // removes X_ at the beginning + return StringUtils.capitalize(this.toString().substring(2).toLowerCase()); + } + + return StringUtils.capitalize(this.toString().toLowerCase()); + } + public static boolean isFullSync(final SyncMode syncMode) { return !EnumSet.of(SyncMode.FAST, SyncMode.X_SNAP, SyncMode.X_CHECKPOINT).contains(syncMode); } diff --git a/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/peers/StaticNodesParser.java b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/peers/StaticNodesParser.java index ed5ad92f40..b825ec085d 100644 --- a/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/peers/StaticNodesParser.java +++ b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/peers/StaticNodesParser.java @@ -44,16 +44,16 @@ public class StaticNodesParser { try { return readEnodesFromPath(path, enodeDnsConfiguration); } catch (FileNotFoundException | NoSuchFileException ex) { - LOG.info("StaticNodes file {} does not exist, no static connections will be created.", path); + LOG.debug("StaticNodes file {} does not exist, no static connections will be created.", path); return emptySet(); } catch (IOException ex) { - LOG.info("Unable to parse static nodes file ({})", path); + LOG.warn("Unable to parse static nodes file ({})", path); throw ex; } catch (DecodeException ex) { - LOG.info("Content of ({}} was invalid json, and could not be decoded.", path); + LOG.warn("Content of ({}} was invalid json, and could not be decoded.", path); throw ex; } catch (IllegalArgumentException ex) { - LOG.info("Parsing ({}) has failed due incorrectly formatted enode element.", path); + LOG.warn("Parsing ({}) has failed due incorrectly formatted enode element.", path); throw ex; } } diff --git a/gradle/check-licenses.gradle b/gradle/check-licenses.gradle index c8fdbf6001..9bea3e148c 100644 --- a/gradle/check-licenses.gradle +++ b/gradle/check-licenses.gradle @@ -132,6 +132,7 @@ downloadLicenses { 'MIT', 'The MIT License', 'The MIT License (MIT)', + 'SPDX-License-Identifier: MIT', ], ] licenses = [ @@ -199,7 +200,7 @@ task checkLicenses { if (!acceptedLicenses.contains((license.@name).toLowerCase())) { def depStrings = [] license.dependency.each { depStrings << it.text() } - bads = bads + depStrings + " => ${license.@name} \n" + bads = bads + depStrings + " => ${license.@name} \n" } } if (bads != "") { diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 7927c5824f..27d9858673 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -516,6 +516,19 @@ + + + + + + + + + + + + + @@ -2661,6 +2674,14 @@ + + + + + + + + diff --git a/gradle/versions.gradle b/gradle/versions.gradle index ea4742c9f4..6c38563c10 100644 --- a/gradle/versions.gradle +++ b/gradle/versions.gradle @@ -206,5 +206,7 @@ dependencyManagement { dependency 'org.yaml:snakeyaml:1.32' dependency 'tech.pegasys.discovery:discovery:22.2.0' + + dependency 'com.github.oshi:oshi-core:6.2.2' } } diff --git a/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/RocksDBPlugin.java b/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/RocksDBPlugin.java index bc9231a10f..d7a62b27af 100644 --- a/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/RocksDBPlugin.java +++ b/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/RocksDBPlugin.java @@ -95,6 +95,10 @@ public class RocksDBPlugin implements BesuPlugin { } } + public boolean isHighSpecEnabled() { + return options.isHighSpec(); + } + private void createAndRegister(final StorageService service) { final List segments = service.getAllSegmentIdentifiers(); diff --git a/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/configuration/RocksDBCLIOptions.java b/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/configuration/RocksDBCLIOptions.java index 230b48db33..71faf5dff3 100644 --- a/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/configuration/RocksDBCLIOptions.java +++ b/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/configuration/RocksDBCLIOptions.java @@ -94,6 +94,10 @@ public class RocksDBCLIOptions { maxOpenFiles, maxBackgroundCompactions, backgroundThreadCount, cacheCapacity, isHighSpec); } + public boolean isHighSpec() { + return isHighSpec; + } + @Override public String toString() { return MoreObjects.toStringHelper(this) diff --git a/util/build.gradle b/util/build.gradle index 48ba0dca57..76ec3358e2 100644 --- a/util/build.gradle +++ b/util/build.gradle @@ -32,6 +32,7 @@ dependencies { api 'org.slf4j:slf4j-api' implementation 'com.google.guava:guava' + implementation 'org.apache.commons:commons-lang3' implementation 'org.apache.logging.log4j:log4j-core' implementation 'org.apache.logging.log4j:log4j-slf4j-impl' implementation 'org.xerial.snappy:snappy-java' diff --git a/util/src/main/java/org/hyperledger/besu/util/log/FramedLogMessage.java b/util/src/main/java/org/hyperledger/besu/util/log/FramedLogMessage.java new file mode 100644 index 0000000000..d28c908352 --- /dev/null +++ b/util/src/main/java/org/hyperledger/besu/util/log/FramedLogMessage.java @@ -0,0 +1,72 @@ +/* + * 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.List; + +import com.google.common.base.Splitter; +import org.apache.commons.lang3.StringUtils; + +public class FramedLogMessage { + private static final int MAX_LINE_LENGTH = 100; + + private FramedLogMessage() {} + + public static String generate(final List logLines) { + final StringBuilder builder = new StringBuilder("\n"); + appendHeader(builder); + + logLines.forEach( + logLine -> + Splitter.fixedLength(76) + .split(logLine) + .forEach( + splitLogLine -> + builder.append( + String.format( + "# %s #\n", + StringUtils.rightPad(splitLogLine, MAX_LINE_LENGTH - 4))))); + + appendFooter(builder); + + return builder.toString(); + } + + public static String generateCentered(final List logLines) { + final StringBuilder builder = new StringBuilder("\n"); + appendHeader(builder); + + logLines.forEach( + logLine -> + builder.append( + String.format("#%s#\n", StringUtils.center(logLine, MAX_LINE_LENGTH - 2)))); + + appendFooter(builder); + + return builder.toString(); + } + + private static void appendHeader(final StringBuilder builder) { + builder.append("#".repeat(MAX_LINE_LENGTH) + "\n").append(emptyLine()); + } + + private static void appendFooter(final StringBuilder builder) { + builder.append(emptyLine()).append("#".repeat(MAX_LINE_LENGTH)); + } + + private static String emptyLine() { + return String.format("#%s#\n", StringUtils.center("", MAX_LINE_LENGTH - 2)); + } +} diff --git a/util/src/main/java/org/hyperledger/besu/util/platform/PlatformDetector.java b/util/src/main/java/org/hyperledger/besu/util/platform/PlatformDetector.java index bcaae99ced..3c320db933 100644 --- a/util/src/main/java/org/hyperledger/besu/util/platform/PlatformDetector.java +++ b/util/src/main/java/org/hyperledger/besu/util/platform/PlatformDetector.java @@ -14,7 +14,14 @@ */ package org.hyperledger.besu.util.platform; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.Charset; import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * Detects OS and VMs. @@ -28,6 +35,7 @@ public class PlatformDetector { private static String _osType; private static String _vm; private static String _arch; + private static String _glibc; public static String getOSType() { if (_osType == null) { @@ -57,6 +65,14 @@ public class PlatformDetector { return _vm; } + public static String getGlibc() { + if (_glibc == null) { + detectGlibc(); + } + + return _glibc; + } + private static final String UNKNOWN = "unknown"; private static void detect() { @@ -213,4 +229,43 @@ public class PlatformDetector { } return System.getProperty(value).toLowerCase(Locale.US).replaceAll("[^a-z0-9]+", ""); } + + private static void detectGlibc() { + final ProcessBuilder processBuilder = + new ProcessBuilder("/bin/bash").command("/usr/bin/ldd", "--version"); + processBuilder.redirectErrorStream(true); + + final StringBuilder rawGlibcVersionBuilder; + try { + final Process process = processBuilder.start(); + rawGlibcVersionBuilder = readGlibcVersionStream(process.getInputStream()); + } catch (IOException e) { + return; + } + + _glibc = normalizeGLibcVersion(rawGlibcVersionBuilder.toString()); + } + + private static StringBuilder readGlibcVersionStream(final InputStream iStream) + throws IOException { + final StringBuilder builder = new StringBuilder(); + String line; + + try (BufferedReader bufferedReader = + new BufferedReader(new InputStreamReader(iStream, Charset.defaultCharset()))) { + while ((line = bufferedReader.readLine()) != null) { + builder.append(line); + builder.append(System.getProperty("line.separator")); + } + } + + return builder; + } + + private static String normalizeGLibcVersion(final String rawGlibcVersion) { + final Pattern pattern = Pattern.compile("[-+]?[0-9]*\\.?[0-9]+"); + final Matcher matcher = pattern.matcher(rawGlibcVersion); + + return matcher.find() ? matcher.group() : null; + } }