Author: rgoers Date: Tue Aug 21 02:26:18 2012 New Revision: 1375371 URL: http://svn.apache.org/viewvc?rev=1375371&view=rev Log: Add some documentation
Modified: logging/log4j/log4j2/trunk/src/site/xdoc/manual/extending.xml logging/log4j/log4j2/trunk/src/site/xdoc/manual/logsep.xml Modified: logging/log4j/log4j2/trunk/src/site/xdoc/manual/extending.xml URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/src/site/xdoc/manual/extending.xml?rev=1375371&r1=1375370&r2=1375371&view=diff ============================================================================== --- logging/log4j/log4j2/trunk/src/site/xdoc/manual/extending.xml (original) +++ logging/log4j/log4j2/trunk/src/site/xdoc/manual/extending.xml Tue Aug 21 02:26:18 2012 @@ -24,32 +24,360 @@ <body> <section name="Extending Log4j"> + <p> + Log4j 2 provides numerous ways that it can be manipulated and extended. This section includes an + overview of the various ways that are directly supported by the Log4j 2 implementation. + </p> <subsection name="LoggerContextFactory"> - + <p> + The LoggerContextFactory binds the Log4j API to its implementation. The Log4j LogManager + locates a LoggerContextFactory by locating all instances of META-INF/log4j-provider.xml, a + file that conforms to the java.util.Properties DTD, and then inspecting each to verify that it + specifies a value for the "Log4jAPIVersion" property that conforms to the version required by the + LogManager. If more than one valid implementation is located an exception will be thrown. + Finally, the value of the "LoggerContextFactory" property will be used to locate the + LoggerContextFactory. In Log4j 2 this is provided by Log4jContextFactory. + </p> </subsection> <subsection name="ContextSelector"> - + <p> + ContextSelectors are called by the Log4j LoggerContext factory. They perform the actual work of + locating or creating a LoggerContext, which is the anchor for Loggers and their configuration. + ContextSelectors are free to implement any mechanism they desire to manage LoggerContexts. The + default Log4jContextFactory checks for the presence of a System Property named "Log4jContextSelector". + If found, the property is expected to contain the name of the Class that implements the + ContextSelector to be used. + </p> + <p> + Log4j provides three ContextSelectors: + <dl> + <dt>BasicContextSelector</dt> + <dd>Uses either a LoggerContext that has been stored in a ThreadLocal or a common LoggerContext.</dd> + <dt>ClassLoaderContextSelector</dt> + <dd>Associates LoggerContexts with the ClassLoader that created the caller of the getLogger call.</dd> + <dt>JNDIContextSelector</dt> + <dd>Locates the LoggerContext by querying JNDI.</dd> + </dl> + </p> </subsection> <subsection name="ConfigurationFactory"> - + <p> + Modifying the way in which logging can be configured is usually one of the areas with the most + interest. The primary method for doing that is by implementing or extending a ConfigurationFactory. + Log4j provides two ways of adding new ConfigurationFactories. The first is by defining the system + property named "log4j.configurationFactory" to the name of the class that should be searched first + for a configuration. The second method is by defining the ConfigurationFactory as a Plugin. + </p> + <p> + All the ConfigurationFactories are then processed in order. Each factory is called on its + getSupportedTypes method to determine the file extensions it supports. If a configuration file + is located with one of the specified file extensions then control is passed to that + ConfigurationFactory to load the configuration and create the Configuration object. + </p> + <p> + Most Configuration extend the BaseConfiguration class. This class expects that the subclass will + process the configuration file and create a hierarchy of Node objects. Each Node is fairly simple + in that it consists of the name of the node, the name/value pairs associated with the node, The + PluginType of the node and a List of all of its child Nodes. BaseConfiguration will then be + passed the Node tree and instantiate the configuration objects from that. + </p> + <source> +@Plugin(name = "XMLConfigurationFactory", type = "ConfigurationFactory") +@Order(5) +public class XMLConfigurationFactory extends ConfigurationFactory { + + /** + * Valid file extensions for XML files. + */ + public static final String[] SUFFIXES = new String[] {".xml", "*"}; + + /** + * Return the Configuration. + * @param source The InputSource. + * @return The Configuration. + */ + public Configuration getConfiguration(InputSource source) { + return new XMLConfiguration(source, configFile); + } + + /** + * Returns the file suffixes for XML files. + * @return An array of File extensions. + */ + public String[] getSupportedTypes() { + return SUFFIXES; + } +}</source> </subsection> <subsection name="LoggerConfig"> - + <p> + LoggerConfig objects are where Loggers created by applications tie into the configuration. The Log4j + implementation requires that all LoggerConfigs be based on the LoggerConfig class, so applications + wishing to make changes must do so by extending the LoggerConfig class. To declare the new + LoggerConfig, declare it as a Plugin of type "Core" and providing the name that applications + should specify as the element name in the configuration. The LoggerConfig should also define + a PluginFactory that will create an instance of the LoggerConfig. + </p> + <p> + The following example shows how the root LoggerConfig simply extends a generic LoggerConfig. + </p> + <source><![CDATA[ +@Plugin(name = "root", type = "Core", printObject = true) +public static class RootLogger extends LoggerConfig { + + @PluginFactory + public static LoggerConfig createLogger(@PluginAttr("additivity") String additivity, + @PluginAttr("level") String loggerLevel, + @PluginElement("appender-ref") AppenderRef[] refs, + @PluginElement("filters") Filter filter) { + List<AppenderRef> appenderRefs = Arrays.asList(refs); + Level level; + try { + level = loggerLevel == null ? Level.ERROR : Level.valueOf(loggerLevel.toUpperCase()); + } catch (Exception ex) { + LOGGER.error("Invalid Log level specified: {}. Defaulting to Error", loggerLevel); + level = Level.ERROR; + } + boolean additive = additivity == null ? true : Boolean.parseBoolean(additivity); + + return new LoggerConfig(LogManager.ROOT_LOGGER_NAME, appenderRefs, filter, level, additive); + } +}]]></source> </subsection> <subsection name="Lookups"> - + <p> + Lookups are the means in which parameter substitution is performed. During Configuration initialization + an "Interpolator" is created that locates all the Lookups and registers them for use when a variable + needs to be resolved. The interpolator matches the "prefix" portion of the variable name to a + registered Lookup and passes control to it to resolve the variable. + </p> + <p> + A Lookup must be declared using a Plugin annotation with a type of "Lookup". The name specified on + the Plugin annotation will be used to match the prefix. Unlike other Plugins, Lookups do not + use a PluginFactory. Instead, they are required to provide a constructor that accepts no arguments. + The example below shows a Lookup that will return the value of a System Property. + </p> + <source> +@Plugin(name = "sys", type = "Lookup") +public class SystemPropertiesLookup implements StrLookup { + + /** + * Lookup the value for the key. + * @param key the key to be looked up, may be null + * @return The value for the key. + */ + public String lookup(String key) { + return System.getProperty(key); + } + + /** + * Lookup the value for the key using the data in the LogEvent. + * @param event The current LogEvent. + * @param key the key to be looked up, may be null + * @return The value associated with the key. + */ + public String lookup(LogEvent event, String key) { + return System.getProperty(key); + } +}</source> </subsection> <subsection name="Filters"> - + <p> + As might be expected, Filters are the used to reject or accept log events as they pass through the + logging system. A Filter is declared using a Plugin annotation of type "Core" and an elementType of + "filter". The name attribute on the Plugin annotation is used to specify the name of the element + users should use to enable the Filter. Specifying the printObject attribute with a value of "true" + indicates that a call to toString will format the arguments to the filter as the configuration + is being processed. The Filter must also specify a PluginFactory method that will be called to + create the Filter. + </p> + <p> + The example below shows a Filter used to reject LogEvents based upon their logging level. Notice the + typical pattern where all the filter methods resolve to a single filter method. + </p> + <source> +@Plugin(name = "ThresholdFilter", type = "Core", elementType = "filter", printObject = true) +public final class ThresholdFilter extends FilterBase { + + private final Level level; + + private ThresholdFilter(Level level, Result onMatch, Result onMismatch) { + super(onMatch, onMismatch); + this.level = level; + } + + public Result filter(Logger logger, Level level, Marker marker, String msg, Object[] params) { + return filter(level); + } + + public Result filter(Logger logger, Level level, Marker marker, Object msg, Throwable t) { + return filter(level); + } + + public Result filter(Logger logger, Level level, Marker marker, Message msg, Throwable t) { + return filter(level); + } + + @Override + public Result filter(LogEvent event) { + return filter(event.getLevel()); + } + + private Result filter(Level level) { + return level.isAtLeastAsSpecificAs(this.level) ? onMatch : onMismatch; + } + + @Override + public String toString() { + return level.toString(); + } + + /** + * Create a ThresholdFilter. + * @param loggerLevel The log Level. + * @param match The action to take on a match. + * @param mismatch The action to take on a mismatch. + * @return The created ThresholdFilter. + */ + @PluginFactory + public static ThresholdFilter createFilter(@PluginAttr("level") String loggerLevel, + @PluginAttr("onMatch") String match, + @PluginAttr("onMismatch") String mismatch) { + Level level = loggerLevel == null ? Level.ERROR : Level.toLevel(loggerLevel.toUpperCase()); + Result onMatch = match == null ? Result.NEUTRAL : Result.valueOf(match.toUpperCase()); + Result onMismatch = mismatch == null ? Result.DENY : Result.valueOf(mismatch.toUpperCase()); + + return new ThresholdFilter(level, onMatch, onMismatch); + } +}</source> </subsection> <subsection name="Appenders"> - + <p> + Appenders are passed an event, (usually) invoke a Layout to format the event, and then "publish" + the event in whatever manner is desired. Appenders are declared as Plugins with a type of "Core" + and an elementType of "appender". The name attribute on the Plugin annotation specifies the name + of the element users must provide in their configuration to use the Appender. Appender's should + specify printObject as "true" if the toString method renders the values of the attributes passed + to the Appender. + </p> + <p> + Appenders must also declare a PluginFactory method that will create the appender. The example + below shows an Appender named "Stub" that can be used as an initial template. + </p> + <p> + Most Appenders use Managers. A manager actually "owns" the resources, such as an OutputStream or + socket. When a reconfiguration occurs a new Appender will be created. However, if nothing significant + in the previous Manager has change the new Appender will simply reference it instead of creating a + new one. This insures that events are not lost while a reconfiguration is taking place without + requiring that logging pause while the reconfiguration takes place. + </p> + <source> +@Plugin(name = "Stub", type = "Core", elementType = "appender", printObject = true) +public final class StubAppender extends OutputStreamAppender { + + private StubAppender(String name, Layout layout, Filter filter, StubManager manager, boolean handleExceptions) { + } + + @PluginFactory + public static StubAppender createAppender(@PluginAttr("name") String name, + @PluginAttr("suppressExceptions") String suppress, + @PluginElement("layout") Layout layout, + @PluginElement("filters") Filter filter) { + + boolean handleExceptions = suppress == null ? true : Boolean.valueOf(suppress); + + if (name == null) { + LOGGER.error("No name provided for StubAppender"); + return null; + } + + StubManager manager = StubManager.getStubManager(name); + if (manager == null) { + return null; + } + if (layout == null) { + layout = PatternLayout.createLayout(null, null, null, null); + } + return new StubAppender(name, layout, filter, manager, handleExceptions); + } +}</source> </subsection> <subsection name="Layouts"> - + <p> + Layouts perform the formatting of events into the printable text that is written by Appenders to + some destination. All Layouts must implement the Layout interface. Layouts that format the + event into a String should extend AbstractStringLayout, which will take care of converting the + String into the required byte array. + </p> + <p> + Every Layout must declare itself as a plugin using the Plugin annotation. The type must be "Core", + and the elementType must be "Layout". printObject should be set to true if the plugin's toString + method will provide a representation of the object and its parameters. The name of the plugin must + match the value users should use to specify it as an element in their Appender configuration. + The plugin also must provide a static method annotated as a PluginFactory and with each of the + methods parameters annotated with PluginAttr or PluginElement as appropriate. + </p> + <source> +@Plugin(name = "SampleLayout", type = "Core", elementType = "layout", printObject = true) +public class SampleLayout extends AbstractStringLayout { + + protected SampleLayout(boolean locationInfo, boolean properties, boolean complete, Charset charset) { + } + + @PluginFactory + public static SampleLayout createLayout(@PluginAttr("locationInfo") String locationInfo, + @PluginAttr("properties") String properties, + @PluginAttr("complete") String complete, + @PluginAttr("charset") String charset) { + Charset c = Charset.isSupported("UTF-8") ? Charset.forName("UTF-8") : Charset.defaultCharset(); + if (charset != null) { + if (Charset.isSupported(charset)) { + c = Charset.forName(charset); + } else { + LOGGER.error("Charset " + charset + " is not supported for layout, using " + c.displayName()); + } + } + boolean info = locationInfo == null ? false : Boolean.valueOf(locationInfo); + boolean props = properties == null ? false : Boolean.valueOf(properties); + boolean comp = complete == null ? false : Boolean.valueOf(complete); + return new SampleLayout(info, props, comp, c); + } +}</source> </subsection> <subsection name="PatternConverters"> - + <p> + PatternConverters are used by the PatternLayout to format the log event into a printable String. Each + Converter is responsible for a single kind of manipulation, however Converters are free to format + the event in complex ways. For example, there are several converters that manipulate Throwables and + format them in various ways. + </p> + <p> + A PatternConverter must first declare itself as a Plugin using the standard Plugin annotation but + must specify value of "Converter" on the type attribute. Furthermore, the Converter must also + specify the ConverterKeys attribute to define the tokens that can be specified in the pattern + (preceded by a '%' character) to identify the Converter. + </p> + <p> + Unlike most other Plugins, Converters do not use a PluginFactory. Instead, each Converter is + required to provide a static newInstance method that accepts an array of Strings as the only + parameter. The String array are the values that are specified within the curly braces that can + follow the converter key. + </p> + <p> + The following shows the skeleton of a Converter plugin. + </p> + <source> +@Plugin(name = "query", type = "Converter") +@ConverterKeys({"q", "query"}) +public final class QueryConverter extends LogEventPatternConverter { + + public QueryConverter(String[] options) { + } + + public static QueryConverter newInstance(final String[] options) { + return new QueryConverter(options); + } +}</source> </subsection> <subsection name="Custom Plugins"> Modified: logging/log4j/log4j2/trunk/src/site/xdoc/manual/logsep.xml URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/src/site/xdoc/manual/logsep.xml?rev=1375371&r1=1375370&r2=1375371&view=diff ============================================================================== --- logging/log4j/log4j2/trunk/src/site/xdoc/manual/logsep.xml (original) +++ logging/log4j/log4j2/trunk/src/site/xdoc/manual/logsep.xml Tue Aug 21 02:26:18 2012 @@ -23,6 +23,61 @@ </properties> <body> - Log4J2 + <section name="Logging Separation"> + <p> + There are many well known use cases where applications may share an environment with other applications + and each has a need to have its own, separate logging environment. This purpose of this section is to + discuss some of these cases and ways to accomplish this. + </p> + <a name="Use Cases"/> + <subsection name="Use Cases"> + <p> + This section describes some of the use cases where Log4j could be used and what its desired behavior + might be. + </p> + <h4>Standalone Application</h4> + <p> + Standalone applications are usually relatively simple. They typically have one bundled executable + that requires only a single logging configuration. + </p> + <h4>Web Applications</h4> + <p> + A typical web application will be packaged as a WAR file and will include all of its dependencies in + WEB-INF/lib and will have its configuration file located in the class path or in a location + configured in the web.xml. + </p> + <h4>Java EE Applications</h4> + <p> + A Java EE application will consist of one or more WAR files and possible some EJBs, typically all + packaged in an EAR file. Usually, it is desirable to have a single configuration that applies to + all the components in the EAR. The logging classes will generally be placed in a location shared + across all the components and the configuration needs to also be shareable. + </p> + <h4>"Shared" Web Applications and REST Service Containers</h4> + <p> + In this scenario there are multiple WAR files deployed into a single container. Each of the applications + should use the same logging configuration and share the same logging implementation across each of the + web applications. When writing to files and streams each of the applications should share them to avoid + the issues that can occur when multiple components try to write to the same file(s) through different + File objects, channels, etc. + </p> + </subsection> + <a name="Approaches"/> + <subsection name="Approaches"> + <h4>The Simple Approach</h4> + <p> + The simplest approach for separating logging within applications is to package each application with + its own copy of Log4j and to use the BasicContextSelector. While this works for standalone applications + and may work for web applications and possibly Java EE applications, it does not work at all in the + last case. However, when this approach does work it should be used as it is ultimately the simplest + and most straightforward way of implementing logging. + </p> + + <h4>Using Context Selectors</h4> + <p> + + </p> + </subsection> + </section> </body> </document>