You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rg...@apache.org on 2012/08/21 04:26:18 UTC
svn commit: r1375371 - in /logging/log4j/log4j2/trunk/src/site/xdoc/manual:
extending.xml logsep.xml
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>