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>