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>