/* * JBoss, Home of Professional Open Source. * Copyright 2010, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.as.logging; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.Locale; import java.util.ResourceBundle; import java.util.function.Supplier; import org.jboss.as.controller.Extension; import org.jboss.as.controller.ExtensionContext; import org.jboss.as.controller.ModelVersion; import org.jboss.as.controller.PathElement; import org.jboss.as.controller.ResourceDefinition; import org.jboss.as.controller.SimpleResourceDefinition; import org.jboss.as.controller.SimpleResourceDefinition.Parameters; import org.jboss.as.controller.SubsystemRegistration; import org.jboss.as.controller.access.constraint.ApplicationTypeConfig; import org.jboss.as.controller.access.management.ApplicationTypeAccessConstraintDefinition; import org.jboss.as.controller.descriptions.ModelDescriptionConstants; import org.jboss.as.controller.descriptions.ResourceDescriptionResolver; import org.jboss.as.controller.descriptions.StandardResourceDescriptionResolver; import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler; import org.jboss.as.controller.parsing.ExtensionParsingContext; import org.jboss.as.controller.registry.ManagementResourceRegistration; import org.jboss.as.controller.services.path.PathInfoHandler; import org.jboss.as.controller.services.path.PathManager; import org.jboss.as.controller.services.path.PathResourceDefinition; import org.jboss.as.controller.services.path.ResolvePathHandler; import org.jboss.as.controller.transform.description.ChainedTransformationDescriptionBuilder; import org.jboss.as.controller.transform.description.ResourceTransformationDescriptionBuilder; import org.jboss.as.controller.transform.description.TransformationDescriptionBuilder; import org.jboss.as.logging.LoggingProfileOperations.LoggingProfileAdd; import org.jboss.as.logging.deployments.resources.LoggingDeploymentResources; import org.jboss.as.logging.logging.LoggingLogger; import org.jboss.as.logging.logmanager.WildFlyLogContextSelector; import org.jboss.as.logging.stdio.LogContextStdioContextSelector; import org.jboss.dmr.ModelNode; import org.jboss.logmanager.LogContext; import org.jboss.modules.Module; import org.jboss.modules.ModuleIdentifier; import org.jboss.modules.ModuleLoader; import org.jboss.staxmapper.XMLElementReader; import org.jboss.stdio.StdioContext; import org.wildfly.security.manager.WildFlySecurityManager; /** * @author Emanuel Muckenhuber * @author <a href="mailto:jperkins@redhat.com">James R. Perkins</a> */ public class LoggingExtension implements Extension { private static final String RESOURCE_NAME = LoggingExtension.class.getPackage().getName() + ".LocalDescriptions"; private static final String SKIP_LOG_MANAGER_PROPERTY = "org.wildfly.logging.skipLogManagerCheck"; public static final String SUBSYSTEM_NAME = "logging"; static final PathElement LOGGING_PROFILE_PATH = PathElement.pathElement(CommonAttributes.LOGGING_PROFILE); static final GenericSubsystemDescribeHandler DESCRIBE_HANDLER = GenericSubsystemDescribeHandler.create(LoggingChildResourceComparator.INSTANCE); private static final int MANAGEMENT_API_MAJOR_VERSION = 3; private static final int MANAGEMENT_API_MINOR_VERSION = 0; private static final int MANAGEMENT_API_MICRO_VERSION = 0; private static final ModelVersion CURRENT_VERSION = ModelVersion.create(MANAGEMENT_API_MAJOR_VERSION, MANAGEMENT_API_MINOR_VERSION, MANAGEMENT_API_MICRO_VERSION); private static final ModuleIdentifier[] LOGGING_API_MODULES = new ModuleIdentifier[] { ModuleIdentifier.create("org.apache.commons.logging"), ModuleIdentifier.create("org.apache.log4j"), ModuleIdentifier.create("org.jboss.logging"), ModuleIdentifier.create("org.jboss.logging.jul-to-slf4j-stub"), ModuleIdentifier.create("org.jboss.logmanager"), ModuleIdentifier.create("org.slf4j"), ModuleIdentifier.create("org.slf4j.ext"), ModuleIdentifier.create("org.slf4j.impl"), }; private static final List<String> DELEGATE_DESC_OPTS = Arrays.asList( AbstractHandlerDefinition.UPDATE_OPERATION_NAME, RootLoggerResourceDefinition.ROOT_LOGGER_ADD_OPERATION_NAME ); /** * Returns a resource description resolver that uses common descriptions for some attributes. * * @param keyPrefix the prefix to be appended to the {@link #SUBSYSTEM_NAME} * * @return the resolver */ public static ResourceDescriptionResolver getResourceDescriptionResolver(final String... keyPrefix) { StringBuilder prefix = new StringBuilder(SUBSYSTEM_NAME); for (String kp : keyPrefix) { prefix.append('.').append(kp); } return new StandardResourceDescriptionResolver(prefix.toString(), RESOURCE_NAME, LoggingExtension.class.getClassLoader(), true, false) { @Override public String getOperationParameterDescription(final String operationName, final String paramName, final Locale locale, final ResourceBundle bundle) { if (DELEGATE_DESC_OPTS.contains(operationName)) { return getResourceAttributeDescription(paramName, locale, bundle); } return super.getOperationParameterDescription(operationName, paramName, locale, bundle); } @Override public String getOperationParameterValueTypeDescription(final String operationName, final String paramName, final Locale locale, final ResourceBundle bundle, final String... suffixes) { if (DELEGATE_DESC_OPTS.contains(operationName)) { return getResourceAttributeDescription(paramName, locale, bundle); } return super.getOperationParameterValueTypeDescription(operationName, paramName, locale, bundle, suffixes); } @Override public String getOperationParameterDeprecatedDescription(final String operationName, final String paramName, final Locale locale, final ResourceBundle bundle) { if (DELEGATE_DESC_OPTS.contains(operationName)) { return getResourceAttributeDeprecatedDescription(paramName, locale, bundle); } return super.getOperationParameterDeprecatedDescription(operationName, paramName, locale, bundle); } }; } @Override public void initialize(final ExtensionContext context) { // The logging subsystem requires JBoss Log Manager to be used. Note this can be overridden and may fail late // instead of early. The reason to allow for the comparison to be overridden is some environments may wrap // the log manager. As long as the delegate log manager is JBoss Log Manager we should be okay. if (getBooleanProperty(SKIP_LOG_MANAGER_PROPERTY)) { LoggingLogger.ROOT_LOGGER.debugf("System property %s was set to true. Skipping the log manager check.", SKIP_LOG_MANAGER_PROPERTY); // Since we're overriding we will check the log manager system property and log a warning if the value is // not org.jboss.logmanager.LogManager. final String logManagerName = WildFlySecurityManager.getPropertyPrivileged("java.util.logging.manager", null); if (!org.jboss.logmanager.LogManager.class.getName().equals(logManagerName)) { LoggingLogger.ROOT_LOGGER.unknownLogManager(logManagerName); } } else { // Testing the log manager must use the FQCN as the classes may be loaded via different class loaders if (!java.util.logging.LogManager.getLogManager().getClass().getName().equals(org.jboss.logmanager.LogManager.class.getName())) { throw LoggingLogger.ROOT_LOGGER.extensionNotInitialized(); } } final WildFlyLogContextSelector contextSelector = WildFlyLogContextSelector.Factory.create(); LogContext.setLogContextSelector(contextSelector); // Install STDIO context selector StdioContext.setStdioContextSelector(new LogContextStdioContextSelector(StdioContext.getStdioContext())); // Load logging API modules try { final ModuleLoader moduleLoader = Module.forClass(LoggingExtension.class).getModuleLoader(); for (ModuleIdentifier moduleIdentifier : LOGGING_API_MODULES) { try { contextSelector.addLogApiClassLoader(moduleLoader.loadModule(moduleIdentifier).getClassLoader()); } catch (Throwable ignore) { // ignore } } } catch (Exception ignore) { // ignore } final SubsystemRegistration subsystem = context.registerSubsystem(SUBSYSTEM_NAME, CURRENT_VERSION); PathManager pathManager = null; // The path manager is only available if this is a server if (context.getProcessType().isServer()) { pathManager = context.getPathManager(); } final LoggingResourceDefinition rootResource = new LoggingResourceDefinition(pathManager, contextSelector); final ManagementResourceRegistration registration = subsystem.registerSubsystemModel(rootResource); registration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, DESCRIBE_HANDLER); // Register root sub-models registerSubModels(registration, true, subsystem, rootResource, context.isRegisterTransformers(), pathManager); // Register logging profile sub-models ApplicationTypeConfig atc = new ApplicationTypeConfig(SUBSYSTEM_NAME, CommonAttributes.LOGGING_PROFILE); ResourceDefinition profile = new SimpleResourceDefinition( new Parameters(LOGGING_PROFILE_PATH, getResourceDescriptionResolver()) .setAddHandler(new LoggingProfileAdd(pathManager)) .setRemoveHandler(LoggingProfileOperations.REMOVE_PROFILE) .setAccessConstraints(new ApplicationTypeAccessConstraintDefinition(atc))); registerLoggingProfileSubModels(registration.registerSubModel(profile), pathManager); // Register deployment resources if (context.isRuntimeOnlyRegistrationValid()) { final SimpleResourceDefinition deploymentSubsystem = new SimpleResourceDefinition(LoggingResourceDefinition.SUBSYSTEM_PATH, getResourceDescriptionResolver("deployment")); final ManagementResourceRegistration deployments = subsystem.registerDeploymentModel(deploymentSubsystem); final ManagementResourceRegistration configurationResource = deployments.registerSubModel(LoggingDeploymentResources.CONFIGURATION); configurationResource.registerSubModel(LoggingDeploymentResources.HANDLER); configurationResource.registerSubModel(LoggingDeploymentResources.LOGGER); configurationResource.registerSubModel(LoggingDeploymentResources.FORMATTER); configurationResource.registerSubModel(LoggingDeploymentResources.FILTER); configurationResource.registerSubModel(LoggingDeploymentResources.POJO); configurationResource.registerSubModel(LoggingDeploymentResources.ERROR_MANAGER); } subsystem.registerXMLElementWriter(LoggingSubsystemWriter.INSTANCE); } @Override public void initializeParsers(final ExtensionParsingContext context) { setParser(context, Namespace.LOGGING_1_0, LoggingSubsystemParser_1_0::new); setParser(context, Namespace.LOGGING_1_1, LoggingSubsystemParser_1_1::new); setParser(context, Namespace.LOGGING_1_2, LoggingSubsystemParser_1_2::new); setParser(context, Namespace.LOGGING_1_3, LoggingSubsystemParser_1_3::new); setParser(context, Namespace.LOGGING_1_4, LoggingSubsystemParser_1_4::new); setParser(context, Namespace.LOGGING_1_5, LoggingSubsystemParser_1_5::new); setParser(context, Namespace.LOGGING_2_0, LoggingSubsystemParser_2_0::new); setParser(context, Namespace.LOGGING_3_0, LoggingSubsystemParser_3_0::new); } private void registerLoggingProfileSubModels(final ManagementResourceRegistration registration, final PathManager pathManager) { registerSubModels(registration, false, null, null, false, pathManager); } private void registerSubModels(final ManagementResourceRegistration registration, final boolean includeLegacyAttributes, final SubsystemRegistration subsystem, final LoggingResourceDefinition subsystemResourceDefinition, final boolean registerTransformers, final PathManager pathManager) { // Only register if the path manager is not null, e.g. is a server ResolvePathHandler resolvePathHandler = null; PathInfoHandler diskUsagePathHandler = null; if (pathManager != null) { resolvePathHandler = ResolvePathHandler.Builder.of(pathManager) .setParentAttribute(CommonAttributes.FILE) .build(); diskUsagePathHandler = PathInfoHandler.Builder.of(pathManager) .setParentAttribute(CommonAttributes.FILE) .addAttribute(PathResourceDefinition.PATH, PathResourceDefinition.RELATIVE_TO) .build(); final LogFileResourceDefinition logFileResourceDefinition = new LogFileResourceDefinition(pathManager); registration.registerSubModel(logFileResourceDefinition); } final RootLoggerResourceDefinition rootLoggerResourceDefinition = new RootLoggerResourceDefinition(includeLegacyAttributes); registration.registerSubModel(rootLoggerResourceDefinition); final LoggerResourceDefinition loggerResourceDefinition = new LoggerResourceDefinition(includeLegacyAttributes); registration.registerSubModel(loggerResourceDefinition); final AsyncHandlerResourceDefinition asyncHandlerResourceDefinition = new AsyncHandlerResourceDefinition(includeLegacyAttributes); registration.registerSubModel(asyncHandlerResourceDefinition); final ConsoleHandlerResourceDefinition consoleHandlerResourceDefinition = new ConsoleHandlerResourceDefinition(includeLegacyAttributes); registration.registerSubModel(consoleHandlerResourceDefinition); final FileHandlerResourceDefinition fileHandlerResourceDefinition = new FileHandlerResourceDefinition(resolvePathHandler, diskUsagePathHandler, includeLegacyAttributes); registration.registerSubModel(fileHandlerResourceDefinition); final PeriodicHandlerResourceDefinition periodicHandlerResourceDefinition = new PeriodicHandlerResourceDefinition(resolvePathHandler, diskUsagePathHandler, includeLegacyAttributes); registration.registerSubModel(periodicHandlerResourceDefinition); final PeriodicSizeRotatingHandlerResourceDefinition periodicSizeRotatingHandlerResourceDefinition = new PeriodicSizeRotatingHandlerResourceDefinition(resolvePathHandler, diskUsagePathHandler); registration.registerSubModel(periodicSizeRotatingHandlerResourceDefinition); final SizeRotatingHandlerResourceDefinition sizeRotatingHandlerResourceDefinition = new SizeRotatingHandlerResourceDefinition(resolvePathHandler, includeLegacyAttributes); registration.registerSubModel(sizeRotatingHandlerResourceDefinition); final CustomHandlerResourceDefinition customHandlerResourceDefinition = new CustomHandlerResourceDefinition(includeLegacyAttributes); registration.registerSubModel(customHandlerResourceDefinition); registration.registerSubModel(SyslogHandlerResourceDefinition.INSTANCE); registration.registerSubModel(PatternFormatterResourceDefinition.INSTANCE); registration.registerSubModel(CustomFormatterResourceDefinition.INSTANCE); if (registerTransformers) { registerTransformers(subsystem, subsystemResourceDefinition, rootLoggerResourceDefinition, loggerResourceDefinition, asyncHandlerResourceDefinition, consoleHandlerResourceDefinition, fileHandlerResourceDefinition, periodicHandlerResourceDefinition, periodicSizeRotatingHandlerResourceDefinition, sizeRotatingHandlerResourceDefinition, customHandlerResourceDefinition, SyslogHandlerResourceDefinition.INSTANCE, PatternFormatterResourceDefinition.INSTANCE, CustomFormatterResourceDefinition.INSTANCE); } } private void registerTransformers(final SubsystemRegistration registration, final TransformerResourceDefinition... defs) { ChainedTransformationDescriptionBuilder chainedBuilder = TransformationDescriptionBuilder.Factory.createChainedSubystemInstance(registration.getSubsystemVersion()); registerTransformers(chainedBuilder, registration.getSubsystemVersion(), KnownModelVersion.VERSION_2_0_0, defs); // Version 1.5.0 has the periodic-size-rotating-file-handler and the suffix attribute on the size-rotating-file-handler. // Neither of these are in 2.0.0 (WildFly 8.x). Mapping from 3.0.0 to 1.5.0 is required registerTransformers(chainedBuilder, registration.getSubsystemVersion(), KnownModelVersion.VERSION_1_5_0, defs); registerTransformers(chainedBuilder, KnownModelVersion.VERSION_1_5_0, KnownModelVersion.VERSION_1_4_0, defs); registerTransformers(chainedBuilder, KnownModelVersion.VERSION_1_4_0, KnownModelVersion.VERSION_1_3_0, defs); chainedBuilder.buildAndRegister(registration, new ModelVersion[] { KnownModelVersion.VERSION_2_0_0.getModelVersion(), }, new ModelVersion[] { KnownModelVersion.VERSION_1_3_0.getModelVersion(), KnownModelVersion.VERSION_1_4_0.getModelVersion(), KnownModelVersion.VERSION_1_5_0.getModelVersion(), }); } private void registerTransformers(final ChainedTransformationDescriptionBuilder chainedBuilder, final KnownModelVersion fromVersion, final KnownModelVersion toVersion, final TransformerResourceDefinition... defs) { registerTransformers(chainedBuilder, fromVersion.getModelVersion(), toVersion, defs); } private void registerTransformers(final ChainedTransformationDescriptionBuilder chainedBuilder, final ModelVersion fromVersion, final KnownModelVersion toVersion, final TransformerResourceDefinition... defs) { final ResourceTransformationDescriptionBuilder subsystemBuilder = chainedBuilder.createBuilder(fromVersion, toVersion.getModelVersion()); final ResourceTransformationDescriptionBuilder loggingProfileBuilder = subsystemBuilder.addChildResource(LOGGING_PROFILE_PATH); for (TransformerResourceDefinition def : defs) { def.registerTransformers(toVersion, subsystemBuilder, loggingProfileBuilder); } } private static void setParser(final ExtensionParsingContext context, final Namespace namespace, final Supplier<XMLElementReader<List<ModelNode>>> supplier) { context.setSubsystemXmlMapping(SUBSYSTEM_NAME, namespace.getUriString(), supplier); } private static boolean getBooleanProperty(final String property) { if (WildFlySecurityManager.isChecking()) { return AccessController.doPrivileged(new PrivilegedAction<Boolean>() { @Override public Boolean run() { return Boolean.getBoolean(property); } }); } return Boolean.getBoolean(property); } public static class LoggingChildResourceComparator implements Comparator<PathElement> { static final LoggingChildResourceComparator INSTANCE = new LoggingChildResourceComparator(); static final int GREATER = 1; static final int EQUAL = 0; static final int LESS = -1; @Override public int compare(final PathElement o1, final PathElement o2) { final String key1 = o1.getKey(); final String key2 = o2.getKey(); int result = key1.compareTo(key2); if (result != EQUAL) { if (ModelDescriptionConstants.SUBSYSTEM.equals(key1)) { result = LESS; } else if (ModelDescriptionConstants.SUBSYSTEM.equals(key2)) { result = GREATER; } else if (CommonAttributes.LOGGING_PROFILE.equals(key1)) { result = LESS; } else if (CommonAttributes.LOGGING_PROFILE.equals(key2)) { result = GREATER; } else if (PatternFormatterResourceDefinition.PATTERN_FORMATTER.getName().equals(key1)) { result = LESS; } else if (PatternFormatterResourceDefinition.PATTERN_FORMATTER.getName().equals(key2)) { result = GREATER; } else if (CustomFormatterResourceDefinition.CUSTOM_FORMATTER.getName().equals(key1)) { result = LESS; } else if (CustomFormatterResourceDefinition.CUSTOM_FORMATTER.getName().equals(key2)) { result = GREATER; } else if (RootLoggerResourceDefinition.ROOT_LOGGER_PATH_NAME.equals(key1)) { result = GREATER; } else if (RootLoggerResourceDefinition.ROOT_LOGGER_PATH_NAME.equals(key2)) { result = LESS; } else if (LoggerResourceDefinition.LOGGER.equals(key1)) { result = GREATER; } else if (LoggerResourceDefinition.LOGGER.equals(key2)) { result = LESS; } else if (AsyncHandlerResourceDefinition.ASYNC_HANDLER.equals(key1)) { result = GREATER; } else if (AsyncHandlerResourceDefinition.ASYNC_HANDLER.equals(key2)) { result = LESS; } } return result; } } }