diff --git a/config/alfresco/content-services-context.xml b/config/alfresco/content-services-context.xml index 486ba26938..4473429f28 100644 --- a/config/alfresco/content-services-context.xml +++ b/config/alfresco/content-services-context.xml @@ -148,13 +148,13 @@ - - - - - - - + + + + + + + diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml index b07e5aee98..527a38e409 100644 --- a/config/alfresco/core-services-context.xml +++ b/config/alfresco/core-services-context.xml @@ -133,6 +133,27 @@ + + + + + + + + + + + + + + + + + + + classpath*:alfresco/module/*/log4j.properties + + @@ -876,7 +897,7 @@ - ${dir.root}/backup-lucene-indexes + ${dir.indexes}/backup-lucene-indexes diff --git a/source/java/org/alfresco/repo/admin/Log4JHierarchyInit.java b/source/java/org/alfresco/repo/admin/Log4JHierarchyInit.java index 42d54ccd0b..67c795c3af 100644 --- a/source/java/org/alfresco/repo/admin/Log4JHierarchyInit.java +++ b/source/java/org/alfresco/repo/admin/Log4JHierarchyInit.java @@ -24,42 +24,196 @@ */ package org.alfresco.repo.admin; +import java.io.BufferedInputStream; +import java.io.InputStream; +import java.util.ArrayList; import java.util.Enumeration; - -import org.apache.log4j.LogManager; -import org.apache.log4j.Logger; +import java.util.List; +import java.util.Properties; import org.apache.log4j.jmx.HierarchyDynamicMBean; +import org.apache.log4j.Logger; +import org.apache.log4j.LogManager; +import org.apache.log4j.PropertyConfigurator; import org.apache.log4j.spi.LoggerRepository; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; + /** * Initialises Log4j's HierarchyDynamicMBean (refer to core-services-context.xml) - * + * and any overriding log4.properties files. + *

+ * Alfresco modules can provide their own log4j.properties file, + * which augments/overrides the global log4j.properties within + * the Alfresco webapp. Within the module's source tree, suppose + * you create:

+ *      config/alfresco/module/{module.id}/log4j.properties
+ * At deployment time, this log4j.properties file will be placed in:
+ *      WEB-INF/classes/alfresco/module/{module.id}/log4j.properties
+ * Where {module.id} is whatever value is set within the AMP's + * module.properties file. For details, see: + * Developing an Alfresco Module + *

+ * For example, if {module.id} is "org.alfresco.module.avmCompare", then + * within your source code you'll have:

+ *      config/alfresco/module/org.alfresco.module.avmCompare/log4j.properties
+ * This would be deployed to:
+ *      WEB-INF/classes/alfresco/module/org.alfresco.module.avmCompare/log4j.properties
+ * By default the 'overriding_log4j_properties' property of the + * 'log4JHierarchyInit' bean within core-services-context.xml is:
+ *      classpath*:alfresco/module/*/log4j.properties
+ * Therefore, Log4JHierarchyInit will discover this supplimentary log4j.properties + * file, and merge it with the main log4j.file (WEB-INF/classes/log4j.properties). + * For example, the org.alfresco.module.avmCompare log4j.properties file might look like this: + *
+ *    #-----------------------------------------------------------------------
+ *    # webscript module log4j.properties
+ *    #                                  
+ *    #   NOTE                           
+ *    #   ----
+ *    #
+ *    #      Log4j uses the following logging levels:
+ *    #      debug,info,warn,error,fatal
+ *    #
+ *    #      To set the logging level of {fullClassName} to {loglevel},
+ *    #      add a line to this file of the following form:
+ *    #
+ *    #               log4j.logger.{fullClassName}={loglevel}
+ *    #
+ *    #      For example, to make 'com.example.MyExample' produce 'debug'
+ *    #      logs, add a line like this:
+ *    #
+ *    #               log4j.logger.com.example.MyExample=debug
+ *    #
+ *    #
+ *    #   WARNING
+ *    #   -------
+ *    #       Log properties in this log4j.properties file override/augment
+ *    #       those in the webapp's main log4j.properties.
+ *    #
+ *    #-----------------------------------------------------------------------
+ *    
+ *    log4j.logger.org.alfresco.module.avmCompare.AvmCompare=info
+ *    
+ * + * This system allows the author of any module to provide rich logging control + * without concern for corrupting the central log4j.properties file during + * AMP installation/deinstallation. For details, see: + * Module Management Tool */ public class Log4JHierarchyInit { - private HierarchyDynamicMBean log4jHierarchy; - - public void setLog4jHierarchy(HierarchyDynamicMBean log4jHierarchy) - { - this.log4jHierarchy = log4jHierarchy; - } - - @SuppressWarnings("unchecked") - public void init() - { - // Add each logger (that has a level set) from the Log4J Repository to the Hierarchy MBean - LoggerRepository r = LogManager.getLoggerRepository(); - - Enumeration loggers = r.getCurrentLoggers(); - Logger logger = null; - - while (loggers.hasMoreElements()) - { - logger = (Logger)loggers.nextElement(); - if (logger.getLevel() != null) - { - log4jHierarchy.addLoggerMBean(logger.getName()); - } - } - } + private HierarchyDynamicMBean log4jHierarchy; + private List extra_log4j_urls; + + public Log4JHierarchyInit() + { + extra_log4j_urls = new ArrayList(); + } + + + /** + * Loads a set of augmenting/overriding log4j.properties files + * from locations specified via an array of 'spring_urls' + * (typically provided by a core-services-context.xml). + * + * This function supports Spring's syntax for retrieving + * multiple class path resources with the same name, + * via the "classpath*:" prefix. For details, see: + * PathMatchingResourcePatternResolver. + */ + public void setOverriding_log4j_properties(List spring_urls) + { + for ( String url : spring_urls ) { extra_log4j_urls.add( url ); } + } + + public void setLog4jHierarchy(HierarchyDynamicMBean log4jHierarchy) + { + this.log4jHierarchy = log4jHierarchy; + } + + @SuppressWarnings("unchecked") + public void init() + { + // Add each logger (that has a level set) from + // the Log4J Repository to the Hierarchy MBean + + LoggerRepository r = LogManager.getLoggerRepository(); + + + // Include overriding loggers + // + // Typically, extra loggers come from AMP modules, but you + // could add others by augmenting core-services-context.xml + // This mechanism allows modules to have their own local + // log4j.properties file within: + // + // WEB-INF/classes/alfresco/module/{module.id}/log4j.properties + // + // Where: module.id is whatever value is set within the AMP's + // 'module.properties' file. + // + // See also: + // http://wiki.alfresco.com/wiki/Developing_an_Alfresco_Module + // (the module.properties section) + // + // And: + // core-services-context.xml + + set_overriding_loggers( r ); + + + Enumeration loggers = r.getCurrentLoggers(); + Logger logger = null; + + while (loggers.hasMoreElements()) + { + logger = (Logger)loggers.nextElement(); + if (logger.getLevel() != null) + { + log4jHierarchy.addLoggerMBean(logger.getName()); + } + } + } + + void set_overriding_loggers( LoggerRepository hierarchy ) + { + for ( String spring_url : extra_log4j_urls ) + { + set_overriding_logger( spring_url, hierarchy ); + } + } + + void set_overriding_logger( String spring_url, + LoggerRepository hierarchy ) + { + PathMatchingResourcePatternResolver resolver; + PropertyConfigurator prop_config; + + prop_config = new PropertyConfigurator(); + resolver = new PathMatchingResourcePatternResolver(); + + Resource[] resources = null; + + try + { + resources = resolver.getResources( spring_url ); + } + catch (Exception e) { return ; } + + + // Read each resource + for (Resource resource : resources ) + { + try + { + InputStream istream = new BufferedInputStream(resource.getInputStream()); + Properties properties = new Properties(); + properties.load(istream); + + prop_config.doConfigure(properties, hierarchy); + } + catch (Throwable e) { /* do nothing */ } + } + } }