All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.quarkus.runtime.configuration.ConfigDiagnostic Maven / Gradle / Ivy

The newest version!
package io.quarkus.runtime.configuration;

import static io.smallrye.config.SmallRyeConfig.SMALLRYE_CONFIG_LOCATIONS;

import java.io.IOException;
import java.net.URI;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.NotDirectoryException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.function.Consumer;

import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.ConfigProvider;
import org.eclipse.microprofile.config.spi.ConfigSource;
import org.jboss.logging.Logger;

import io.quarkus.runtime.ImageMode;
import io.smallrye.config.ConfigValue;
import io.smallrye.config.SmallRyeConfig;
import io.smallrye.config.common.utils.StringUtil;

/**
 * Utility methods to log configuration problems.
 */
public final class ConfigDiagnostic {
    private static final Logger log = Logger.getLogger("io.quarkus.config");

    private static final List errorsMessages = new CopyOnWriteArrayList<>();
    private static final Set errorKeys = new CopyOnWriteArraySet<>();

    private ConfigDiagnostic() {
    }

    public static void invalidValue(String name, IllegalArgumentException ex) {
        final String message = ex.getMessage();
        final String loggedMessage = message != null ? message
                : String.format("An invalid value was given for configuration key \"%s\"", name);
        errorsMessages.add(loggedMessage);
        errorKeys.add(name);
    }

    public static void missingValue(String name, NoSuchElementException ex) {
        final String message = ex.getMessage();
        final String loggedMessage = message != null ? message
                : String.format("Configuration key \"%s\" is required, but its value is empty/missing", name);
        errorsMessages.add(loggedMessage);
        errorKeys.add(name);
    }

    public static void duplicate(String name) {
        final String loggedMessage = String.format("Configuration key \"%s\" was specified more than once", name);
        errorsMessages.add(loggedMessage);
        errorKeys.add(name);
    }

    public static void deprecated(String name) {
        log.warnf("Configuration key \"%s\" is deprecated", name);
    }

    public static void unknown(String name) {
        log.warnf(
                "Unrecognized configuration key \"%s\" was provided; it will be ignored; verify that the dependency extension for this configuration is set or that you did not make a typo",
                name);
    }

    public static void unknown(NameIterator name) {
        unknown(name.getName());
    }

    /**
     * Report any unused properties.
     * 
* The list of unused properties may contain false positives. This is caused when an environment variable is set up, * and we cannot determine correctly if it was used or not. *
* Environment variables require a conversion to regular property names so a Map can be properly populated when * iterating {@link Config#getPropertyNames()}. Because an Environment variable name may match multiple property * names, we try the best effort to report unknowns by matching used properties in their Environment variable name * format. * * @param properties the set of possible unused properties */ public static void unknownProperties(Set properties) { if (properties.isEmpty()) { return; } SmallRyeConfig config = ConfigProvider.getConfig().unwrap(SmallRyeConfig.class); Set usedProperties = new HashSet<>(); StringBuilder tmp = null; for (String property : config.getPropertyNames()) { if (properties.contains(property)) { continue; } if (tmp == null) { tmp = new StringBuilder(property.length()); } else { tmp.setLength(0); } String usedProperty = StringUtil.replaceNonAlphanumericByUnderscores(property, tmp); if (properties.contains(usedProperty)) { continue; } usedProperties.add(usedProperty); } for (String property : properties) { // Indexed properties not supported by @ConfigRoot, but they can show up due to the YAML source. Just ignore them. if (property.indexOf('[') != -1 && property.indexOf(']') != -1) { continue; } boolean found = false; if (!usedProperties.isEmpty()) { if (tmp == null) { tmp = new StringBuilder(property.length()); } else { tmp.setLength(0); } String propertyWithUnderscores = StringUtil.replaceNonAlphanumericByUnderscores(property, tmp); for (String usedProperty : usedProperties) { if (usedProperty.equalsIgnoreCase(propertyWithUnderscores)) { found = true; break; } } } if (!found) { ConfigValue configValue = config.getConfigValue(property); if (property.equals(configValue.getName())) { unknown(property); } } } } public static void unknownRunTime(String name) { if (ImageMode.current() == ImageMode.NATIVE_RUN) { // only warn at run time for native images, otherwise the user will get warned twice for every property unknown(name); } } public static void unknownRunTime(NameIterator name) { unknownRunTime(name.getName()); } public static void unknownPropertiesRuntime(Set properties) { if (ImageMode.current() == ImageMode.NATIVE_RUN) { unknownProperties(properties); } } /** * Determine if a fatal configuration error has occurred. * * @return {@code true} if a fatal configuration error has occurred */ public static boolean isError() { return !errorsMessages.isEmpty(); } /** * Reset the config error status (for e.g. testing). */ public static void resetError() { errorKeys.clear(); errorsMessages.clear(); } public static String getNiceErrorMessage() { StringBuilder b = new StringBuilder(); for (String errorsMessage : errorsMessages) { b.append(" - "); b.append(errorsMessage); b.append(System.lineSeparator()); } return b.toString(); } public static Set getErrorKeys() { return new HashSet<>(errorKeys); } private static final DirectoryStream.Filter CONFIG_FILES_FILTER = new DirectoryStream.Filter<>() { @Override public boolean accept(final Path entry) { // Ignore .properties, because we know these are have a default loader in core // Ignore profile files. The loading rules require the main file to be present, so we only need the type String filename = entry.getFileName().toString(); return Files.isRegularFile(entry) && filename.startsWith("application.") && !filename.endsWith(".properties"); } }; public static Set configFiles(Path configFilesLocation) throws IOException { if (!Files.exists(configFilesLocation)) { return Collections.emptySet(); } Set configFiles = new HashSet<>(); try (DirectoryStream candidates = Files.newDirectoryStream(configFilesLocation, CONFIG_FILES_FILTER)) { for (Path candidate : candidates) { configFiles.add(candidate.toUri().toURL().toString()); } } catch (NotDirectoryException ignored) { log.debugf("File %s is not a directory", configFilesLocation.toAbsolutePath()); return Collections.emptySet(); } return configFiles; } public static Set configFilesFromLocations() throws Exception { SmallRyeConfig config = ConfigProvider.getConfig().unwrap(SmallRyeConfig.class); Set configFiles = new HashSet<>(); configFiles.addAll(configFiles(Paths.get(System.getProperty("user.dir"), "config"))); Optional> optionalLocations = config.getOptionalValues(SMALLRYE_CONFIG_LOCATIONS, URI.class); optionalLocations.ifPresent(new Consumer>() { @Override public void accept(final List locations) { for (URI location : locations) { Path path = location.getScheme() != null && location.getScheme().equals("file") ? Paths.get(location) : Paths.get(location.getPath()); if (Files.isDirectory(path)) { try { configFiles.addAll(configFiles(path)); } catch (IOException e) { // Ignore } } } } }); return configFiles; } public static void unknownConfigFiles(final Set configFiles) { SmallRyeConfig config = ConfigProvider.getConfig().unwrap(SmallRyeConfig.class); Set configNames = new HashSet<>(); for (ConfigSource configSource : config.getConfigSources()) { if (configSource.getName() != null && configSource.getName().contains("application")) { configNames.add(configSource.getName()); } } for (String configFile : configFiles) { boolean found = false; for (String configName : configNames) { if (configName.contains(configFile)) { found = true; break; } } if (!found) { log.warnf( "Unrecognized configuration file %s found; Please, check if your are providing the proper extension to load the file", configFile); } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy