/* * Licensed to the Apache Software Foundation (ASF) under one or more contributor license * agreements. See the NOTICE file distributed with this work for additional information regarding * copyright ownership. The ASF licenses this file to You 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.apache.geode.internal.logging.log4j; import org.apache.geode.GemFireIOException; import org.apache.geode.distributed.internal.DistributionConfig; import org.apache.geode.internal.OSProcess; import org.apache.geode.internal.i18n.LocalizedStrings; import org.apache.geode.internal.logging.*; import org.apache.geode.internal.process.ProcessLauncherContext; import org.apache.geode.internal.util.LogFileUtils; import java.io.*; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; /** * Holds on to one or many instances of LogWriterAppender and provides convenience methods for * controlling their lifecycles. * */ public class LogWriterAppenders { public final static String APPEND_TO_LOG_FILE = DistributionConfig.GEMFIRE_PREFIX + "append-log"; private static final boolean ALLOW_REDIRECT = true; public enum Identifier { MAIN(false), SECURITY(true); private final boolean isSecure; private Identifier(final boolean isSecure) { this.isSecure = isSecure; } public boolean isSecure() { return this.isSecure; } }; private static Map<Identifier, LogWriterAppender> appenders = new HashMap<Identifier, LogWriterAppender>(); private static Map<Identifier, AtomicInteger> references = new HashMap<Identifier, AtomicInteger>(); /** * Returns the named LogWriterAppender or null if it does not exist. */ public synchronized static LogWriterAppender getAppender(final Identifier id) { return appenders.get(id); } /** * Returns the named LogWriterAppender or creates it if necessary. */ public synchronized static LogWriterAppender getOrCreateAppender(final Identifier id, final boolean appendToFile, final boolean isLoner, final LogConfig config, final boolean logConfig) { LogWriterAppender appender = appenders.get(id); if (appender == null) { appender = createLogWriterAppender(appendToFile, isLoner, id.isSecure(), config, logConfig); appenders.put(id, appender); references.put(id, new AtomicInteger(1)); } else { appender.setConfig(config); references.get(id).incrementAndGet(); } return appender; } /** * Returns the named LogWriterAppender or creates it if necessary. */ public static LogWriterAppender getOrCreateAppender(final Identifier id, final boolean isLoner, final LogConfig config, final boolean logConfig) { final boolean appendToFile = Boolean.getBoolean(APPEND_TO_LOG_FILE); return getOrCreateAppender(id, appendToFile, isLoner, config, logConfig); } /** * Creates the log writer appender for a distributed system based on the system's parsed * configuration. The initial banner and messages are also entered into the log by this method. * * @param isLoner Whether the distributed system is a loner or not * @param isSecurity Whether a log for security related messages has to be created * @param config The DistributionConfig for the target distributed system * @param logConfig if true log the configuration * @throws GemFireIOException if the log file can't be opened for writing */ static LogWriterAppender createLogWriterAppender(final boolean appendToFile, final boolean isLoner, final boolean isSecurity, final LogConfig config, final boolean logConfig) { final boolean isDistributionConfig = config instanceof DistributionConfig; final DistributionConfig dsConfig = isDistributionConfig ? (DistributionConfig) config : null; File logFile = config.getLogFile(); String firstMsg = null; boolean firstMsgWarning = false; AlertAppender.getInstance().setAlertingDisabled(isLoner); // security-log-file is specified in DistributionConfig if (isSecurity) { if (isDistributionConfig) { File tmpLogFile = dsConfig.getSecurityLogFile(); if (tmpLogFile != null && !tmpLogFile.equals(new File(""))) { logFile = tmpLogFile; } } else { throw new IllegalArgumentException("DistributionConfig is expected for SecurityLogWriter"); } } // log-file is NOT specified in DistributionConfig if (logFile == null || logFile.equals(new File(""))) { // out = System.out; return null; } // log-file is specified in DistributionConfig // if logFile exists attempt to rename it for rolling if (logFile.exists()) { final boolean useChildLogging = config.getLogFile() != null && !config.getLogFile().equals(new File("")) && config.getLogFileSizeLimit() != 0; final boolean statArchivesRolling = isDistributionConfig && dsConfig.getStatisticArchiveFile() != null && !dsConfig.getStatisticArchiveFile().equals(new File("")) && dsConfig.getArchiveFileSizeLimit() != 0 && dsConfig.getStatisticSamplingEnabled(); if (!appendToFile || useChildLogging || statArchivesRolling) { // check useChildLogging for // bug 50659 final File oldMain = ManagerLogWriter.getLogNameForOldMainLog(logFile, isSecurity || useChildLogging || statArchivesRolling); final boolean succeeded = LogFileUtils.renameAggressively(logFile, oldMain); if (succeeded) { firstMsg = LocalizedStrings.InternalDistributedSystem_RENAMED_OLD_LOG_FILE_TO_0 .toLocalizedString(oldMain); } else { firstMsgWarning = true; firstMsg = LocalizedStrings.InternalDistributedSystem_COULD_NOT_RENAME_0_TO_1 .toLocalizedString(new Object[] {logFile, oldMain}); } } } // create a FileOutputStream to the logFile FileOutputStream fos; try { fos = new FileOutputStream(logFile, true); } catch (FileNotFoundException ex) { String s = LocalizedStrings.InternalDistributedSystem_COULD_NOT_OPEN_LOG_FILE_0 .toLocalizedString(logFile); throw new GemFireIOException(s, ex); } final PrintStream out = new PrintStream(fos); // create the ManagerLogWriter that LogWriterAppender will wrap ManagerLogWriter mlw = null; String logWriterLoggerName = null; if (isSecurity) { mlw = new SecurityManagerLogWriter(dsConfig.getSecurityLogLevel(), out, config.getName()); logWriterLoggerName = LogService.SECURITY_LOGGER_NAME; } else { mlw = new ManagerLogWriter(config.getLogLevel(), out, config.getName()); logWriterLoggerName = LogService.MAIN_LOGGER_NAME; } mlw.setConfig(config); // if (mlw.infoEnabled()) { -- skip here and instead do this in LogWriterFactory when creating // the LogWriterLogger // if (!isLoner /* do this on a loner to fix bug 35602 */ // || !Boolean.getBoolean(InternalLocator.INHIBIT_DM_BANNER)) { // mlw.info(Banner.getString(null)); // } // } AppenderContext[] appenderContext = new AppenderContext[1]; if (isSecurity) { appenderContext[0] = LogService.getAppenderContext(LogService.SECURITY_LOGGER_NAME); } else { appenderContext[0] = LogService.getAppenderContext(); // ROOT or // gemfire.logging.appenders.LOGGER } // create the LogWriterAppender that delegates to ManagerLogWriter; final LogWriterAppender appender = LogWriterAppender.create(appenderContext, logWriterLoggerName, mlw, fos); // remove stdout appender from MAIN_LOGGER_NAME only if isUsingGemFireDefaultConfig -- see // #51819 if (!isSecurity && LogService.MAIN_LOGGER_NAME.equals(logWriterLoggerName) && LogService.isUsingGemFireDefaultConfig()) { LogService.removeConsoleAppender(); } // log the first msg about renaming logFile for rolling if it pre-existed final InternalLogWriter logWriter = mlw; if (firstMsg != null) { if (firstMsgWarning) { logWriter.warning(firstMsg); } else { logWriter.info(firstMsg); } } // log the config if (logConfig) { if (!isLoner) { // LOG:CONFIG: changed from config to info logWriter.info(LocalizedStrings.InternalDistributedSystem_STARTUP_CONFIGURATIONN_0, config.toLoggerString()); } } // LOG: do NOT allow redirectOutput if (ALLOW_REDIRECT) { // fix #46493 by moving redirectOutput invocation here if (ProcessLauncherContext.isRedirectingOutput()) { try { OSProcess.redirectOutput(config.getLogFile()); } catch (IOException e) { logWriter.error(e); // throw new GemFireIOException("Unable to redirect output to " + config.getLogFile(), e); } } } return appender; } public synchronized static void startupComplete(final Identifier id) { LogWriterAppender appender = appenders.get(id); if (appender != null) { appender.startupComplete(); } } public synchronized static void destroy(final Identifier id) { // TODO:LOG:KIRK: new Exception("KIRK destroy called for " + id).printStackTrace(); LogWriterAppender appender = appenders.get(id); if (appender == null) { return; } AtomicInteger count = references.get(id); if (count == null) { throw new IllegalStateException("Count is null for " + id); } if (count.get() < 0) { throw new IllegalStateException( "Count is non-positive integer for " + id + ": " + count.get()); } if (count.decrementAndGet() > 0) { return; } else { appenders.remove(id); references.remove(id); appender.destroy(); } } public synchronized static void stop(final Identifier id) { LogWriterAppender appender = appenders.get(id); if (appender != null) { appender.stop(); } } public synchronized static void configChanged(final Identifier id) { LogWriterAppender appender = appenders.get(id); if (appender != null) { appender.configChanged(); } } }