Skip to content

Commit

Permalink
feat: centralize configuration logging (#501)
Browse files Browse the repository at this point in the history
Signed-off-by: Matt Peterson <[email protected]>
  • Loading branch information
mattp-swirldslabs authored Jan 21, 2025
1 parent d391365 commit 07a3ba8
Show file tree
Hide file tree
Showing 32 changed files with 514 additions and 165 deletions.
3 changes: 3 additions & 0 deletions server/docker/logging.properties
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ io.helidon.common.level=INFO
#com.hedera.block.level=FINE
#com.hedera.block.server.level=FINE

# Configure the configuration logging - OFF will suppress all configuration logging
#com.hedera.block.server.config.logging.ConfigurationLoggingImpl.level=OFF

# Configure specific Block Node loggers
#com.hedera.block.server.producer.ProducerBlockItemObserver.level=FINE
#com.hedera.block.server.mediator.LiveStreamMediatorImpl.level=FINE
Expand Down
32 changes: 20 additions & 12 deletions server/src/main/java/com/hedera/block/server/BlockNodeApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
import static com.hedera.block.server.Constants.PBJ_PROTOCOL_PROVIDER_CONFIG_NAME;
import static java.lang.System.Logger;
import static java.lang.System.Logger.Level.INFO;
import static java.util.Objects.requireNonNull;

import com.hedera.block.server.config.logging.ConfigurationLogging;
import com.hedera.block.server.health.HealthService;
import com.hedera.block.server.pbj.PbjBlockAccessService;
import com.hedera.block.server.pbj.PbjBlockStreamService;
Expand Down Expand Up @@ -34,6 +36,7 @@ public class BlockNodeApp {
private final PbjBlockStreamService pbjBlockStreamService;
private final PbjBlockAccessService pbjBlockAccessService;
private final ServerConfig serverConfig;
private final ConfigurationLogging configurationLogging;

/**
* Constructs a new BlockNodeApp with the specified dependencies.
Expand All @@ -47,18 +50,20 @@ public class BlockNodeApp {
*/
@Inject
public BlockNodeApp(
@NonNull ServiceStatus serviceStatus,
@NonNull HealthService healthService,
@NonNull PbjBlockStreamService pbjBlockStreamService,
@NonNull PbjBlockAccessService pbjBlockAccessService,
@NonNull WebServerConfig.Builder webServerBuilder,
@NonNull ServerConfig serverConfig) {
this.serviceStatus = serviceStatus;
this.healthService = healthService;
this.pbjBlockStreamService = pbjBlockStreamService;
this.pbjBlockAccessService = pbjBlockAccessService;
this.webServerBuilder = webServerBuilder;
this.serverConfig = serverConfig;
@NonNull final ServiceStatus serviceStatus,
@NonNull final HealthService healthService,
@NonNull final PbjBlockStreamService pbjBlockStreamService,
@NonNull final PbjBlockAccessService pbjBlockAccessService,
@NonNull final WebServerConfig.Builder webServerBuilder,
@NonNull final ServerConfig serverConfig,
@NonNull final ConfigurationLogging configurationLogging) {
this.serviceStatus = requireNonNull(serviceStatus);
this.healthService = requireNonNull(healthService);
this.pbjBlockStreamService = requireNonNull(pbjBlockStreamService);
this.pbjBlockAccessService = requireNonNull(pbjBlockAccessService);
this.webServerBuilder = requireNonNull(webServerBuilder);
this.serverConfig = requireNonNull(serverConfig);
this.configurationLogging = requireNonNull(configurationLogging);
}

/**
Expand All @@ -68,6 +73,9 @@ public BlockNodeApp(
*/
public void start() throws IOException {

// Log the configuration
configurationLogging.log();

final HttpRouting.Builder httpRouting =
HttpRouting.builder().register(healthService.getHealthRootPath(), healthService);

Expand Down
10 changes: 4 additions & 6 deletions server/src/main/java/com/hedera/block/server/ServerConfig.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
package com.hedera.block.server;

import com.hedera.block.server.config.logging.Loggable;
import com.swirlds.config.api.ConfigData;
import com.swirlds.config.api.ConfigProperty;
import com.swirlds.config.api.validation.annotation.Max;
Expand All @@ -16,12 +17,12 @@
*/
@ConfigData("server")
public record ServerConfig(
@ConfigProperty(defaultValue = defaultMaxMessageSizeBytes)
@Loggable
@ConfigProperty(defaultValue = defaultMaxMessageSizeBytes)
@Min(minMaxMessageSizeBytes)
@Max(maxMaxMessageSizeBytes)
int maxMessageSizeBytes,
@ConfigProperty(defaultValue = defaultPort) @Min(minPort) @Max(maxPort) int port) {
private static final System.Logger LOGGER = System.getLogger(ServerConfig.class.getName());
@Loggable @ConfigProperty(defaultValue = defaultPort) @Min(minPort) @Max(maxPort) int port) {

// Constants for maxMessageSizeBytes property
private static final String defaultMaxMessageSizeBytes = "4_194_304";
Expand All @@ -42,9 +43,6 @@ public record ServerConfig(

validateMaxMessageSizeBytes(maxMessageSizeBytes);
validatePort(port);

LOGGER.log(System.Logger.Level.INFO, "Server configuration server.maxMessageSizeBytes: " + maxMessageSizeBytes);
LOGGER.log(System.Logger.Level.INFO, "Server configuration server.port: " + port);
}

private void validatePort(int port) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
package com.hedera.block.server.config;

import com.hedera.block.server.ServerConfig;
import com.hedera.block.server.config.logging.ConfigurationLogging;
import com.hedera.block.server.config.logging.ConfigurationLoggingImpl;
import com.hedera.block.server.consumer.ConsumerConfig;
import com.hedera.block.server.mediator.MediatorConfig;
import com.hedera.block.server.notifier.NotifierConfig;
Expand Down Expand Up @@ -129,4 +131,10 @@ static ServerConfig provideServerConfig(Configuration configuration) {
static VerificationConfig provideVerificationConfig(Configuration configuration) {
return configuration.getConfigData(VerificationConfig.class);
}

@Singleton
@Provides
static ConfigurationLogging provideConfigurationLogging(Configuration configuration) {
return new ConfigurationLoggingImpl(configuration);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,25 @@
*/
public final class ServerMappedConfigSourceInitializer {
private static final List<ConfigMapping> MAPPINGS = List.of(
// Please add properties in alphabetical order
new ConfigMapping("consumer.timeoutThresholdMillis", "CONSUMER_TIMEOUT_THRESHOLD_MILLIS"),
new ConfigMapping("persistence.storage.liveRootPath", "PERSISTENCE_STORAGE_LIVE_ROOT_PATH"),
new ConfigMapping("persistence.storage.archiveRootPath", "PERSISTENCE_STORAGE_ARCHIVE_ROOT_PATH"),
new ConfigMapping("persistence.storage.type", "PERSISTENCE_STORAGE_TYPE"),
new ConfigMapping("persistence.storage.compression", "PERSISTENCE_STORAGE_COMPRESSION"),
new ConfigMapping("persistence.storage.compressionLevel", "PERSISTENCE_STORAGE_COMPRESSION_LEVEL"),
new ConfigMapping("service.delayMillis", "SERVICE_DELAY_MILLIS"),
new ConfigMapping("mediator.ringBufferSize", "MEDIATOR_RING_BUFFER_SIZE"),
new ConfigMapping("mediator.type", "MEDIATOR_TYPE"),
new ConfigMapping("notifier.ringBufferSize", "NOTIFIER_RING_BUFFER_SIZE"),
new ConfigMapping("persistence.storage.archiveRootPath", "PERSISTENCE_STORAGE_ARCHIVE_ROOT_PATH"),
new ConfigMapping("persistence.storage.compression", "PERSISTENCE_STORAGE_COMPRESSION"),
new ConfigMapping("persistence.storage.compressionLevel", "PERSISTENCE_STORAGE_COMPRESSION_LEVEL"),
new ConfigMapping("persistence.storage.liveRootPath", "PERSISTENCE_STORAGE_LIVE_ROOT_PATH"),
new ConfigMapping("persistence.storage.type", "PERSISTENCE_STORAGE_TYPE"),
new ConfigMapping("producer.type", "PRODUCER_TYPE"),
new ConfigMapping("server.maxMessageSizeBytes", "SERVER_MAX_MESSAGE_SIZE_BYTES"),
new ConfigMapping("server.port", "SERVER_PORT"),
new ConfigMapping("prometheus.endpointEnabled", "PROMETHEUS_ENDPOINT_ENABLED"),
new ConfigMapping("prometheus.endpointPortNumber", "PROMETHEUS_ENDPOINT_PORT_NUMBER"),
new ConfigMapping("verification.enabled", "VERIFICATION_ENABLED"),
new ConfigMapping("server.maxMessageSizeBytes", "SERVER_MAX_MESSAGE_SIZE_BYTES"),
new ConfigMapping("server.port", "SERVER_PORT"),
new ConfigMapping("service.shutdownDelayMillis", "SERVICE_SHUTDOWN_DELAY_MILLIS"),
new ConfigMapping("verification.hashCombineBatchSize", "VERIFICATION_HASH_COMBINE_BATCH_SIZE"),
new ConfigMapping("verification.sessionType", "VERIFICATION_SESSION_TYPE"),
new ConfigMapping("verification.hashCombineBatchSize", "VERIFICATION_HASH_COMBINE_BATCH_SIZE"));
new ConfigMapping("verification.type", "VERIFICATION_TYPE"));

private ServerMappedConfigSourceInitializer() {}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: Apache-2.0
package com.hedera.block.server.config.logging;

/**
* Use this interface to log configuration data.
*/
public interface ConfigurationLogging {
/**
* Log the configuration data.
*/
void log();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// SPDX-License-Identifier: Apache-2.0
package com.hedera.block.server.config.logging;

import static java.lang.System.Logger.Level.INFO;
import static java.util.Objects.requireNonNull;

import com.swirlds.common.metrics.config.MetricsConfig;
import com.swirlds.common.metrics.platform.prometheus.PrometheusConfig;
import com.swirlds.config.api.ConfigData;
import com.swirlds.config.api.ConfigProperty;
import com.swirlds.config.api.Configuration;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.RecordComponent;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import javax.inject.Inject;

/**
* Use this class to log configuration data.
*/
public class ConfigurationLoggingImpl implements ConfigurationLogging {

private final System.Logger LOGGER = System.getLogger(getClass().getName());

private final Configuration configuration;
private final Set<Record> loggablePackages = new HashSet<>();

/**
* Create a new instance of ConfigurationLoggingImpl.
*
* @param configuration the resolved configuration to log
*/
@Inject
public ConfigurationLoggingImpl(@NonNull final Configuration configuration) {
this.configuration = requireNonNull(configuration);

// The configuration properties in these packages
// are out of our control. So allow them to be logged.
loggablePackages.add(configuration.getConfigData(MetricsConfig.class));
loggablePackages.add(configuration.getConfigData(PrometheusConfig.class));
}

/**
* Log the configuration data.
*/
@Override
public void log() {

// FINE, FINER and FINEST will be allowed to log the configuration
if (LOGGER.isLoggable(INFO)) {
final Map<String, Object> config = collectConfig(configuration);

// Header
final String bannerLine = "=".repeat(Math.max(0, calculateMaxLineLength(config)));

LOGGER.log(INFO, bannerLine);
LOGGER.log(INFO, "Block Node Configuration");
LOGGER.log(INFO, bannerLine);

// Log the configuration
for (Map.Entry<String, Object> e : config.entrySet()) {
LOGGER.log(INFO, e.getKey() + "=" + e.getValue());
}

// Footer
LOGGER.log(INFO, bannerLine);
}
}

@NonNull
Map<String, Object> collectConfig(@NonNull final Configuration configuration) {
final Map<String, Object> config = new TreeMap<>();

// Iterate over all the configuration data types
for (Class<? extends Record> configType : configuration.getConfigDataTypes()) {

// Only log record components that are annotated with @ConfigData
final ConfigData configDataAnnotation = configType.getDeclaredAnnotation(ConfigData.class);
if (configDataAnnotation != null) {

// For each record component, check the field annotations
for (RecordComponent component : configType.getRecordComponents()) {
if (component.isAnnotationPresent(ConfigProperty.class)) {

final String fieldName = component.getName();
final Record configRecord = configuration.getConfigData(configType);
try {
final Object value = component.getAccessor().invoke(configRecord);

// If the field is not annotated as '@Loggable' and it's
// not exempted in loggablePackages then it's a sensitive
// value and needs to be handled differently.
if (component.getAnnotation(Loggable.class) == null
&& !loggablePackages.contains(configuration.getConfigData(configType))) {

// If the field is blank then log the value as a blank string
// to let an admin know the sensitive value was not injected.
// Otherwise, log the value as '*****' to mask it.
final String maskedValue = (value.toString().isEmpty()) ? "" : "*****";
config.put(configDataAnnotation.value() + "." + fieldName, maskedValue);
} else {
// Log clear text values which were annotated with
// '@Loggable' or which were explicitly added to
// loggablePackages.
config.put(configDataAnnotation.value() + "." + fieldName, value);
}
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
}
}

return config;
}

static int calculateMaxLineLength(@NonNull final Map<String, Object> output) {
int maxLength = 0;
for (Map.Entry<String, Object> e : output.entrySet()) {

int lineLength = e.getKey().length() + e.getValue().toString().length();
if (lineLength > maxLength) {
maxLength = lineLength;
}
}
return maxLength;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: Apache-2.0
package com.hedera.block.server.config.logging;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.RECORD_COMPONENT})
public @interface Loggable {}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: Apache-2.0
package com.hedera.block.server.consumer;

import com.hedera.block.common.utils.Preconditions;
import com.hedera.block.server.config.logging.Loggable;
import com.swirlds.config.api.ConfigData;
import com.swirlds.config.api.ConfigProperty;

Expand All @@ -11,20 +13,14 @@
* timed out and will be disconnected
*/
@ConfigData("consumer")
public record ConsumerConfig(@ConfigProperty(defaultValue = "1500") long timeoutThresholdMillis) {
private static final System.Logger LOGGER = System.getLogger(ConsumerConfig.class.getName());
public record ConsumerConfig(@Loggable @ConfigProperty(defaultValue = "1500") long timeoutThresholdMillis) {

/**
* Validate the configuration.
*
* @throws IllegalArgumentException if the configuration is invalid
* @throws IllegalArgumentException if the timeoutThresholdMillis is not positive
*/
public ConsumerConfig {
if (timeoutThresholdMillis <= 0) {
throw new IllegalArgumentException("Timeout threshold must be greater than 0");
}

LOGGER.log(
System.Logger.Level.INFO, "Consumer configuration timeoutThresholdMillis: " + timeoutThresholdMillis);
Preconditions.requirePositive(timeoutThresholdMillis);
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
// SPDX-License-Identifier: Apache-2.0
package com.hedera.block.server.mediator;

import static java.lang.System.Logger.Level.INFO;

import com.hedera.block.common.utils.Preconditions;
import com.hedera.block.server.config.logging.Loggable;
import com.swirlds.config.api.ConfigData;
import com.swirlds.config.api.ConfigProperty;

Expand All @@ -19,9 +18,8 @@
// 131072 works but not with persistence
@ConfigData("mediator")
public record MediatorConfig(
@ConfigProperty(defaultValue = "4194304") int ringBufferSize,
@ConfigProperty(defaultValue = "PRODUCTION") String type) {
private static final System.Logger LOGGER = System.getLogger(MediatorConfig.class.getName());
@Loggable @ConfigProperty(defaultValue = "4_194_304") int ringBufferSize,
@Loggable @ConfigProperty(defaultValue = "PRODUCTION") MediatorType type) {

/**
* Validate the configuration.
Expand All @@ -31,7 +29,13 @@ public record MediatorConfig(
public MediatorConfig {
Preconditions.requirePositive(ringBufferSize, "Mediator Ring Buffer Size must be positive");
Preconditions.requirePowerOfTwo(ringBufferSize, "Mediator Ring Buffer Size must be a power of 2");
LOGGER.log(INFO, "Mediator configuration mediator.ringBufferSize: " + ringBufferSize);
LOGGER.log(INFO, "Mediator configuration mediator.type: " + type);
}

/**
* The type of mediator to use - PRODUCTION or NO_OP.
*/
public enum MediatorType {
PRODUCTION,
NO_OP,
}
}
Loading

0 comments on commit 07a3ba8

Please sign in to comment.