Author: rgoers
Date: Sun Nov 27 07:22:46 2011
New Revision: 1206675
URL: http://svn.apache.org/viewvc?rev=1206675&view=rev
Log:
Finish first draft of configuration documentation.
Added:
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/test/java/org/apache/logging/log4j/core/appender/routing/JSONRoutingAppender2Test.java
- copied, changed from r1204693,
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/test/java/org/apache/logging/log4j/core/appender/routing/JSONRoutingAppenderTest.java
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/test/resources/log4j-routing2.json
- copied, changed from r1204693,
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/test/resources/log4j-routing.json
Modified:
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/main/java/org/apache/logging/log4j/core/config/JSONConfiguration.java
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/main/java/org/apache/logging/log4j/core/config/XMLConfiguration.java
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/main/java/org/apache/logging/log4j/core/lookup/MapLookup.java
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/src/site/site.xml
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/src/site/xdoc/manual/configuration.xml
Modified:
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/main/java/org/apache/logging/log4j/core/config/JSONConfiguration.java
URL:
http://svn.apache.org/viewvc/logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/main/java/org/apache/logging/log4j/core/config/JSONConfiguration.java?rev=1206675&r1=1206674&r2=1206675&view=diff
==============================================================================
---
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/main/java/org/apache/logging/log4j/core/config/JSONConfiguration.java
(original)
+++
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/main/java/org/apache/logging/log4j/core/config/JSONConfiguration.java
Sun Nov 27 07:22:46 2011
@@ -148,10 +148,15 @@ public class JSONConfiguration extends B
if (n.isArray()) {
logger.debug("Processing node for array " +
entry.getKey());
for (int i=0; i < n.size(); ++i) {
- PluginType entryType =
getPluginManager().getPluginType(entry.getKey());
+ String pluginType = getType(n.get(i), entry.getKey());
+ PluginType entryType =
getPluginManager().getPluginType(pluginType);
Node item = new Node(node, entry.getKey(), entryType);
processAttributes(item, n.get(i));
- logger.debug("Processing " + entry.getKey() + "[" + i
+ "]");
+ if (pluginType.equals(entry.getKey())) {
+ logger.debug("Processing " + entry.getKey() + "["
+ i + "]");
+ } else {
+ logger.debug("Processing " + pluginType + " " +
entry.getKey() + "[" + i + "]");
+ }
Iterator<Map.Entry<String, JsonNode>> itemIter =
n.get(i).getFields();
List<Node> itemChildren = item.getChildren();
while (itemIter.hasNext()) {
@@ -167,8 +172,6 @@ public class JSONConfiguration extends B
logger.debug("Processing node for object " +
entry.getKey());
children.add(constructNode(entry.getKey(), node, n));
}
-
-
}
}
@@ -184,14 +187,30 @@ public class JSONConfiguration extends B
return node;
}
+ private String getType(JsonNode node, String name) {
+ Iterator<Map.Entry<String, JsonNode>> iter = node.getFields();
+ while (iter.hasNext()) {
+ Map.Entry<String, JsonNode> entry = iter.next();
+ if (entry.getKey().equalsIgnoreCase("type")) {
+ JsonNode n = entry.getValue();
+ if (n.isValueNode()) {
+ return n.asText();
+ }
+ }
+ }
+ return name;
+ }
+
private void processAttributes(Node parent, JsonNode node) {
Map<String, String> attrs = parent.getAttributes();
Iterator<Map.Entry<String, JsonNode>> iter = node.getFields();
while (iter.hasNext()) {
Map.Entry<String, JsonNode> entry = iter.next();
- JsonNode n = entry.getValue();
- if (n.isValueNode()) {
- attrs.put(entry.getKey(), n.asText());
+ if (!entry.getKey().equalsIgnoreCase("type")) {
+ JsonNode n = entry.getValue();
+ if (n.isValueNode()) {
+ attrs.put(entry.getKey(), n.asText());
+ }
}
}
}
Modified:
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/main/java/org/apache/logging/log4j/core/config/XMLConfiguration.java
URL:
http://svn.apache.org/viewvc/logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/main/java/org/apache/logging/log4j/core/config/XMLConfiguration.java?rev=1206675&r1=1206674&r2=1206675&view=diff
==============================================================================
---
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/main/java/org/apache/logging/log4j/core/config/XMLConfiguration.java
(original)
+++
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/main/java/org/apache/logging/log4j/core/config/XMLConfiguration.java
Sun Nov 27 07:22:46 2011
@@ -62,6 +62,8 @@ public class XMLConfiguration extends Ba
private boolean strict = false;
+ private String schema = null;
+
private static final String[] verboseClasses = new String[] {
ResolverUtil.class.getName() };
private Validator validator;
@@ -95,6 +97,8 @@ public class XMLConfiguration extends Ba
setName(entry.getValue());
} else if ("strict".equalsIgnoreCase(entry.getKey())) {
strict = Boolean.parseBoolean(entry.getValue());
+ } else if ("schema".equalsIgnoreCase(entry.getKey())) {
+ schema = entry.getValue();
} else if ("monitorInterval".equalsIgnoreCase(entry.getKey()))
{
int interval = Integer.parseInt(entry.getValue());
if (interval > 0 && configFile != null) {
@@ -129,24 +133,31 @@ public class XMLConfiguration extends Ba
} catch (ParserConfigurationException pex) {
logger.error("Error parsing " + source.getSystemId(), pex);
}
- if (strict && buffer != null) {
- InputStream is =
getClass().getClassLoader().getResourceAsStream(LOG4J_XSD);
- Source src = new StreamSource(is, LOG4J_XSD);
- SchemaFactory factory =
SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
- Schema schema = null;
+ if (strict && schema != null && buffer != null) {
+ InputStream is = null;
try {
- schema = factory.newSchema(src);
- } catch (SAXException ex) {
- logger.error("Error parsing Log4j schema", ex);
- }
- if (schema != null) {
- validator = schema.newValidator();
+ is = getClass().getClassLoader().getResourceAsStream(schema);
+ } catch (Exception ex) {
+ logger.error("Unable to access schema " + schema);
+ }
+ if (is != null) {
+ Source src = new StreamSource(is, LOG4J_XSD);
+ SchemaFactory factory =
SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+ Schema schema = null;
try {
- validator.validate(new StreamSource(new
ByteArrayInputStream(buffer)));
- } catch (IOException ioe) {
- logger.error("Error reading configuration for validation",
ioe);
+ schema = factory.newSchema(src);
} catch (SAXException ex) {
- logger.error("Error validating configuration", ex);
+ logger.error("Error parsing Log4j schema", ex);
+ }
+ if (schema != null) {
+ validator = schema.newValidator();
+ try {
+ validator.validate(new StreamSource(new
ByteArrayInputStream(buffer)));
+ } catch (IOException ioe) {
+ logger.error("Error reading configuration for
validation", ioe);
+ } catch (SAXException ex) {
+ logger.error("Error validating configuration", ex);
+ }
}
}
}
Modified:
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/main/java/org/apache/logging/log4j/core/lookup/MapLookup.java
URL:
http://svn.apache.org/viewvc/logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/main/java/org/apache/logging/log4j/core/lookup/MapLookup.java?rev=1206675&r1=1206674&r2=1206675&view=diff
==============================================================================
---
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/main/java/org/apache/logging/log4j/core/lookup/MapLookup.java
(original)
+++
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/main/java/org/apache/logging/log4j/core/lookup/MapLookup.java
Sun Nov 27 07:22:46 2011
@@ -17,12 +17,15 @@
package org.apache.logging.log4j.core.lookup;
import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.message.MapMessage;
import java.util.Map;
/**
* The basis for a lookup based on a Map.
*/
+@Plugin(name="map",type="Lookup")
public class MapLookup<V> implements StrLookup<V> {
/**
* Map keys are variable names and value.
@@ -30,7 +33,7 @@ public class MapLookup<V> implements Str
private final Map<String, V> map;
/**
- * Creates a new instance backed by a Map.
+ * Creates a new instance backed by a Map. Used by the default lookup.
*
* @param map the map of keys to values, may be null
*/
@@ -39,6 +42,13 @@ public class MapLookup<V> implements Str
}
/**
+ * Constructor when used directly as a plugin.
+ */
+ public MapLookup() {
+ this.map = null;
+ }
+
+ /**
* Looks up a String key to a String value using the map.
* <p/>
* If the map is null, then null is returned.
@@ -59,6 +69,18 @@ public class MapLookup<V> implements Str
}
public String lookup(LogEvent event, String key) {
- return lookup(key);
+ if (map == null && !(event.getMessage() instanceof MapMessage)) {
+ return null;
+ }
+ if (map != null && map.containsKey(key)) {
+ Object obj = map.get(key);
+ if (obj != null) {
+ return obj.toString();
+ }
+ }
+ if (event.getMessage() instanceof MapMessage) {
+ return ((MapMessage) event.getMessage()).get(key);
+ }
+ return null;
}
}
Copied:
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/test/java/org/apache/logging/log4j/core/appender/routing/JSONRoutingAppender2Test.java
(from r1204693,
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/test/java/org/apache/logging/log4j/core/appender/routing/JSONRoutingAppenderTest.java)
URL:
http://svn.apache.org/viewvc/logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/test/java/org/apache/logging/log4j/core/appender/routing/JSONRoutingAppender2Test.java?p2=logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/test/java/org/apache/logging/log4j/core/appender/routing/JSONRoutingAppender2Test.java&p1=logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/test/java/org/apache/logging/log4j/core/appender/routing/JSONRoutingAppenderTest.java&r1=1204693&r2=1206675&rev=1206675&view=diff
==============================================================================
---
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/test/java/org/apache/logging/log4j/core/appender/routing/JSONRoutingAppenderTest.java
(original)
+++
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/test/java/org/apache/logging/log4j/core/appender/routing/JSONRoutingAppender2Test.java
Sun Nov 27 07:22:46 2011
@@ -40,8 +40,8 @@ import static org.junit.Assert.assertTru
/**
*
*/
-public class JSONRoutingAppenderTest {
- private static final String CONFIG = "log4j-routing.json";
+public class JSONRoutingAppender2Test {
+ private static final String CONFIG = "log4j-routing2.json";
private static Configuration config;
private static ListAppender app;
private static LoggerContext ctx;
Copied:
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/test/resources/log4j-routing2.json
(from r1204693,
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/test/resources/log4j-routing.json)
URL:
http://svn.apache.org/viewvc/logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/test/resources/log4j-routing2.json?p2=logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/test/resources/log4j-routing2.json&p1=logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/test/resources/log4j-routing.json&r1=1204693&r2=1206675&rev=1206675&view=diff
==============================================================================
---
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/test/resources/log4j-routing.json
(original)
+++
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-core/src/test/resources/log4j-routing2.json
Sun Nov 27 07:22:46 2011
@@ -4,31 +4,32 @@
},
"ThresholdFilter": { "level": "debug" },
"appenders": {
- "Console": { "name": "STDOUT",
- "PatternLayout": { "pattern": "%m%n" }
- },
- "List": { "name": "List",
- "ThresholdFilter": { "level": "debug" }
- },
- "Routing": { "name": "Routing",
- "Routes": { "pattern": "$${sd:type}",
- "Route": [
- {
- "RollingFile": {
- "name": "Rolling-${sd:type}", "fileName": "${filename}",
- "filePattern": "target/rolling1/test1-${sd:type}.%i.log.gz",
- "PatternLayout": {"pattern": "%d %p %C{1.} [%t] %m%n"},
- "SizeBasedTriggeringPolicy": { "size": "500" }
- }
- },
- { "appender-ref": "STDOUT", "key": "Audit"},
- { "appender-ref": "List", "key": "Service"}
- ]
+ "appender": [
+ { "type": "Console", "name": "STDOUT", "PatternLayout": { "pattern":
"%m%n" }},
+ { "type": "List", "name": "List", "ThresholdFilter": { "level":
"debug" }},
+ { "type": "Routing", "name": "Routing",
+ "Routes": { "pattern": "$${sd:type}",
+ "Route": [
+ {
+ "RollingFile": {
+ "name": "Rolling-${sd:type}", "fileName": "${filename}",
+ "filePattern": "target/rolling1/test1-${sd:type}.%i.log.gz",
+ "PatternLayout": {"pattern": "%d %p %C{1.} [%t] %m%n"},
+ "SizeBasedTriggeringPolicy": { "size": "500" }
+ }
+ },
+ { "appender-ref": "STDOUT", "key": "Audit"},
+ { "appender-ref": "List", "key": "Service"}
+ ]
+ }
}
- }
+ ]
},
"loggers": {
- "logger": { "name": "EventLogger", "level": "info", "additivity":
"false", "appender-ref": { "ref": "Routing" }},
+ "logger": [
+ { "name": "EventLogger", "level": "info", "additivity": "false",
"appender-ref": { "ref": "Routing" }},
+ { "name": "com.foo.bar", "level": "error", "additivity": "false",
"appender-ref": { "ref": "Console" }}
+ ],
"root": { "level": "error", "appender-ref": { "ref": "STDOUT" }}
}
}
Modified:
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/src/site/site.xml
URL:
http://svn.apache.org/viewvc/logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/src/site/site.xml?rev=1206675&r1=1206674&r2=1206675&view=diff
==============================================================================
--- logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/src/site/site.xml
(original)
+++ logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/src/site/site.xml Sun
Nov 27 07:22:46 2011
@@ -45,7 +45,7 @@
<item name="Architecture" href="/manual/architecture.html"/>
<item name="Configuration" href="/manual/configuration.html"
collapse="true">
<item name="Automatic Configuration"
href="/manual/configuration.html#AutomaticConfiguration"/>
- <item name="Additivity" href="/manual/configuration.html#Additvity"/>
+ <item name="Additivity" href="/manual/configuration.html#Additivity"/>
<item name="Automatic Reconfiguration"
href="/manual/configuration.html#AutomaticReconfiguration"/>
<item name="Configuration Syntax"
href="/manual/configuration.html#ConfigurationSyntax"/>
<item name="Property Substitution"
href="/manual/configuration.html#PropertySubstitution"/>
Modified:
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/src/site/xdoc/manual/configuration.xml
URL:
http://svn.apache.org/viewvc/logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/src/site/xdoc/manual/configuration.xml?rev=1206675&r1=1206674&r2=1206675&view=diff
==============================================================================
---
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/src/site/xdoc/manual/configuration.xml
(original)
+++
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/src/site/xdoc/manual/configuration.xml
Sun Nov 27 07:22:46 2011
@@ -40,6 +40,8 @@
components to the default configuration.</li>
<li>Programmatically, by calling methods on the internal Logger
class.</li>
</ol>
+ </p>
+ <p>
This page focuses primarily on configuring Log4j through a
configuration file. Information on
programmatically configuring Log4j can be found at <a
href="../extending.html">Extending Log4j 2</a>.
</p>
@@ -246,16 +248,498 @@
When configured from a File, Log4j has the ability to
automatically detect changes to the configuration
file and reconfigure itself. If the monitorInterval attribute is
specified on the configuration element
and is set to a non-zero value then the file will be checked the
next time a log event is evaluated
- and/or logged and the monitorInterval has elapsed since the last
check.
+ and/or logged and the monitorInterval has elapsed since the last
check. The example below shows how
+ to configure the attribute so that the configuration file will be
checked for changes only after at
+ least 30 seconds have elapsed. The minimum, and default, interval
is 30 seconds.
</p>
+ <source><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
+<configuration monitorInterval="30">
+...
+</configuration>]]></source>
</subsection>
<a name="ConfigurationSyntax"/>
<subsection name="Configuration Syntax">
+ <p>
+ As the previous examples have shown as well as those to follow,
Log4j allows you to easily
+ redefine logging behavior without needing to modify your
application. It is possible to
+ disable logging for certain parts of the application, log only
when specific criteria are met such
+ as the action being performed for a specific user, route output to
Flume or a log reporting system,
+ etc. Being able to do this requires understanding the syntax of
the configuration files.
+ </p>
+ <h4>Configuration with XML</h4>
+ <p>
+ The configuration element in the XML file accetps several
attributes:
+ <table>
+ <tr>
+ <th>Attribute Name</th>
+ <th>Description</th>
+ </tr>
+ <tr>
+ <td>monitorInterval</td>
+ <td>The minimum amount of time, in seconds, that must elapse
before the file configuration
+ is checked for changes.</td>
+ </tr>
+ <tr>
+ <td>name</td>
+ <td>The name of the configuration.</td>
+ </tr>
+ <tr>
+ <td>packages</td>
+ <td>A comma separated list of package names to search for
plugins. Plugins are only loaded
+ once per classloader so changing this value may not have any
effect upon reconfiguration.</td>
+ </tr>
+ <tr>
+ <td>schema</td>
+ <td>Identifies the location for the classloader to located the
XML Schema to use to validate
+ the configuration. Only valid when strict is set to true. If
not set no schema validation
+ will take place.</td>
+ </tr>
+ <tr>
+ <td>status</td>
+ <td>The level of internal Log4j events that should be logged
to the console.</td>
+ </tr>
+ <tr>
+ <td>strict</td>
+ <td>Enables the use of the strict XML format. Not supported in
JSON configurations.</td>
+ </tr>
+ <tr>
+ <td>verbose</td>
+ <td>Enables diagnostic information while loading plugins.</td>
+ </tr>
+ </table>
+ </p>
+ <p>
+ Log4j can be configured using two XML flavors; concise and strict.
The concise format makes
+ configuration very easy as the element names match the components
they represent however it
+ cannot be validated with an XML schema. For example, the
ConsoleAppender is configured by
+ declaring an XML element named Console under its parent appenders
element. However, element
+ and attribute names are are not case sensitive. In addition,
attributes can either be specified
+ as an XML attribute or as an XML element that has no attributes
and has a text value. So
+ </p>
+ <source><![CDATA[<patternLayout pattern="%m%n"/>]]></source>
+ <p>and</p>
+ <source><![CDATA[<PatternLayout>
+ <pattern>%m%n</pattern>
+</PatternLayout>]]></source>
+ <p>
+ are equivalent.
+ </p>
+ <p>
+ The file below represents the structure of an XML configuration,
but note
+ that the elements in italics below represent the concise element
names that would appear in their place.
+ </p>
+
+ <source><![CDATA[<?xml version="1.0" encoding="UTF-8"?>;
+<configuration>
+ <properties>
+ <property name="name1">value</property>
+ <property name="name2" value="value2"/>
+ </properties>
+ <]]><i>filter</i> ... <![CDATA[/>
+ <appenders>
+ <]]><i>appender</i> ... <![CDATA[>
+ <]]><i>filter</i> ... <![CDATA[/>
+ </]]><i>appender</i><![CDATA[>
+ ...
+ </appenders>
+ <loggers>
+ <logger name="name1">
+ <]]><i>filter</i> ... <![CDATA[/>
+ </logger>
+ ...
+ <root level="level">
+ <appender-ref ref="name"/>
+ </root>
+ </loggers>
+</configuration>]]></source>
+ <p>
+ See the many examples on this page for sample appender, filter and
logger declarations.
+ </p>
+ <h5>Strict XML</h5>
+ <p>
+ In addition to the concise XML format above, Log4j allows
configurations to be specified in a
+ more "normal" XML manner that can be validated using an XML
Schema. This is accomplished by
+ replacing the friendly element names above with their object type
as shown below. For example,
+ instead of the ConsoleAppender being configuerd using an element
named Console it is instead
+ configured as an appender element with a type attribute containing
"Console".
+ </p>
+ <source><![CDATA[<?xml version="1.0" encoding="UTF-8"?>;
+<configuration>
+ <properties>
+ <property name="name1">value</property>
+ <property name="name2" value="value2"/>
+ </properties>
+ <filter type="type" ... />
+ <appenders>
+ <appender type="type" name="name">
+ <filter type="type" ... />
+ </appender>
+ ...
+ </appenders>
+ <loggers>
+ <logger name="name1">
+ <filter type="type" ... />
+ </logger>
+ ...
+ <root level="level">
+ <appender-ref ref="name"/>
+ </root>
+ </loggers>
+</configuration>]]></source>
+ <p>
+ Below is a sample configuration using the strict format.
+ </p>
+ <source><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
+<configuration status="debug" strict="true" name="XMLConfigTest"
packages="org.apache.logging.log4j.test">
+ <properties>
+ <property name="filename">target/test.log</property>
+ </properties>
+ <filter type="ThresholdFilter" level="trace"/>
+
+ <appenders>
+ <appender type="Console" name="STDOUT">
+ <layout type="PatternLayout" pattern="%m MDC%X%n"/>
+ <filters>
+ <filter type="MarkerFilter" marker="FLOW" onMatch="DENY"
onMismatch="NEUTRAL"/>
+ <filter type="MarkerFilter" marker="EXCEPTION" onMatch="DENY"
onMismatch="ACCEPT"/>
+ </filters>
+ </appender>
+ <appender type="Console" name="FLOW">
+ <layout type="PatternLayout" pattern="%C{1}.%M %m %ex%n"/>
+ <filters>
+ <filter type="MarkerFilter" marker="FLOW" onMatch="ACCEPT"
onMismatch="NEUTRAL"/>
+ <filter type="MarkerFilter" marker="EXCEPTION" onMatch="ACCEPT"
onMismatch="DENY"/>
+ </filters>
+ </appender>
+ <appender type="File" name="File" fileName="${filename}">
+ <layout type="PatternLayout">
+ <pattern>%d %p %C{1.} [%t] %m%n</pattern>
+ </layout>
+ </appender>
+ <appender type="List" name="List">
+ </appender>
+ </appenders>
+
+ <loggers>
+ <logger name="org.apache.logging.log4j.test1" level="debug"
additivity="false">
+ <filter type="ThreadContextMapFilter">
+ <KeyValuePair key="test" value="123"/>
+ </filter>
+ <appender-ref ref="STDOUT"/>
+ </logger>>
+
+ <logger name="org.apache.logging.log4j.test2" level="debug"
additivity="false">
+ <appender-ref ref="File"/>
+ </logger>>
+
+ <root level="trace">
+ <appender-ref ref="List"/>
+ </root>
+ </loggers>
+
+</configuration>]]></source>
+ <h4>Configuration with JSON</h4>
+ <p>
+ In addition to XML, Log4j can be configured using JSON. The JSON
format is very similar to the
+ concise XML format. Each key represents the name of a plugin and
the key/value pairs associated
+ with it are its attributes. Where a key contains more than a
simple value it itself will be a
+ subordinate plugin. In the example below, ThresholdFilter,
Console, and PatternLayout are all
+ plugins while the Console plugin will be assigned a value of
STDOUT for its name attribute and the
+ ThresholdFilter will be assigned a level of debug.
+ </p>
+ <source>{ "configuration": { "status": "error", "name":
"RoutingTest", "packages": "org.apache.logging.log4j.test",
+ "properties": {
+ "property": { "name": "filename", "value" :
"target/rolling1/rollingtest-$${sd:type}.log" }
+ },
+ "ThresholdFilter": { "level": "debug" },
+ "appenders": {
+ "Console": { "name": "STDOUT",
+ "PatternLayout": { "pattern": "%m%n" }
+ },
+ "List": { "name": "List",
+ "ThresholdFilter": { "level": "debug" }
+ },
+ "Routing": { "name": "Routing",
+ "Routes": { "pattern": "$${sd:type}",
+ "Route": [
+ {
+ "RollingFile": {
+ "name": "Rolling-${sd:type}", "fileName": "${filename}",
+ "filePattern": "target/rolling1/test1-${sd:type}.%i.log.gz",
+ "PatternLayout": {"pattern": "%d %p %C{1.} [%t] %m%n"},
+ "SizeBasedTriggeringPolicy": { "size": "500" }
+ }
+ },
+ { "appender-ref": "STDOUT", "key": "Audit"},
+ { "appender-ref": "List", "key": "Service"}
+ ]
+ }
+ }
+ },
+ "loggers": {
+ "logger": { "name": "EventLogger", "level": "info", "additivity":
"false", "appender-ref": { "ref": "Routing" }},
+ "root": { "level": "error", "appender-ref": { "ref": "STDOUT" }}
+ }
+ }
+}</source>
+ <p>
+ Note that in the RoutingAppender the Route element has been
declared as an array. This is
+ valid because each array element will be a Route component. This
won't work for elements such as
+ appenders and filters, where each element has a different name in
the concise format. Appenders and
+ filters can be defined as array elements if each appender or
filter declares an attribute named "type"
+ that contains the type of the appender. The following example
illustrates this as well as how to
+ declare multiple loggers as an array.
+ </p>
+ <source>{ "configuration": { "status": "debug", "name":
"RoutingTest", "packages": "org.apache.logging.log4j.test",
+ "properties": {
+ "property": { "name": "filename", "value" :
"target/rolling1/rollingtest-$${sd:type}.log" }
+ },
+ "ThresholdFilter": { "level": "debug" },
+ "appenders": {
+ "appender": [
+ { "type": "Console", "name": "STDOUT", "PatternLayout": { "pattern":
"%m%n" }},
+ { "type": "List", "name": "List", "ThresholdFilter": { "level":
"debug" }},
+ { "type": "Routing", "name": "Routing",
+ "Routes": { "pattern": "$${sd:type}",
+ "Route": [
+ {
+ "RollingFile": {
+ "name": "Rolling-${sd:type}", "fileName": "${filename}",
+ "filePattern": "target/rolling1/test1-${sd:type}.%i.log.gz",
+ "PatternLayout": {"pattern": "%d %p %C{1.} [%t] %m%n"},
+ "SizeBasedTriggeringPolicy": { "size": "500" }
+ }
+ },
+ { "appender-ref": "STDOUT", "key": "Audit"},
+ { "appender-ref": "List", "key": "Service"}
+ ]
+ }
+ }
+ ]
+ },
+ "loggers": {
+ "logger": [
+ { "name": "EventLogger", "level": "info", "additivity": "false",
"appender-ref": { "ref": "Routing" }},
+ { "name": "com.foo.bar", "level": "error", "additivity": "false",
"appender-ref": { "ref": "Console" }}
+ ],
+ "root": { "level": "error", "appender-ref": { "ref": "STDOUT" }}
+ }
+ }
+}</source>
+ <h4>Configuring loggers</h4>
+ <p>
+ An understanding of how loggers work in Log4j is critical before
trying to configure them.
+ Please reference the Log4j <a
href="../architecture.html">architecture</a> if more information is
+ required. Trying to configure Log4j without understanding those
concepts will lead to frustration.
+ </p>
+ <p>
+ A LoggerConfig is configured using the <code>logger</code>
element. The <code>logger</code> eleemnt
+ must have a name attribute specified, will usually have a level
attribute specified and may
+ also have an additivity attribute specified. The level may be
configured with one of TRACE,
+ DEBUG, INFO, WARN, ERROR, ALL or OFF. If no level is specified it
will default to ERROR. The
+ additivity attribute may be assigned a value of true or false. If
the attribute is omitted
+ the default value of false will be used.
+ </p>
+ <p>
+ The LoggerConfig may also be configured with one or more
appender-ref elements. Each appender
+ referenced will become associated with the specified LoggerConfig.
If multiple appenders
+ are configured on the LoggerConfig each of them be called when
processing logging events.
+ </p>
+ <p>
+ Every configuration must have a root logger. If one is not
configured the default root LoggerConfig,
+ which has a level of ERROR but with no appenders attached, will be
used. The main differences
+ between the root logger and other loggers are
+ <ol>
+ <li>The root logger does not have a name attribute.</li>
+ <li>The root logger does not support the additivity attribute
since it has no parent.</li>
+ </ol>
+ </p>
+ <h4>Configuring Appenders</h4>
+ <p>
+ An appender is configured either using the specific appender
plugin's name or with an appender
+ element and the type attibute containing the appender plugin's
name. In addition each appender
+ must have a name attribute specified with a value that is unique
within the set of appenders.
+ The name will be used by loggers to reference the appender as
described in the previous section.
+ </p>
+ <p>
+ Most appenders also support a layout to be configured (which again
may be specified either
+ using the specific Layout plugin's name as the eleemnt or with
"layout" as the element name
+ along with a type attribute that contains the layout plugin's
name. The various appenders
+ will contain other attributes or elements that are required for
them to function properly.
+ </p>
+ <h4>Configuring Filters</h4>
+ <p>
+ Log4j allows a filter to be specified in any of 3 places:
+ <ol>
+ <li>At the same level as the appenders, loggers and properties
elements. These filters can accept
+ or reject events before they have been passed to a
LoggerConfig.</li>
+ <li>In a logger element. These filters can accept or reject
events for specific loggers.</li>
+ <li>In an appender element. These filters can prevent or cause
events to be processed by
+ the appender.</li>
+ </ol>
+ </p>
+ <p>
+ Although only a single <code>filter</code> element can be
configured, that element may be the
+ <code>filters</code> element which represents the CompositeFilter.
The <code>filters</code> element
+ allows any number of <code>filter</code> elements to be configured
within it. The following example
+ shows how multiple filters can be configured on the
ConsoleAppender.
+ </p>
+ <source><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
+<configuration status="debug" name="XMLConfigTest"
packages="org.apache.logging.log4j.test">
+ <properties>
+ <property name="filename">target/test.log</property>
+ </properties>
+ <ThresholdFilter level="trace"/>
+
+ <appenders>
+ <Console name="STDOUT">
+ <PatternLayout pattern="%m MDC%X%n"/>
+ <filters>
+ <MarkerFilter marker="FLOW" onMatch="DENY" onMismatch="NEUTRAL"/>
+ <MarkerFilter marker="EXCEPTION" onMatch="DENY" onMismatch="ACCEPT"/>
+ </filters>
+ </Console>
+ <Console name="FLOW">
+ <PatternLayout pattern="%C{1}.%M %m %ex%n"/>
+ <filters>
+ <MarkerFilter marker="FLOW" onMatch="ACCEPT" onMismatch="NEUTRAL"/>
+ <MarkerFilter marker="EXCEPTION" onMatch="ACCEPT" onMismatch="DENY"/>
+ </filters>
+ </Console>
+ <File name="File" fileName="${filename}">
+ <PatternLayout>
+ <pattern>%d %p %C{1.} [%t] %m%n</pattern>
+ </PatternLayout>
+ </File>
+ <List name="List">
+ </List>
+ </appenders>
+ <loggers>
+ <logger name="org.apache.logging.log4j.test1" level="debug"
additivity="false">
+ <ThreadContextMapFilter>
+ <KeyValuePair key="test" value="123"/>
+ </ThreadContextMapFilter>
+ <appender-ref ref="STDOUT"/>
+ </logger>>
+
+ <logger name="org.apache.logging.log4j.test2" level="debug"
additivity="false">
+ <appender-ref ref="File"/>
+ </logger>>
+
+ <root level="trace">
+ <appender-ref ref="List"/>
+ </root>
+ </loggers>
+
+</configuration>]]></source>
</subsection>
<a name="PropertySubstitution"/>
<subsection name="Property Substitution">
+ <p>
+ Log4j 2 supports the ability to specify tokens in the
configuration as references to properties defined
+ elsewhere. Some of these properties will be resolved when the
configuration file is interpreted while
+ others may be passed to components where they will be evaluated at
runtime. To accomplish this, Log4j
+ uses variations Apache Commons Lang's
+ <a
href="../log4j2-core/apidocs/org/apache/logging/log4j/core/lookup/StrSubstitutor">StrSubstitutor</a>
+ and <a
href="../log4j2-core/apidocs/org/apache/logging/log4j/core/lookup/StrLookup">StrLookup</a>
+ classes. In a manner similar to Ant or Maven, this allows
variables declared as <code>${name}</code>
+ to be resolved using properties declared in the configuration
itself. For example, the following example
+ shows the filename for the rolling file appender being declared as
a property.
+ </p>
+<source><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
+<configuration status="debug" name="RoutingTest"
packages="org.apache.logging.log4j.test">
+ <properties>
+ <property
name="filename">target/rolling1/rollingtest-$${sd:type}.log</property>
+ </properties>
+ <ThresholdFilter level="debug"/>
+
+ <appenders>
+ <Console name="STDOUT">
+ <PatternLayout pattern="%m%n"/>
+ </Console>
+ <List name="List">
+ <ThresholdFilter level="debug"/>
+ </List>
+ <Routing name="Routing">
+ <Routes pattern="$${sd:type}">
+ <Route>
+ <RollingFile name="Rolling-${sd:type}" fileName="${filename}"
+
filePattern="target/rolling1/test1-${sd:type}.%i.log.gz">
+ <PatternLayout>
+ <pattern>%d %p %C{1.} [%t] %m%n</pattern>
+ </PatternLayout>
+ <SizeBasedTriggeringPolicy size="500" />
+ </RollingFile>
+ </Route>
+ <Route appender-ref="STDOUT" key="Audit"/>
+ <Route appender-ref="List" key="Service"/>
+ </Routes>
+ </Routing>
+ </appenders>
+ <loggers>
+ <logger name="EventLogger" level="info" additivity="false">
+ <appender-ref ref="Routing"/>
+ </logger>
+
+ <root level="error">
+ <appender-ref ref="STDOUT"/>
+ </root>
+ </loggers>
+
+</configuration>]]></source>
+ <p>
+ While this is useful, there are many more places properties can
originate from. To accommodate this,
+ Log4j also supports the syntax <code>${prefix:name}</code> where
the prefix identifies tells Log4j
+ that variable name should be evaluated in a specific context. The
contexts that are built in to Logj4
+ are:
+ <table>
+ <tr>
+ <th>Prefix</th>
+ <th>Context</th>
+ </tr>
+ <tr>
+ <td>ctx</td>
+ <td>Thread Context Map (MDC)</td>
+ </tr>
+ <tr>
+ <td>env</td>
+ <td>System environment variables</td>
+ </tr>
+ <tr>
+ <td>map</td>
+ <td>A value from a MapMessage</td>
+ </tr>
+ <tr>
+ <td>sd</td>
+ <td>A value from a StructuredDataMessage. The key "id" will
return the name of the StructuredDataId
+ without the enterprise number. The key "type" will return
the message type. Other keys will
+ retrieve individual elements from the Map.</td>
+ </tr>
+ <tr>
+ <td>sys</td>
+ <td>System properties</td>
+ </tr>
+ </table>
+ </p>
+ <p>
+ An interesting feature of StrLookup processing is that when a
variable reference is declared with
+ multiple leading '$' characters each time the variable is resolved
the leading '$' is simply removed.
+ In the previous example the "Routes" element is capable of
resolving the variable at runtime. To allow
+ this the prefix value is specified as a variable with two leading
'$' characters. When the configuration
+ file is first processed the first variable is simply removed.
Thus, when the Routes element is evaluated
+ at runtime it is the variable declaration "${sd:type}" which
causes the event to be inspected for a
+ StructuredDataMessage and if one is present the value of its type
attribute to be used as the routing key.
+ Not all elements support resolving variables at runtime.
Components that do will specifically call that
+ out in their documentation.
+ </p>
+ <p>
+ <i>As a footnote, it is worth pointing out that the variables in
the RollingFile appender declaration
+ will also not be evaluated when the configuration is processed.
This is simply because the resolution
+ of the whole RollingFile element is deferred until a match occurs.
+ See <a
href="../appenders.html#RoutingAppender">RoutingAppender</a> for more
information.</i>
+ </p>
</subsection>
<a name="StatusMessages"/>
<subsection name="Status Messages">
@@ -378,9 +862,19 @@
</p>
</subsection>
<a name="UnitTestingInMaven"/>
- <subsection name="Unit Testing in Maven">
-
-
+ <subsection name="Testing in Maven">
+ <p>
+ Maven can run unit and functional tests during the build cycle. By
default, any files placed in
+ <code>src/test/resources</code> are automatically copied to
target/test-classes and are included
+ in the classpath during execution of any tests. As such, placing a
log4j-test.xml into this directory
+ will cause it to be used instead of a log4j.xml or log4j.json that
might be present. Thus a different
+ log configuration can be used during testing than what is used in
production.
+ </p>
+ <p>
+ A second approach, which is extensively used by Log4j 2, is to set
the log4j.configurationFile property
+ in the method annotated with @BeforeClass in the junit test class.
This will allow an arbitrarily
+ named file to be used during the test.
+ </p>
</subsection>
</section>
</body>