/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.logging; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.Properties; import javax.servlet.ServletContext; import org.apache.log4j.Appender; import org.apache.log4j.ConsoleAppender; import org.apache.log4j.FileAppender; import org.apache.log4j.LogManager; import org.apache.log4j.PropertyConfigurator; import org.geoserver.platform.GeoServerExtensions; import org.geoserver.platform.GeoServerResourceLoader; import org.geoserver.platform.resource.Paths; import org.geoserver.platform.resource.Resource; import org.geoserver.platform.resource.Resource.Type; import org.vfny.geoserver.global.ConfigurationException; public class LoggingUtils { public static final String RELINQUISH_LOG4J_CONTROL = "RELINQUISH_LOG4J_CONTROL"; public static final String GT2_LOGGING_REDIRECTION = "GT2_LOGGING_REDIRECTION"; public static final String GEOSERVER_LOG_LOCATION = "GEOSERVER_LOG_LOCATION"; public static enum GeoToolsLoggingRedirection { JavaLogging, CommonsLogging, Log4J; /** * Returns the enum value corresponding to the name (using case insensitive comparison) * or Log4j if no match is found * @param name * */ public static GeoToolsLoggingRedirection findValue(String name) { for (GeoToolsLoggingRedirection value : values()) { if(value.name().equalsIgnoreCase(name)) return value; } return Log4J; } } public static void configureGeoServerLogging(GeoServerResourceLoader loader, InputStream loggingConfigStream, boolean suppressStdOutLogging, boolean suppressFileLogging, String logFileName) throws FileNotFoundException, IOException, ConfigurationException { //JD: before we wipe out the logging configuration, save any appenders that are not // console or file based. This allows for other types of appenders to remain in tact // when geoserver is reloaded. List<Appender> appenders = new ArrayList(); Enumeration a = LogManager.getRootLogger().getAllAppenders(); while( a.hasMoreElements() ) { Appender appender = (Appender) a.nextElement(); if ( !( appender instanceof ConsoleAppender || appender instanceof FileAppender ) ){ //save it appenders.add( appender ); } } Properties lprops = new Properties(); lprops.load(loggingConfigStream); LogManager.resetConfiguration(); // LogLog.setQuietMode(true); PropertyConfigurator.configure(lprops); // LogLog.setQuietMode(false); // configuring the log4j file logger if(!suppressFileLogging) { Appender gslf = org.apache.log4j.Logger.getRootLogger().getAppender("geoserverlogfile"); if (gslf instanceof org.apache.log4j.FileAppender) { if (logFileName == null ) { logFileName = loader.get("logs").get("geoserver.log").file().getAbsolutePath(); } else { if (!new File(logFileName).isAbsolute()) { logFileName = new File(loader.getBaseDirectory(), logFileName).getAbsolutePath(); LoggingInitializer.LOGGER.fine("Non-absolute pathname detected for logfile. Setting logfile relative to data dir."); } } lprops.setProperty("log4j.appender.geoserverlogfile.File", logFileName); PropertyConfigurator.configure(lprops); LoggingInitializer.LOGGER.fine("Logging output to file '" + logFileName + "'"); } else if (gslf != null) { LoggingInitializer.LOGGER.warning("'log4j.appender.geoserverlogfile' appender is defined, but isn't a FileAppender. GeoServer won't control the file-based logging."); } else { LoggingInitializer.LOGGER.warning("'log4j.appender.geoserverlogfile' appender isn't defined. GeoServer won't control the file-based logging."); } } // ... and the std output logging too if (suppressStdOutLogging) { LoggingInitializer.LOGGER.info("Suppressing StdOut logging. If you want to see GeoServer logs, be sure to look in '" + logFileName + "'"); Enumeration allAppenders = org.apache.log4j.Logger.getRootLogger().getAllAppenders(); Appender curApp; while (allAppenders.hasMoreElements()) { curApp = (Appender)allAppenders.nextElement(); if (curApp instanceof org.apache.log4j.ConsoleAppender) { org.apache.log4j.Logger.getRootLogger().removeAppender(curApp); } } } //add the appenders we saved above for ( Appender appender : appenders ) { LogManager.getRootLogger().addAppender( appender ); } LoggingInitializer.LOGGER.fine("FINISHED CONFIGURING GEOSERVER LOGGING -------------------------"); } public static void initLogging(GeoServerResourceLoader resourceLoader, String configFileName, boolean suppressStdOutLogging, String logFileName) throws Exception { //to initialize logging we need to do a couple of things: // 1) Figure out whether the user has 'overridden' some configuration settings // in the logging system (not using log4j in commons-logging.properties or perhaps // has set up their own 'custom' log4j.properties file. // 2) If they *have*, then we don't worry about configuring logging // 3) If they haven't, then we configure logging to use the log4j config file // specified, and remove console appenders if the suppressstdoutlogging is true. LoggingInitializer.LOGGER.fine("CONFIGURING GEOSERVER LOGGING -------------------------"); if (configFileName == null) { configFileName = "DEFAULT_LOGGING.properties"; LoggingInitializer.LOGGER.warning("No log4jConfigFile defined in services.xml: using 'DEFAULT_LOGGING.properties'"); } Resource resource = resourceLoader.get( Paths.path("logs", configFileName) ); if( resource == null || resource.getType() == Type.UNDEFINED ){ //hmm, well, we don't have a log4j config file and this could be due to the fact //that this is a data-dir upgrade. We can count on the DEFAULT_LOGGING.properties file //being present on the classpath, so we'll upgrade their data_dir and then use the //default DEFAULT_LOGGING.properties configuration. LoggingInitializer.LOGGER.warning("log4jConfigFile '" + configFileName + "' couldn't be found in the data dir, so GeoServer will " + "install the various logging config file into the data dir, and then try to find it again."); Resource logs = resourceLoader.get( "logs" ); File lcdir = logs.dir(); //now we copy in the various logging config files from the base repo location on the classpath final String[] lcfiles = new String[] { "DEFAULT_LOGGING.properties", "GEOSERVER_DEVELOPER_LOGGING.properties", "GEOTOOLS_DEVELOPER_LOGGING.properties", "PRODUCTION_LOGGING.properties", "QUIET_LOGGING.properties", "TEST_LOGGING.properties", "VERBOSE_LOGGING.properties" }; for (int i = 0; i < lcfiles.length; i++) { File target = new File(lcdir.getAbsolutePath(), lcfiles[i]); if (!target.exists()) { resourceLoader.copyFromClassPath(lcfiles[i], target); } } //ok, the possibly-new 'logs' directory is in-place, with all the various configs there. // Is the originally configured log4jconfigfile there now? if (resource.getType() != Type.RESOURCE) { LoggingInitializer.LOGGER.warning("Still couldn't find log4jConfigFile '" + configFileName + "'. Using DEFAULT_LOGGING.properties instead."); } resource = resourceLoader.get( Paths.path("logs", "DEFAULT_LOGGING.properties") ); } if (resource == null || resource.getType() != Type.RESOURCE) { throw new ConfigurationException("Unable to load logging configuration '" + configFileName + "'. In addition, an attempt " + "was made to create the 'logs' directory in your data dir, and to use the DEFAULT_LOGGING configuration, but" + "this failed as well. Is your data dir writeable?"); } // reconfiguring log4j logger levels by resetting and loading a new set of configuration properties try(InputStream loggingConfigStream = resource.in()) { if (loggingConfigStream == null) { LoggingInitializer.LOGGER.warning("Couldn't open Log4J configuration file '" + resource); return; } else { LoggingInitializer.LOGGER.fine("GeoServer logging profile '" + resource.name() + "' enabled."); } configureGeoServerLogging(resourceLoader, loggingConfigStream, suppressStdOutLogging, false, logFileName); } } /** * Finds the log location in the "context" (system variable, env variable, servlet context) * or uses the provided base location otherwise */ public static String getLogFileLocation(String baseLocation) { return getLogFileLocation(baseLocation, null); } /** * Finds the log location in the "context" (system variable, env variable, servlet context) * or uses the provided base location otherwise. * <p> * This method accepts a servlet context directly for cases where the logging location must * be known but the spring application context may not be initialized yet. * </p> */ public static String getLogFileLocation(String baseLocation, ServletContext context) { //accept a servlet context directly in the case of startup where the application context // is not yet available, in other cases (like a logging change) we can fall back on the // app context and dervive the servlet context from that String location = context != null ? GeoServerExtensions.getProperty(LoggingUtils.GEOSERVER_LOG_LOCATION, context) : GeoServerExtensions.getProperty(LoggingUtils.GEOSERVER_LOG_LOCATION); if(location == null) { return baseLocation; } else { return location; } } }