/*
* Copyright 2016 Red Hat, Inc.
*
* 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.
*/
package org.jboss.as.cli.embedded;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;
import java.util.logging.Handler;
import org.jboss.as.cli.CommandContext;
import org.jboss.logmanager.Configurator;
import org.jboss.logmanager.Level;
import org.jboss.logmanager.LogContext;
import org.jboss.logmanager.Logger;
import org.jboss.logmanager.PropertyConfigurator;
import org.jboss.logmanager.config.LogContextConfiguration;
import org.wildfly.security.manager.WildFlySecurityManager;
/**
* Used to control the {@linkplain LogContext log context} for embedded servers.
* <p>
* A single global log context is used for all embedded servers launched from a session. This is needed as static
* loggers will be created on a single log context. Reusing the same log context allows these loggers to still work and
* the log context to be reconfigured based on the embedded server that's booting.
* </p>
*
* @author <a href="mailto:jperkins@redhat.com">James R. Perkins</a>
*/
class EmbeddedLogContext {
private static class Holder {
static final LogContext LOG_CONTEXT = LogContext.create();
}
/**
* Configures the log context for the server and returns the configured log context.
*
* @param logDir the logging directory, from jboss.server|domain.log.dir standalone default {@code $JBOSS_HOME/standalone/log}
* @param configDir the configuration directory from jboss.server|domain.config.dir, standalone default {@code $JBOSS_HOME/standalone/configuration}
* @param defaultLogFileName the name of the log file to pass to {@code org.jboss.boot.log.file}
* @param ctx the command context used to report errors to
*
* @return the configured log context
*/
static synchronized LogContext configureLogContext(final File logDir, final File configDir, final String defaultLogFileName, final CommandContext ctx) {
final LogContext embeddedLogContext = Holder.LOG_CONTEXT;
final Path bootLog = logDir.toPath().resolve(Paths.get(defaultLogFileName));
final Path loggingProperties = configDir.toPath().resolve(Paths.get("logging.properties"));
if (Files.exists(loggingProperties)) {
WildFlySecurityManager.setPropertyPrivileged("org.jboss.boot.log.file", bootLog.toAbsolutePath().toString());
try (final InputStream in = Files.newInputStream(loggingProperties)) {
// Attempt to get the configurator from the root logger
Configurator configurator = embeddedLogContext.getAttachment("", Configurator.ATTACHMENT_KEY);
if (configurator == null) {
configurator = new PropertyConfigurator(embeddedLogContext);
final Configurator existing = embeddedLogContext.getLogger("").attachIfAbsent(Configurator.ATTACHMENT_KEY, configurator);
if (existing != null) {
configurator = existing;
}
}
configurator.configure(in);
} catch (IOException e) {
ctx.printLine(String.format("Unable to configure logging from configuration file %s. Reason: %s", loggingProperties, e.getLocalizedMessage()));
}
}
return embeddedLogContext;
}
/**
* Attempts to clear the global log context used for embedded servers.
*/
static synchronized void clearLogContext() {
final LogContext embeddedLogContext = Holder.LOG_CONTEXT;
// Remove the configurator and clear the log context
final Configurator configurator = embeddedLogContext.getLogger("").detach(Configurator.ATTACHMENT_KEY);
// If this was a PropertyConfigurator we can use the LogContextConfiguration API to tear down the LogContext
if (configurator instanceof PropertyConfigurator) {
final LogContextConfiguration logContextConfiguration = ((PropertyConfigurator) configurator).getLogContextConfiguration();
clearLogContext(logContextConfiguration);
} else if (configurator instanceof LogContextConfiguration) {
clearLogContext((LogContextConfiguration) configurator);
} else {
// Remove all the handlers and close them as well as reset the loggers
final List<String> loggerNames = Collections.list(embeddedLogContext.getLoggerNames());
for (String name : loggerNames) {
final Logger logger = embeddedLogContext.getLoggerIfExists(name);
if (logger != null) {
final Handler[] handlers = logger.clearHandlers();
if (handlers != null) {
for (Handler handler : handlers) {
handler.close();
}
}
logger.setFilter(null);
logger.setUseParentFilters(false);
logger.setUseParentHandlers(true);
logger.setLevel(Level.INFO);
}
}
}
}
private static void clearLogContext(final LogContextConfiguration logContextConfiguration) {
try {
// Remove all the handlers
logContextConfiguration.getHandlerNames().forEach(logContextConfiguration::removeHandlerConfiguration);
// Remove all the formatters
logContextConfiguration.getFormatterNames().forEach(logContextConfiguration::removeFormatterConfiguration);
// Remove all the error managers
logContextConfiguration.getErrorManagerNames().forEach(logContextConfiguration::removeErrorManagerConfiguration);
// Remove all the POJO's
logContextConfiguration.getPojoNames().forEach(logContextConfiguration::removePojoConfiguration);
// Remove all the loggers
logContextConfiguration.getLoggerNames().forEach(logContextConfiguration::removeLoggerConfiguration);
// Remove all the filters
logContextConfiguration.getFilterNames().forEach(logContextConfiguration::removeFilterConfiguration);
logContextConfiguration.commit();
} finally {
logContextConfiguration.forget();
}
}
}