You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rg...@apache.org on 2019/05/05 05:27:31 UTC
[logging-log4j2] 10/12: LOG4J2-913 - Add more tests and update site
This is an automated email from the ASF dual-hosted git repository.
rgoers pushed a commit to branch release-2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
commit e5f4479e25ce76a66b7c4af78bbbd626e2f34ac5
Author: Ralph Goers <rg...@apache.org>
AuthorDate: Sun Mar 31 23:05:33 2019 -0700
LOG4J2-913 - Add more tests and update site
---
.../core/appender/mom/kafka/KafkaAppender.java | 8 +-
.../core/appender/mom/kafka/KafkaManager.java | 39 ++++
.../log4j/core/layout/AbstractJacksonLayout.java | 142 +++++++++++++-
log4j-docker/pom.xml | 21 ++
.../logging/log4j/docker/model/Container.java | 6 +
.../logging/log4j/docker/model/IPAMConfig.java | 11 ++
.../apache/logging/log4j/docker/model/Network.java | 12 +-
.../log4j/docker/model/NetworkSettings.java | 177 +++++++++++++++++
log4j-docker/src/site/markdown/index.md.vm | 55 ++++++
log4j-docker/src/site/site.xml | 52 +++++
.../logging/log4j/flume/appender/BatchEvent.java | 4 +
.../log4j/flume/appender/FlumeAppender.java | 1 -
.../log4j/flume/appender/FlumeAvroManager.java | 42 ++--
log4j-samples/log4j-samples-flume-embedded/pom.xml | 1 +
log4j-samples/log4j-samples-flume-remote/pom.xml | 1 +
.../log4j-spring-cloud-config-client/pom.xml | 4 +-
.../src/site/markdown/index.md | 88 +++++++++
.../src/site/site.xml | 52 +++++
.../README.md | 74 +++----
.../docker/app-compose.yml | 21 ++
.../docker/docker-compose.yml | 1 +
.../pom.xml | 16 ++
.../pom.xml | 20 +-
.../src/main/config-repo/log4j2.xml | 53 +++++-
.../src/main/resources/log4j2.xml | 2 +-
log4j-spring-cloud-config/pom.xml | 1 +
.../src/site/markdown/index.md | 21 ++
log4j-spring-cloud-config/src/site/site.xml | 52 +++++
src/site/markdown/index.md.vm | 6 +
src/site/markdown/manual/cloud.md | 212 +++++++++++++++++++++
src/site/resources/images/DockerFluentd.drawio | 1 +
src/site/resources/images/DockerFluentd.png | Bin 0 -> 16444 bytes
.../images/DockerFluentdAggregator.drawio | 1 +
.../resources/images/DockerFluentdAggregator.png | Bin 0 -> 15394 bytes
src/site/resources/images/DockerLogFile.drawio | 1 +
src/site/resources/images/DockerLogFile.png | Bin 0 -> 14674 bytes
src/site/resources/images/DockerStdout.drawio | 1 +
src/site/resources/images/DockerStdout.png | Bin 0 -> 19551 bytes
src/site/resources/images/DockerTCP.drawio | 1 +
src/site/resources/images/DockerTCP.png | Bin 0 -> 11895 bytes
src/site/resources/images/LoggerAggregator.drawio | 1 +
src/site/resources/images/LoggerAggregator.png | Bin 0 -> 11342 bytes
src/site/site.xml | 4 +
src/site/xdoc/manual/configuration.xml.vm | 78 ++++++++
src/site/xdoc/manual/lookups.xml | 25 +++
src/site/xdoc/runtime-dependencies.xml | 74 ++++---
46 files changed, 1265 insertions(+), 117 deletions(-)
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/kafka/KafkaAppender.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/kafka/KafkaAppender.java
index ef4d656..73d95a8 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/kafka/KafkaAppender.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/kafka/KafkaAppender.java
@@ -67,8 +67,8 @@ public final class KafkaAppender extends AbstractAppender {
AbstractLifeCycle.LOGGER.error("No layout provided for KafkaAppender");
return null;
}
- final KafkaManager kafkaManager = new KafkaManager(getConfiguration().getLoggerContext(), getName(), topic,
- syncSend, getPropertyArray(), key);
+ final KafkaManager kafkaManager = KafkaManager.getManager(getConfiguration().getLoggerContext(),
+ getName(), topic, syncSend, getPropertyArray(), key);
return new KafkaAppender(getName(), layout, getFilter(), isIgnoreExceptions(), kafkaManager,
getPropertyArray());
}
@@ -108,8 +108,8 @@ public final class KafkaAppender extends AbstractAppender {
AbstractLifeCycle.LOGGER.error("No layout provided for KafkaAppender");
return null;
}
- final KafkaManager kafkaManager =
- new KafkaManager(configuration.getLoggerContext(), name, topic, true, properties, key);
+ final KafkaManager kafkaManager = KafkaManager.getManager(configuration.getLoggerContext(), name, topic,
+ true, properties, key);
return new KafkaAppender(name, layout, filter, ignoreExceptions, kafkaManager, null);
}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/kafka/KafkaManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/kafka/KafkaManager.java
index 9af11cd..8db4204 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/kafka/KafkaManager.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/kafka/KafkaManager.java
@@ -31,6 +31,7 @@ import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.clients.producer.RecordMetadata;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.AbstractManager;
+import org.apache.logging.log4j.core.appender.ManagerFactory;
import org.apache.logging.log4j.core.config.Property;
import org.apache.logging.log4j.core.util.Log4jThread;
@@ -50,7 +51,11 @@ public class KafkaManager extends AbstractManager {
private final String topic;
private final String key;
private final boolean syncSend;
+ private static final KafkaManagerFactory factory = new KafkaManagerFactory();
+ /*
+ * The Constructor should have been declared private as all Managers are create by the internal factory;
+ */
public KafkaManager(final LoggerContext loggerContext, final String name, final String topic, final boolean syncSend,
final Property[] properties, final String key) {
super(loggerContext, name);
@@ -135,4 +140,38 @@ public class KafkaManager extends AbstractManager {
return topic;
}
+ public static KafkaManager getManager(final LoggerContext loggerContext, final String name, final String topic,
+ final boolean syncSend, final Property[] properties, final String key) {
+ StringBuilder sb = new StringBuilder(name);
+ for (Property prop: properties) {
+ sb.append(" ").append(prop.getName()).append("=").append(prop.getValue());
+ }
+ return getManager(sb.toString(), factory, new FactoryData(loggerContext, topic, syncSend, properties, key));
+ }
+
+ private static class FactoryData {
+ private final LoggerContext loggerContext;
+ private final String topic;
+ private final boolean syncSend;
+ private final Property[] properties;
+ private final String key;
+
+ public FactoryData(final LoggerContext loggerContext, final String topic, final boolean syncSend,
+ final Property[] properties, final String key) {
+ this.loggerContext = loggerContext;
+ this.topic = topic;
+ this.syncSend = syncSend;
+ this.properties = properties;
+ this.key = key;
+ }
+
+ }
+
+ private static class KafkaManagerFactory implements ManagerFactory<KafkaManager, FactoryData> {
+ @Override
+ public KafkaManager createManager(String name, FactoryData data) {
+ return new KafkaManager(data.loggerContext, name, data.topic, data.syncSend, data.properties, data.key);
+ }
+ }
+
}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractJacksonLayout.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractJacksonLayout.java
index 985ef7f..bae404c 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractJacksonLayout.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractJacksonLayout.java
@@ -22,19 +22,25 @@ import java.nio.charset.Charset;
import java.util.LinkedHashMap;
import java.util.Map;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.ThreadContext;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
-import org.apache.logging.log4j.core.impl.Log4jLogEvent;
+import org.apache.logging.log4j.core.impl.ThrowableProxy;
import org.apache.logging.log4j.core.jackson.XmlConstants;
import org.apache.logging.log4j.core.lookup.StrSubstitutor;
+import org.apache.logging.log4j.core.time.Instant;
import org.apache.logging.log4j.core.util.KeyValuePair;
import org.apache.logging.log4j.core.util.StringBuilderWriter;
-import org.apache.logging.log4j.message.ReusableMessage;
+import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.util.ReadOnlyStringMap;
import org.apache.logging.log4j.util.Strings;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
+import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonRootName;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import com.fasterxml.jackson.core.JsonGenerationException;
@@ -274,19 +280,15 @@ abstract class AbstractJacksonLayout extends AbstractStringLayout {
}
}
- private static LogEvent convertMutableToLog4jEvent(final LogEvent event) {
- // TODO Jackson-based layouts have certain filters set up for Log4jLogEvent.
- // TODO Need to set up the same filters for MutableLogEvent but don't know how...
- // This is a workaround.
- return event instanceof ReusableMessage ? Log4jLogEvent.createMemento(event) : event;
- }
-
protected Object wrapLogEvent(final LogEvent event) {
if (additionalFields.length > 0) {
// Construct map for serialization - note that we are intentionally using original LogEvent
final Map<String, String> additionalFieldsMap = resolveAdditionalFields(event);
// This class combines LogEvent with AdditionalFields during serialization
return new LogEventWithAdditionalFields(event, additionalFieldsMap);
+ } else if (event instanceof Message) {
+ // If the LogEvent implements the Messagee interface Jackson will not treat is as a LogEvent.
+ return new ReadOnlyLogEventWrapper(event);
} else {
// No additional fields, return original object
return event;
@@ -314,7 +316,7 @@ abstract class AbstractJacksonLayout extends AbstractStringLayout {
public void toSerializable(final LogEvent event, final Writer writer)
throws JsonGenerationException, JsonMappingException, IOException {
- objectWriter.writeValue(writer, wrapLogEvent(convertMutableToLog4jEvent(event)));
+ objectWriter.writeValue(writer, wrapLogEvent(event));
writer.write(eol);
if (includeNullDelimiter) {
writer.write('\0');
@@ -358,4 +360,124 @@ abstract class AbstractJacksonLayout extends AbstractStringLayout {
this.valueNeedsLookup = AbstractJacksonLayout.valueNeedsLookup(this.value);
}
}
+
+ private static class ReadOnlyLogEventWrapper implements LogEvent {
+
+ @JsonIgnore
+ private final LogEvent event;
+
+ public ReadOnlyLogEventWrapper(LogEvent event) {
+ this.event = event;
+ }
+
+ @Override
+ public LogEvent toImmutable() {
+ return event.toImmutable();
+ }
+
+ @Override
+ public Map<String, String> getContextMap() {
+ return event.getContextMap();
+ }
+
+ @Override
+ public ReadOnlyStringMap getContextData() {
+ return event.getContextData();
+ }
+
+ @Override
+ public ThreadContext.ContextStack getContextStack() {
+ return event.getContextStack();
+ }
+
+ @Override
+ public String getLoggerFqcn() {
+ return event.getLoggerFqcn();
+ }
+
+ @Override
+ public Level getLevel() {
+ return event.getLevel();
+ }
+
+ @Override
+ public String getLoggerName() {
+ return event.getLoggerName();
+ }
+
+ @Override
+ public Marker getMarker() {
+ return event.getMarker();
+ }
+
+ @Override
+ public Message getMessage() {
+ return event.getMessage();
+ }
+
+ @Override
+ public long getTimeMillis() {
+ return event.getTimeMillis();
+ }
+
+ @Override
+ public Instant getInstant() {
+ return event.getInstant();
+ }
+
+ @Override
+ public StackTraceElement getSource() {
+ return event.getSource();
+ }
+
+ @Override
+ public String getThreadName() {
+ return event.getThreadName();
+ }
+
+ @Override
+ public long getThreadId() {
+ return event.getThreadId();
+ }
+
+ @Override
+ public int getThreadPriority() {
+ return event.getThreadPriority();
+ }
+
+ @Override
+ public Throwable getThrown() {
+ return event.getThrown();
+ }
+
+ @Override
+ public ThrowableProxy getThrownProxy() {
+ return event.getThrownProxy();
+ }
+
+ @Override
+ public boolean isEndOfBatch() {
+ return event.isEndOfBatch();
+ }
+
+ @Override
+ public boolean isIncludeLocation() {
+ return event.isIncludeLocation();
+ }
+
+ @Override
+ public void setEndOfBatch(boolean endOfBatch) {
+
+ }
+
+ @Override
+ public void setIncludeLocation(boolean locationRequired) {
+
+ }
+
+ @Override
+ public long getNanoTime() {
+ return event.getNanoTime();
+ }
+ }
}
diff --git a/log4j-docker/pom.xml b/log4j-docker/pom.xml
index f4eeea2..74d7090 100644
--- a/log4j-docker/pom.xml
+++ b/log4j-docker/pom.xml
@@ -31,6 +31,8 @@
<log4jParentDir>${basedir}/..</log4jParentDir>
<docLabel>Log4j Docker Library Documentation</docLabel>
<projectDir>/docker</projectDir>
+ <maven.compiler.source>1.8</maven.compiler.source>
+ <maven.compiler.target>1.8</maven.compiler.target>
<module.name>org.apache.logging.log4j.docker</module.name>
</properties>
<dependencies>
@@ -63,6 +65,25 @@
</dependencies>
<build>
<plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-toolchains-plugin</artifactId>
+ <version>1.1</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>toolchain</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <toolchains>
+ <jdk>
+ <version>[8, )</version>
+ </jdk>
+ </toolchains>
+ </configuration>
+ </plugin>
<!-- Include the standard NOTICE and LICENSE -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
diff --git a/log4j-docker/src/main/java/org/apache/logging/log4j/docker/model/Container.java b/log4j-docker/src/main/java/org/apache/logging/log4j/docker/model/Container.java
index cb8be45..aee7c51 100644
--- a/log4j-docker/src/main/java/org/apache/logging/log4j/docker/model/Container.java
+++ b/log4j-docker/src/main/java/org/apache/logging/log4j/docker/model/Container.java
@@ -31,6 +31,12 @@ public class Container {
@JsonProperty("Names")
private List<String> names;
+ @JsonProperty("Path")
+ private String path;
+
+ @JsonProperty("Args")
+ private String[] args;
+
@JsonProperty("Image")
private String image;
diff --git a/log4j-docker/src/main/java/org/apache/logging/log4j/docker/model/IPAMConfig.java b/log4j-docker/src/main/java/org/apache/logging/log4j/docker/model/IPAMConfig.java
index acc3eeb..b17ad92 100644
--- a/log4j-docker/src/main/java/org/apache/logging/log4j/docker/model/IPAMConfig.java
+++ b/log4j-docker/src/main/java/org/apache/logging/log4j/docker/model/IPAMConfig.java
@@ -32,6 +32,9 @@ public class IPAMConfig {
@JsonProperty("Gateway")
private String gateway;
+ @JsonProperty("IPv4Address")
+ private String ipv4Address;
+
public String getSubnet() {
return subnet;
}
@@ -55,4 +58,12 @@ public class IPAMConfig {
public void setGateway(String gateway) {
this.gateway = gateway;
}
+
+ public String getIpv4Address() {
+ return ipv4Address;
+ }
+
+ public void setIpv4Address(String ipv4Address) {
+ this.ipv4Address = ipv4Address;
+ }
}
diff --git a/log4j-docker/src/main/java/org/apache/logging/log4j/docker/model/Network.java b/log4j-docker/src/main/java/org/apache/logging/log4j/docker/model/Network.java
index c542252..5e0674a 100644
--- a/log4j-docker/src/main/java/org/apache/logging/log4j/docker/model/Network.java
+++ b/log4j-docker/src/main/java/org/apache/logging/log4j/docker/model/Network.java
@@ -24,13 +24,13 @@ import com.fasterxml.jackson.annotation.JsonProperty;
public class Network {
@JsonProperty("IPAMConfig")
- private String ipamConfig;
+ private IPAMConfig ipamConfig;
@JsonProperty("Links")
private String links;
@JsonProperty("Aliases")
- private String aliases;
+ private String[] aliases;
@JsonProperty("NetworkID")
private String networkId;
@@ -62,11 +62,11 @@ public class Network {
@JsonProperty("DriverOpts")
private String driverOpts;
- public String getIpamConfig() {
+ public IPAMConfig getIpamConfig() {
return ipamConfig;
}
- public void setIpamConfig(String ipamConfig) {
+ public void setIpamConfig(IPAMConfig ipamConfig) {
this.ipamConfig = ipamConfig;
}
@@ -78,11 +78,11 @@ public class Network {
this.links = links;
}
- public String getAliases() {
+ public String[] getAliases() {
return aliases;
}
- public void setAliases(String aliases) {
+ public void setAliases(String[] aliases) {
this.aliases = aliases;
}
diff --git a/log4j-docker/src/main/java/org/apache/logging/log4j/docker/model/NetworkSettings.java b/log4j-docker/src/main/java/org/apache/logging/log4j/docker/model/NetworkSettings.java
index 7dfaa68..a1835d6 100644
--- a/log4j-docker/src/main/java/org/apache/logging/log4j/docker/model/NetworkSettings.java
+++ b/log4j-docker/src/main/java/org/apache/logging/log4j/docker/model/NetworkSettings.java
@@ -28,6 +28,55 @@ public class NetworkSettings {
@JsonProperty("Networks")
private Map<String, Network> networks;
+ @JsonProperty("Bridge")
+ private String bridge;
+
+ @JsonProperty("SandboxID")
+ private String sandboxId;
+
+ @JsonProperty("HairpinMode")
+ private boolean hairpinMode;
+
+ @JsonProperty("LinkLocalIPv6Address")
+ private String linkLocalIPv6Address;
+
+ @JsonProperty("LinkLocalIPv6PrefixLen")
+ private int linkLocalIPv6PrefixLen;
+
+ @JsonProperty("Ports")
+ private Map<String, String> ports;
+
+ @JsonProperty("SandboxKey")
+ private String sandboxKey;
+
+ @JsonProperty("SecondaryIPAddresses")
+ private String secondaryIPaddresses;
+
+ @JsonProperty("EndpointID")
+ private String endpointId;
+
+ @JsonProperty("Gateway")
+ private String gateway;
+
+ @JsonProperty("GlobalIPv6Address")
+ private String globalIPv6Address;
+
+ @JsonProperty("GlobalIPv6PrefixLen")
+ private int globalIPv6PrefixLen;
+
+ @JsonProperty("IPAddress")
+ private String ipAddress;
+
+ @JsonProperty("IPPrefixLen")
+ private int ipPrefixLen;
+
+ @JsonProperty("IPv6Gateway")
+ private String ipv6Gateway;
+
+ @JsonProperty("MacAddress")
+ private String macAddress;
+
+
public Map<String, Network> getNetworks() {
return networks;
}
@@ -35,4 +84,132 @@ public class NetworkSettings {
public void setNetworks(Map<String, Network> networks) {
this.networks = networks;
}
+
+ public String getBridge() {
+ return bridge;
+ }
+
+ public void setBridge(String bridge) {
+ this.bridge = bridge;
+ }
+
+ public String getSandboxId() {
+ return sandboxId;
+ }
+
+ public void setSandboxId(String sandboxId) {
+ this.sandboxId = sandboxId;
+ }
+
+ public boolean isHairpinMode() {
+ return hairpinMode;
+ }
+
+ public void setHairpinMode(boolean hairpinMode) {
+ this.hairpinMode = hairpinMode;
+ }
+
+ public String getLinkLocalIPv6Address() {
+ return linkLocalIPv6Address;
+ }
+
+ public void setLinkLocalIPv6Address(String linkLocalIPv6Address) {
+ this.linkLocalIPv6Address = linkLocalIPv6Address;
+ }
+
+ public int getLinkLocalIPv6PrefixLen() {
+ return linkLocalIPv6PrefixLen;
+ }
+
+ public void setLinkLocalIPv6PrefixLen(int linkLocalIPv6PrefixLen) {
+ this.linkLocalIPv6PrefixLen = linkLocalIPv6PrefixLen;
+ }
+
+ public Map<String, String> getPorts() {
+ return ports;
+ }
+
+ public void setPorts(Map<String, String> ports) {
+ this.ports = ports;
+ }
+
+ public String getSandboxKey() {
+ return sandboxKey;
+ }
+
+ public void setSandboxKey(String sandboxKey) {
+ this.sandboxKey = sandboxKey;
+ }
+
+ public String getSecondaryIPaddresses() {
+ return secondaryIPaddresses;
+ }
+
+ public void setSecondaryIPaddresses(String secondaryIPaddresses) {
+ this.secondaryIPaddresses = secondaryIPaddresses;
+ }
+
+ public String getEndpointId() {
+ return endpointId;
+ }
+
+ public void setEndpointId(String endpointId) {
+ this.endpointId = endpointId;
+ }
+
+ public String getGateway() {
+ return gateway;
+ }
+
+ public void setGateway(String gateway) {
+ this.gateway = gateway;
+ }
+
+ public String getGlobalIPv6Address() {
+ return globalIPv6Address;
+ }
+
+ public void setGlobalIPv6Address(String globalIPv6Address) {
+ this.globalIPv6Address = globalIPv6Address;
+ }
+
+ public int getGlobalIPv6PrefixLen() {
+ return globalIPv6PrefixLen;
+ }
+
+ public void setGlobalIPv6PrefixLen(int globalIPv6PrefixLen) {
+ this.globalIPv6PrefixLen = globalIPv6PrefixLen;
+ }
+
+ public String getIpAddress() {
+ return ipAddress;
+ }
+
+ public void setIpAddress(String ipAddress) {
+ this.ipAddress = ipAddress;
+ }
+
+ public int getIpPrefixLen() {
+ return ipPrefixLen;
+ }
+
+ public void setIpPrefixLen(int ipPrefixLen) {
+ this.ipPrefixLen = ipPrefixLen;
+ }
+
+ public String getIpv6Gateway() {
+ return ipv6Gateway;
+ }
+
+ public void setIpv6Gateway(String ipv6Gateway) {
+ this.ipv6Gateway = ipv6Gateway;
+ }
+
+ public String getMacAddress() {
+ return macAddress;
+ }
+
+ public void setMacAddress(String macAddress) {
+ this.macAddress = macAddress;
+ }
}
diff --git a/log4j-docker/src/site/markdown/index.md.vm b/log4j-docker/src/site/markdown/index.md.vm
new file mode 100644
index 0000000..42ee2cc
--- /dev/null
+++ b/log4j-docker/src/site/markdown/index.md.vm
@@ -0,0 +1,55 @@
+<!-- vim: set syn=markdown : -->
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+#set($dollar = '$')
+#set($h1='#')
+#set($h2='##')
+
+$h1 Log4j Docker Support
+
+Log4j supports Docker by providing a Lookup to retrieve container information.
+
+$h2 Accessing Docker
+
+The Log4j Docker support requires access to the Docker REST intrerface. In practical terms this means the
+application either needs access to unix:///var/run/docker.sock through a volume mount (not recommended),
+bind Docker to another host/port or unix socket. or use a proxy application to provide access. The
+Log4j Spring Cload sample application uses a socat proxy to access Docker.
+
+$h2 Lookup Attributes
+
+Log4j Docker provides access to the following container attributes:
+
+* containerId - The full id assigned to the container.
+* containerName - The name assigned to the container.
+* imageId - The id assigned to the image.
+* imageName - The name assigned to the image.
+* shortContainerId - The first 12 characters of the container id.
+* shortImageId - The first 12 characters of the image id.
+
+#set( $D = '${' )
+#set( $container = 'docker:containerId}')
+Attributes may be accessed by adding
+```
+$D$container
+```
+to the configuration. Note that docker variables are only resolved once during logging initializaton so they
+shouldn't be referenced with more than one '$' character.
+
+$h2 Requirements
+Log4j Docker requires Log4j Core, Log4j API and a minimum of Java 8.
+For more information, see [Runtime Dependencies](../runtime-dependencies.html).
diff --git a/log4j-docker/src/site/site.xml b/log4j-docker/src/site/site.xml
new file mode 100644
index 0000000..7322f3b
--- /dev/null
+++ b/log4j-docker/src/site/site.xml
@@ -0,0 +1,52 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<project name="Log4j Docker Support"
+ xmlns="http://maven.apache.org/DECORATION/1.4.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/DECORATION/1.4.0 http://maven.apache.org/xsd/decoration-1.4.0.xsd">
+ <body>
+ <links>
+ <item name="Apache" href="http://www.apache.org/" />
+ <item name="Logging Services" href="http://logging.apache.org/"/>
+ <item name="Log4j" href="../index.html"/>
+ </links>
+
+ <!-- Component-specific reports -->
+ <menu ref="reports"/>
+
+ <!-- Overall Project Info -->
+ <menu name="Log4j Project Information" img="icon-info-sign">
+ <item name="Dependencies" href="../dependencies.html" />
+ <item name="Dependency Convergence" href="../dependency-convergence.html" />
+ <item name="Dependency Management" href="../dependency-management.html" />
+ <item name="Project Team" href="../team-list.html" />
+ <item name="Mailing Lists" href="../mail-lists.html" />
+ <item name="Issue Tracking" href="../issue-tracking.html" />
+ <item name="Project License" href="../license.html" />
+ <item name="Source Repository" href="../source-repository.html" />
+ <item name="Project Summary" href="../project-summary.html" />
+ </menu>
+
+ <menu name="Log4j Project Reports" img="icon-cog">
+ <item name="Changes Report" href="../changes-report.html" />
+ <item name="JIRA Report" href="../jira-report.html" />
+ <item name="Surefire Report" href="../surefire-report.html" />
+ <item name="RAT Report" href="../rat-report.html" />
+ </menu>
+ </body>
+</project>
diff --git a/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/BatchEvent.java b/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/BatchEvent.java
index 41af4fa..9a6f86d 100644
--- a/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/BatchEvent.java
+++ b/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/BatchEvent.java
@@ -35,4 +35,8 @@ public class BatchEvent {
public List<Event> getEvents() {
return events;
}
+
+ public int size() {
+ return events.size();
+ }
}
diff --git a/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeAppender.java b/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeAppender.java
index 210aadd..d99cbf7 100644
--- a/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeAppender.java
+++ b/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeAppender.java
@@ -19,7 +19,6 @@ package org.apache.logging.log4j.flume.appender;
import java.io.Serializable;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicLong;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.Filter;
diff --git a/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeAvroManager.java b/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeAvroManager.java
index c1dd288..d1767e5 100644
--- a/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeAvroManager.java
+++ b/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeAvroManager.java
@@ -16,6 +16,7 @@
*/
package org.apache.logging.log4j.flume.appender;
+import java.util.List;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
@@ -24,6 +25,7 @@ import org.apache.flume.api.RpcClient;
import org.apache.flume.api.RpcClientFactory;
import org.apache.logging.log4j.core.appender.AppenderLoggingException;
import org.apache.logging.log4j.core.appender.ManagerFactory;
+import org.apache.logging.log4j.util.Timer;
/**
* Manager for FlumeAvroAppenders.
@@ -50,7 +52,7 @@ public class FlumeAvroManager extends AbstractFlumeManager {
private final int current = 0;
- private RpcClient rpcClient = null;
+ private volatile RpcClient rpcClient = null;
private BatchEvent batchEvent = new BatchEvent();
private long nextSend = 0;
@@ -97,9 +99,9 @@ public class FlumeAvroManager extends AbstractFlumeManager {
if (batchSize <= 0) {
batchSize = 1;
- }
-
- final StringBuilder sb = new StringBuilder("FlumeAvro[");
+ };
+ final StringBuilder sb = new StringBuilder(name);
+ sb.append(" FlumeAvro[");
boolean first = true;
for (final Agent agent : agents) {
if (!first) {
@@ -149,9 +151,13 @@ public class FlumeAvroManager extends AbstractFlumeManager {
return delayMillis;
}
- public synchronized void send(final BatchEvent events) {
+ public void send(final BatchEvent events) {
if (rpcClient == null) {
- rpcClient = connect(agents, retries, connectTimeoutMillis, requestTimeoutMillis);
+ synchronized (this) {
+ if (rpcClient == null) {
+ rpcClient = connect(agents, retries, connectTimeoutMillis, requestTimeoutMillis);
+ }
+ }
}
if (rpcClient != null) {
@@ -175,7 +181,7 @@ public class FlumeAvroManager extends AbstractFlumeManager {
}
@Override
- public synchronized void send(final Event event) {
+ public void send(final Event event) {
if (batchSize == 1) {
if (rpcClient == null) {
rpcClient = connect(agents, retries, connectTimeoutMillis, requestTimeoutMillis);
@@ -199,14 +205,22 @@ public class FlumeAvroManager extends AbstractFlumeManager {
throw new AppenderLoggingException("No Flume agents are available");
}
} else {
- batchEvent.addEvent(event);
- final int eventCount = batchEvent.getEvents().size();
- if (eventCount == 1) {
- nextSend = System.nanoTime() + delayNanos;
+ int eventCount;
+ BatchEvent batch = null;
+ synchronized(batchEvent) {
+ batchEvent.addEvent(event);
+ eventCount = batchEvent.size();
+ long now = System.nanoTime();
+ if (eventCount == 1) {
+ nextSend = now + delayNanos;
+ }
+ if (eventCount >= batchSize || now >= nextSend) {
+ batch = batchEvent;
+ batchEvent = new BatchEvent();
+ }
}
- if (eventCount >= batchSize || System.nanoTime() >= nextSend) {
- send(batchEvent);
- batchEvent = new BatchEvent();
+ if (batch != null) {
+ send(batch);
}
}
}
diff --git a/log4j-samples/log4j-samples-flume-embedded/pom.xml b/log4j-samples/log4j-samples-flume-embedded/pom.xml
index 43982a1..c1895b3 100644
--- a/log4j-samples/log4j-samples-flume-embedded/pom.xml
+++ b/log4j-samples/log4j-samples-flume-embedded/pom.xml
@@ -28,6 +28,7 @@
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <deploy.plugin.version>2.8.2</deploy.plugin.version>
</properties>
<dependencies>
<dependency>
diff --git a/log4j-samples/log4j-samples-flume-remote/pom.xml b/log4j-samples/log4j-samples-flume-remote/pom.xml
index 8f33337..228bcec 100644
--- a/log4j-samples/log4j-samples-flume-remote/pom.xml
+++ b/log4j-samples/log4j-samples-flume-remote/pom.xml
@@ -28,6 +28,7 @@
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <deploy.plugin.version>2.8.2</deploy.plugin.version>
</properties>
<dependencies>
<dependency>
diff --git a/log4j-spring-cloud-config/log4j-spring-cloud-config-client/pom.xml b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/pom.xml
index 6ab556b..1317ced 100644
--- a/log4j-spring-cloud-config/log4j-spring-cloud-config-client/pom.xml
+++ b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/pom.xml
@@ -26,9 +26,9 @@
<artifactId>log4j-spring-cloud-config-client</artifactId>
<packaging>jar</packaging>
<name>Apache Log4j Spring Cloud Config Client Support</name>
- <description></description>
+ <description>Apache Log4j Spring Cloud Config Client Support</description>
<properties>
- <log4jParentDir>${basedir}/..</log4jParentDir>
+ <log4jParentDir>${basedir}/../..</log4jParentDir>
<docLabel>Log4j Spring Cloud Config Client Documentation</docLabel>
<projectDir>/log4j-spring-cloud-config-client</projectDir>
<module.name>org.apache.logging.log4j.spring.cloud.config.client</module.name>
diff --git a/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/site/markdown/index.md b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/site/markdown/index.md
new file mode 100644
index 0000000..5530859
--- /dev/null
+++ b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/site/markdown/index.md
@@ -0,0 +1,88 @@
+<!-- vim: set syn=markdown : -->
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+# Log4j Spring Cloud Configuration
+
+This module allows logging configuration files managed in be dynamically updated when new versions are available in
+Spring Cloud Configuration.
+
+## Overview
+
+Spring Boot applications initialize logging 3 times.
+1. SpringApplication declares a Logger. This Logger will be initialized using Log4j's "normal" mechanisms. Thus
+a system property named log4j2.configurationFile will be checked to see if a specific configuration file has been
+provided, otherwise it will search for a configuration file on the classpath. The property may also be declare
+in log4j2.component.properties.
+
+## Usage
+
+Log4j configuration files that specify a monitor interval of greater than zero will use polling to determine
+whether the configuration has been updated. If the monitor interval is zero then Log4j will listen for notifications
+from Spring Cloud Config and will check for configuration changes each time an event is generated. If the
+monitor interval is less than zero Log4j will not check for changes to the logging configuration.
+
+When referencing a configuration located in Spring Cloud Config the configuration should be referenced similar to
+
+```
+log4j.configurationFile=http://host.docker.internal:8888/ConfigService/sampleapp/default/master/log4j2.xml
+```
+When running in a docker container host.docker.internal may be used as the domain name to access an application
+running on the same hose outside of the docker container. Note that in accordance with Spring Cloud Config
+practices but the application, profile, and label should be specified in the url.
+
+The Spring Cloud Config support also allows connections using TLS and/or basic authentication. When using basic
+authentication the userid and password may be specified as system properties or in log4j2.component.properties as
+
+```
+log4j2.configurationUserName=guest
+log4j2.configurationPassword=guest
+```
+Note that Log4j currently does not support encrypting the password.
+
+If more extensive authentication is required an ```AuthorizationProvider``` can be implemented and defined in
+the log4j2.authorizationProvider system property or in log4j2.component.properties.
+
+TLS can be enabled by adding the following system properties or defining them in log4j2.component.properties
+
+| Property | Optional or Default Value | Description |
+| ------------- |-------|:-------------|
+| log4j2.trustStore.location | Optional | The location of the trust store. If not provided the default trust store will be used.|
+| log4j2.trustStore.password | Optional | Password needed to access the trust store. |
+| log4j2.trustStore.passwordFile | Optinoal | The location of a file that contains the password for the trust store. |
+| log4j2.trustStore.passwordEnvironmentVariable | Optional | The name of the environment variable that contains the trust store password. |
+| log4j2.trustStore.keyStoreType | Required if keystore location provided | The type of key store. |
+| log4j2.trustStore.keyManagerFactoryAlgorithm | Optional | Java cryptographic algorithm. |
+| log4j2.keyStore.location | Optional | The location of the key store. If not provided the default key store will be used.|
+| log4j2.keyStore.password | Optional | Password needed to access the key store. |
+| log4j2.keyStore.passwordFile | Optional | The location of a file that contains the password for the key store. |
+| log4j2.keyStore.passwordEnvironmentVariable | Optional | The name of the environment variable that contains the key store password.|
+| log4j2.keyStore.type | Required if trust store location provided. | The type of key store. |
+| log4j2.keyStore.keyManagerFactoryAlgorithm | Optional | Java cryptographic algorithm. |
+| log4j2.ssl.verifyHostName | false | true or false |
+
+
+
+## Requirements
+
+The Log4j 2 Spring Cloud Configuration integration has a dependency on Log4j 2 API, Log4j 2 Core, and
+Spring Cloud Configuration versions 2.0.3.RELEASE or 2.1.1.RELEASE or later versions it either release series.
+For more information, see [Runtime Dependencies](../../runtime-dependencies.html).
+
+
+
+
diff --git a/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/site/site.xml b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/site/site.xml
new file mode 100644
index 0000000..5abfffd
--- /dev/null
+++ b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/site/site.xml
@@ -0,0 +1,52 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<project name="Log4j Spring Cloud Config Integration"
+ xmlns="http://maven.apache.org/DECORATION/1.4.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/DECORATION/1.4.0 http://maven.apache.org/xsd/decoration-1.4.0.xsd">
+ <body>
+ <links>
+ <item name="Apache" href="http://www.apache.org/" />
+ <item name="Logging Services" href="http://logging.apache.org/"/>
+ <item name="Log4j" href="../index.html"/>
+ </links>
+
+ <!-- Component-specific reports -->
+ <menu ref="reports"/>
+
+ <!-- Overall Project Info -->
+ <menu name="Log4j Project Information" img="icon-info-sign">
+ <item name="Dependencies" href="../dependencies.html" />
+ <item name="Dependency Convergence" href="../dependency-convergence.html" />
+ <item name="Dependency Management" href="../dependency-management.html" />
+ <item name="Project Team" href="../team-list.html" />
+ <item name="Mailing Lists" href="../mail-lists.html" />
+ <item name="Issue Tracking" href="../issue-tracking.html" />
+ <item name="Project License" href="../license.html" />
+ <item name="Source Repository" href="../source-repository.html" />
+ <item name="Project Summary" href="../project-summary.html" />
+ </menu>
+
+ <menu name="Log4j Project Reports" img="icon-cog">
+ <item name="Changes Report" href="../changes-report.html" />
+ <item name="JIRA Report" href="../jira-report.html" />
+ <item name="Surefire Report" href="../surefire-report.html" />
+ <item name="RAT Report" href="../rat-report.html" />
+ </menu>
+ </body>
+</project>
diff --git a/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/README.md b/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/README.md
index 93448e3..3251015 100644
--- a/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/README.md
+++ b/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/README.md
@@ -1,53 +1,33 @@
-#Local Development
-###Prerequisites
-Note: This guide uses Homebrew (package manage for macOS). It is not necessary to use Homebrew, but it does
-simplify the installation process.
-* Install Homebrew: `/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"`
- * If you already have Homebrew installed, make sure it is updated: `brew update`
-* Add the cask-versions tap for Homebrew: `brew tap homebrew/cask-versions`
-* Install Docker: `brew cask install docker`
-* Install docker-machine: `brew install docker-machine`
-* Set up Docker connection to nexus: https://confluence.nextiva.xyz/display/DP/Docker+connection+to+nexus
-* Find OpenJDK 11 cask `brew search java`
- * Currently the cask for java 11 is just named 'java', but this may change in the future. You can verify by
- running `brew cask info <caskName>` to verify the version.
-* Install OpenJDK 11 cask: `brew cask install java`
-* Set JAVA_HOME to java 11 installation directory (/Library/Java/JavaVirtualMachines/jdk-11.0.1.jdk/Contents/Home)
- * If you need to use and switch between multiple Java versions, consider using jEnv to simplify this process
- http://www.jenv.be
+##Log4j Spring Cloud Sample Application
-###Starting the Application
-* Start local postgres image `./docker/up.sh`
-* Compile and start local application image `./docker/restartApp.sh`
- * Alternatively: Run FulfillmentApplication.java as a Spring Boot application using java -jar target/fulfillment-service.jar.
-* Local swagger URL is available at http://localhost:8080/swagger-ui.html
+This application uses Spring Boot and reads the logging configuration from the companion Spring Cloud Config Server
+project. The log4j2.xml file is located in the config-repo directory in that project.
-# Java client
-* Using the fulfillment-service-client is recommended when integrating Java applications with this service.
-###Using the client
-* Add the latest version of fulfillment-service-client as a dependency to your application.
-* Import the OrderSubmissionServiceClient class into your application configuration.
-* Specify fulfillment-service.url as a property
+This sample packages the application in a docker container that is packaged with rabbit-mq (to allow dynamic updates
+from Spring Cloud Config), fluent-bit (to test as a log forwarder), and Apache Flume (to test as a log forwarder).
+###Prerequisites
+Note: This guide assumes you already have docker installed. If you do not you may either use homebrew to install
+it or follow the instructions at https://docs.docker.com/docker-for-mac/install/.
-# Database setup
-Docker will create a container for postgres - a local version of Postgresql does not need to be installed.
-Before starting the application the tables in Postgres must be created. Until this is automatced login to pgAdmin
-using fulfillment_app/fulfillment_app as the credentialsand run the script in
-fulfillment-service-web/resources/postgres/schema.sql.
+Like Log4j, the sample app uses the Maven toolchains plugin. The sample app may be built with Java 8 but is
+configured to run in a docker container with Java 11.
-# Environment properties
-* This applications uses Kubernetes Config Maps to configure properties for an environment. The properties
-configured in application.yml will be used unless overridden at https://git.nextiva.xyz/projects/REL/repos/k8s-platform/browse
+The KafkaAppender requires a Kafka instance to write to. On MacOS a Kafka instance can be created by
+```
+brew install kafka
+zookeeper-server-start /usr/local/etc/kafka/zookeeper.properties & kafka-server-start /usr/local/etc/kafka/server.properties
+```
+
+###Starting the Application
+* Start the companion rabbit-mq, fluent-bit and flume images `./docker/up.sh`
+* Compile and start local application image `./docker/restartApp.sh`
+* The application exposes two endpoints.
+ 1. http://localhost:8080/sample/log?threads=1&count=100000 This endpoint will log up to 100,000 events using
+ 1 or more threads.
+ 1. http://localhost:8080/sample/exception This endpoint generates an exception that can be used to verify whether
+ multiline logging works with the chosen set of components.
-# Swagger
-* Dev: https://fulfillment-service.dev.nextiva.io/swagger-ui.ml
-* Rc: https://fulfillment-service.qa.nextiva.io/swagger-ui.html
-* Prod: https://fulfillment-service.prod.nextiva.io/swagger-ui.html
+###Viewing the logs
-#Automated Testing
-<!---
-* Unit tests can be run using `mvn test -Dgroups=UnitTest`
-* Integration tests (do not require Fulfillment Service but do require connection to third parties)
-can be run using `mvn test -Dgroups=IntegrationTest`
--->
-* Functional tests (those that require Fulfillment Service running) can be run using `mvn -P integration-tests verify`
\ No newline at end of file
+Accessing the log files varies depending on the appending being used.
+
diff --git a/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/docker/app-compose.yml b/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/docker/app-compose.yml
new file mode 100755
index 0000000..01cb293
--- /dev/null
+++ b/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/docker/app-compose.yml
@@ -0,0 +1,21 @@
+version: "3"
+services:
+ sampleapp:
+ container_name: sampleapp
+ image: sampleapp
+ environment:
+ DOCKER_URI: http://socat:1234
+ SERVICE_PARAMS: --spring.config.location=classpath:/,classpath:/application-local-docker.yml
+ ports:
+ - "5005:5005"
+ - "8080:4567"
+ networks:
+ sample_network:
+ aliases:
+ - sampleapp
+
+networks:
+ sample_network:
+
+volumes:
+ pgdata:
\ No newline at end of file
diff --git a/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/docker/docker-compose.yml b/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/docker/docker-compose.yml
index 10a5b13..76a0d09 100755
--- a/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/docker/docker-compose.yml
+++ b/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/docker/docker-compose.yml
@@ -65,6 +65,7 @@ services:
aliases:
- flume
+
networks:
sample_network:
diff --git a/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/pom.xml b/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/pom.xml
index 0122205..f059876 100644
--- a/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/pom.xml
+++ b/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/pom.xml
@@ -18,6 +18,8 @@
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <maven.compiler.source>1.8</maven.compiler.source>
+ <maven.compiler.target>1.8</maven.compiler.target>
<!--<manifestfile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestfile>-->
</properties>
@@ -114,6 +116,11 @@
<version>1.9.0</version>
</dependency>
<dependency>
+ <groupId>org.apache.kafka</groupId>
+ <artifactId>kafka-clients</artifactId>
+ <version>2.2.0</version>
+ </dependency>
+ <dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
@@ -131,6 +138,15 @@
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-site-plugin</artifactId>
+ <version>${site.plugin.version}</version>
+ <configuration>
+ <skip>true</skip>
+ <skipDeploy>true</skipDeploy>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-toolchains-plugin</artifactId>
<version>1.1</version>
<executions>
diff --git a/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-server/pom.xml b/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-server/pom.xml
index 684b0cf..900015b 100644
--- a/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-server/pom.xml
+++ b/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-server/pom.xml
@@ -36,7 +36,7 @@
<maven.google.code.findbugs.findbugs.version>3.0.1</maven.google.code.findbugs.findbugs.version>
<maven.jacoco.version>0.8.1</maven.jacoco.version>
<maven.pmd.version>3.9.0</maven.pmd.version>
-
+ <site.plugin.version>3.4</site.plugin.version>
<!-- maven plugin config -->
<pmd.failurePriority>2</pmd.failurePriority>
@@ -120,6 +120,15 @@
<build>
<plugins>
<plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-site-plugin</artifactId>
+ <version>${site.plugin.version}</version>
+ <configuration>
+ <skip>true</skip>
+ <skipDeploy>true</skipDeploy>
+ </configuration>
+ </plugin>
+ <plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
@@ -177,6 +186,15 @@
</plugins>
</build>
+ <distributionManagement>
+ <downloadUrl>https://logging.apache.org/log4j/2.x/download.html</downloadUrl>
+ <!-- site is only included to make maven-site-plugin stop complaining -->
+ <site>
+ <id>www.example.com</id>
+ <url>scp://www.example.com/www/docs/project/</url>
+ </site>
+ </distributionManagement>
+
<repositories>
<repository>
<id>spring-snapshots</id>
diff --git a/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-server/src/main/config-repo/log4j2.xml b/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-server/src/main/config-repo/log4j2.xml
index 1c061a7..1e5eea6 100644
--- a/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-server/src/main/config-repo/log4j2.xml
+++ b/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-server/src/main/config-repo/log4j2.xml
@@ -22,7 +22,7 @@
<KeyValuePair key="imageName" value="\${docker:imageName}"/>
</JsonLayout>
</Flume>
- <Flume name="avro" ignoreExceptions="false" batchSize="100" compress="false">
+ <Flume name="avro" ignoreExceptions="false" batchSize="1" compress="false">
<Agent host="flume" port="5050"/>
<JsonLayout properties="true" compact="true" eventEol="true">
<KeyValuePair key="containerId" value="\${docker:containerId}"/>
@@ -30,6 +30,25 @@
<KeyValuePair key="imageName" value="\${docker:imageName}"/>
</JsonLayout>
</Flume>
+ <Flume name="avroSyslog" ignoreExceptions="false" batchSize="100" compress="false">
+ <Agent host="flume" port="5050"/>
+ <RFC5424Layout enterpriseNumber="18060" includeMDC="true" mdcId="RequestContext" appName="Sample"
+ mdcPrefix="">
+ <LoggerFields>
+ <KeyValuePair key="containerId" value="\${docker:containerId}"/>
+ <KeyValuePair key="containerName" value="\${docker:containerName}"/>
+ <KeyValuePair key="imageName" value="\${docker:imageName}"/>
+ </LoggerFields>
+ </RFC5424Layout>
+ </Flume>
+ <Kafka name="Kafka" topic="log-test" syncSend="false">
+ <JsonLayout properties="true" compact="true" eventEol="true">
+ <KeyValuePair key="containerId" value="\${docker:containerId}"/>
+ <KeyValuePair key="containerName" value="\${docker:containerName}"/>
+ <KeyValuePair key="imageName" value="\${docker:imageName}"/>
+ </JsonLayout>
+ <Property name="bootstrap.servers">host.docker.internal:9092</Property>
+ </Kafka>
<Socket name="fluent-bit" host="fluent-bit" port="24221">
<JsonLayout properties="true" compact="true" eventEol="true">
<KeyValuePair key="containerId" value="\${docker:containerId}"/>
@@ -37,9 +56,34 @@
<KeyValuePair key="imageName" value="\${docker:imageName}"/>
</JsonLayout>
</Socket>
-
+ <RollingFile name="RollingFile" fileName="/var/log/sampleapp/app.log"
+ filePattern="/var/log/sampleapp/archive/app.log.%i">
+ <JsonLayout properties="true" compact="true" eventEol="true">
+ <KeyValuePair key="containerId" value="\${docker:containerId}"/>
+ <KeyValuePair key="containerName" value="\${docker:containerName}"/>
+ <KeyValuePair key="imageName" value="\${docker:imageName}"/>
+ </JsonLayout>
+ <SizeBasedTriggeringPolicy size="10MB" />
+ <DefaultRolloverStrategy max="5"/>
+ </RollingFile>
+ <RollingFile name="RollingSyslog" fileName="/var/log/sampleapp/syslog.log"
+ filePattern="/var/log/sampleapp/archive/syslog.log.%i">
+ <RFC5424Layout enterpriseNumber="18060" includeMDC="true" mdcId="RequestContext" appName="Sample"
+ mdcPrefix="">
+ <LoggerFields>
+ <KeyValuePair key="containerId" value="\${docker:containerId}"/>
+ <KeyValuePair key="containerName" value="\${docker:containerName}"/>
+ <KeyValuePair key="imageName" value="\${docker:imageName}"/>
+ </LoggerFields>
+ </RFC5424Layout>
+ <SizeBasedTriggeringPolicy size="10MB" />
+ <DefaultRolloverStrategy max="5"/>
+ </RollingFile>
</Appenders>
<Loggers>
+ <Logger name="org.apache.kafka" level="warn" additivity="false">
+ <AppenderRef ref="console"/>
+ </Logger>
<Logger name="org.apache.flume" level="warn" additivity="false">
<AppenderRef ref="console"/>
</Logger>
@@ -47,10 +91,7 @@
<AppenderRef ref="console"/>
</Logger>
<Root level="DEBUG">
- <AppenderRef ref="flume"/>
- <!--<AppenderRef ref="flume"/>-->
- <!--<AppenderRef ref="console"/>-->
- <!--<AppenderRef ref="fluent-bit"/>-->
+ <AppenderRef ref="Kafka"/>
</Root>
</Loggers>
</Configuration>
\ No newline at end of file
diff --git a/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-server/src/main/resources/log4j2.xml b/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-server/src/main/resources/log4j2.xml
index ff66652..3a01958 100644
--- a/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-server/src/main/resources/log4j2.xml
+++ b/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-server/src/main/resources/log4j2.xml
@@ -22,7 +22,7 @@
</Console>
</Appenders>
<Loggers>
- <Root level="debug">
+ <Root level="ERROR">
<AppenderRef ref="STDOUT" />
</Root>
</Loggers>
diff --git a/log4j-spring-cloud-config/pom.xml b/log4j-spring-cloud-config/pom.xml
index a89c6ed..56e0d40 100644
--- a/log4j-spring-cloud-config/pom.xml
+++ b/log4j-spring-cloud-config/pom.xml
@@ -34,6 +34,7 @@
<spring-cloud-version>Finchley.SR3</spring-cloud-version>
<spring-boot.version>2.0.8.RELEASE</spring-boot.version>
<springVersion>5.0.12.RELEASE</springVersion>
+ <log4jParentDir>${basedir}/..</log4jParentDir>
</properties>
<dependencyManagement>
<dependencies>
diff --git a/log4j-spring-cloud-config/src/site/markdown/index.md b/log4j-spring-cloud-config/src/site/markdown/index.md
new file mode 100644
index 0000000..7f51be4
--- /dev/null
+++ b/log4j-spring-cloud-config/src/site/markdown/index.md
@@ -0,0 +1,21 @@
+<!-- vim: set syn=markdown : -->
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+# Log4j Spring Cloud Config
+
+
diff --git a/log4j-spring-cloud-config/src/site/site.xml b/log4j-spring-cloud-config/src/site/site.xml
new file mode 100644
index 0000000..14bbb87
--- /dev/null
+++ b/log4j-spring-cloud-config/src/site/site.xml
@@ -0,0 +1,52 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<project name="Log4j Spring Cloud Config"
+ xmlns="http://maven.apache.org/DECORATION/1.4.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/DECORATION/1.4.0 http://maven.apache.org/xsd/decoration-1.4.0.xsd">
+ <body>
+ <links>
+ <item name="Apache" href="http://www.apache.org/" />
+ <item name="Logging Services" href="http://logging.apache.org/"/>
+ <item name="Log4j" href="../index.html"/>
+ </links>
+
+ <!-- Component-specific reports -->
+ <menu ref="reports"/>
+
+ <!-- Overall Project Info -->
+ <menu name="Log4j Project Information" img="icon-info-sign">
+ <item name="Dependencies" href="../dependencies.html" />
+ <item name="Dependency Convergence" href="../dependency-convergence.html" />
+ <item name="Dependency Management" href="../dependency-management.html" />
+ <item name="Project Team" href="../team-list.html" />
+ <item name="Mailing Lists" href="../mail-lists.html" />
+ <item name="Issue Tracking" href="../issue-tracking.html" />
+ <item name="Project License" href="../license.html" />
+ <item name="Source Repository" href="../source-repository.html" />
+ <item name="Project Summary" href="../project-summary.html" />
+ </menu>
+
+ <menu name="Log4j Project Reports" img="icon-cog">
+ <item name="Changes Report" href="../changes-report.html" />
+ <item name="JIRA Report" href="../jira-report.html" />
+ <item name="Surefire Report" href="../surefire-report.html" />
+ <item name="RAT Report" href="../rat-report.html" />
+ </menu>
+ </body>
+</project>
diff --git a/src/site/markdown/index.md.vm b/src/site/markdown/index.md.vm
index 36a42ce..7c94a85 100644
--- a/src/site/markdown/index.md.vm
+++ b/src/site/markdown/index.md.vm
@@ -107,6 +107,12 @@ $h3 Integrating with Application Servers
Version 2.10.0 introduces a the module log4j-appserver to improve integration with Apache Tomcat and Eclipse Jetty.
+$h3 Cloud Enabled
+
+Version 2.12.0 introduces support for accessing Dcoker container information via a Lookup and for accessing
+and updating the Log4j configuration through Spring Clooud Configuration. See [Logging in the Cloud](manual/cloud.html)
+for details.
+
$h2 Documentation
The Log4j 2 User's Guide is available on this [site](manual/index.html) or as a downloadable
diff --git a/src/site/markdown/manual/cloud.md b/src/site/markdown/manual/cloud.md
new file mode 100644
index 0000000..71a1125
--- /dev/null
+++ b/src/site/markdown/manual/cloud.md
@@ -0,0 +1,212 @@
+<!-- vim: set syn=markdown : -->
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+# Using Log4j in Cloud Enabled Applications
+
+## The Twelve-Factor Application
+
+The Logging Guidelines for [The Twelve-Factor App](https://12factor.net/logs) state the all logs should be routed
+unbuffered to stdout. Since this is the least common denominator it is guaranteed to work for all applications. Howeever,
+as with any set of general guidelines, choosing the least common denominator approach comes at a cost. Some of the costs
+in Java applications include:
+
+1. Java stack traces are multi-line log messages. The standard docker log driver cannot handle these properly. See
+[Docker Issue #22920](https://github.com/moby/moby/issues/22920) which was closed with the message "Don't Care".
+1. When logging to stdout in Docker, log events pass through Java's standard output handling which is then directed
+to the operating system so that the output can be piped into a file. The overhead of all this is measurably slower
+than just writing directly to a file as can be seen by the performance results below where logging
+to stdout is anywhere from 20 to 200% slower than logging directly to the file. However, these results alone
+would not be enough to argue against writing to the standard output stream as they only amount to about 20-30
+microseconds per logging call.
+1. When performing audit logging using a framework such as log4j-audit guaranteed delivery of the audit events
+is required. Many of the options for writing the output, including writing to the standard output stream, do
+not guarantee delivery. In these cases the event must be delivered to a "forwarder" that acknowledges receipt
+only when it has placed the event in durable storage, such as what Apache Flume or Apache Kafka will do.
+
+## Logging Approaches
+
+All the solutions discussed on this page are predicated with the idea that log files cannot permanently
+reside on the file system and that all log events should be routed to one or more log analysis tools that will
+be used for reporting and alreting. There are many ways to forward and collect events to be sent to the
+log analysis tools.
+
+Note that any approach that bypasses Docker's logging drivers requires Log4j's
+[Docker Loookup](lookups.html#DockerLookup) to allow Docker attributes to be injected into the log events.
+
+### Logging to the Standard Output Stream
+
+As discussed above, this is the recommended 12-Factor approach for applications running in a docker container.
+The Log4j team does not recommend this approach if exceptions will be logged by the Java application.
+
+![Stdout](../images/DockerStdout.png "Application Logging to the Standard Output Stream")
+
+### Logging to the Standard Output Stream with the Docker Fluentd Logging Driver
+
+Docker provides alternate [logging drivers](https://docs.docker.com/config/containers/logging/configure/),
+such as [gelf](https://docs.docker.com/config/containers/logging/gelf/) or
+[fluentd](https://docs.docker.com/config/containers/logging/fluentd/), that
+can be used to redirect the standard output stream to a log forwarder or log aggregator.
+
+When routing to a log forwarder it is expected that the forwarder will have the same lifetime as the
+application. If the forwarder should fail the management tools would be expected to also terminate
+other containers dependent on the forwarder.
+
+![Docker Fluentbit](../images/DockerFluentd.png "Logging via StdOut using the Docker Fluentd Logging Driver to Fluent-bit")
+
+As an alternative the logging drivers could be configured to route events directly to a logging aggregator.
+This is generally not a good idea as the logging drivers only allow a single host and port to be configured.
+The docker documentation isn't clear but infers that log events will be dropped when log events cannot be
+delivered so this method should not be used if a highly available solution is required.
+
+![Docker Fluentd](../images/DockerFluentdAggregator.png "Logging via StdOut using the Docker Fluentd Logging Driver to Fluentd")
+
+### Logging to a File
+
+While this is not the recommended 12-Factor approach, it performs very well. However, it requires that the
+application declare a volume where the log files will reside and then configure the log forwarder to tail
+those files. Care must also be taken to automatically manage the disk space used for the logs, which Log4j
+can perform via the Delete action on the [RollingFileAppender](appenders.html#RollingFileAppender).
+
+![File](../images/DockerLogFile.png "Logging to a File")
+
+### Sending Directly to a Log Forwarder via TCP
+
+Sending logs directly to a Log Forwarder is simple as it generally just requires that the forwarder's
+host and port be configured on a SocketAppender with an appropriate layout.
+
+![TCP](../images/DockerTCP.png "Application Logging to a Forwarder via TCP")
+
+### Sending Directly to a Log Aggregator via TCP
+
+Similar to sending logs to a forwarder, logs can also be sent to a cluster of aggregators. However,
+setting this up is not as simple. Since, to be highly available, a cluster of aggregators must be used.
+However, the SocketAppender currently can only be configured with a single host and port. To allow
+for failover if the primary aggregator fails the SocketAppender must be enclosed in a
+[FailoverAppender](appenders.html#FailoverAppender),
+which would also have the secondary aggregator configured.
+
+![Aggregator](../images/LoggerAggregator.png "Application Logging to an Aggregator via TCP")
+
+## Managing Logging Configuration
+
+Spring Boot provides another least common denominator approach to logging configuration. It will let you set the
+log level for various Loggers within an application which can be dynamically updated via REST endpoints provided
+by Spring. While this works in a lot of cases it does not support any of the more advanced filtering featurs of
+Log4j. For example, since it cannot add or modify any Filters other than the log level of a logger, changes cannot be made to allow
+all log events for a specific user or customer to temporarily be logged
+(see [DynamicThresholdFilter](filters.html#DynamicThresholdFilter) or
+[ThreadContextMapFilter](filters.html#ThreadContextMapFilter)) or any other kinds of changes to filters.
+Also, in a micro-services, clustered environment it is quite likely that these changes will need to be propagated
+to multiple servers at the same time. Trying to achieve this via REST calls could be difficult.
+
+Log4j supports dynamic reconfiguration. since the first release Log4j has supported reconfiguration through a file.
+Beginning with Log4j 2.12.0 Log4j also supports accessing the configuration via HTTP(S) and monitoring the file
+for changes by using the HTTP "If-Modified-Since" header. A patch has also been integrated into Spring Cloud Config
+starting with versions 2.0.3 and 2.1.1 for it to honor the If-Modified-Since header. In addition, the
+log4j-spring-cloud-config project will listen for update events published by Spring Cloud Bus and then verify
+that the configuratoin file has been modified, so polling via HTTP is not required.
+
+Log4j also supports composite configurations. A distributed application spread across microservices could
+share a common configuration file that could be used to control things like enabling debug logging for a
+specific user.
+
+While the standard Spring Boot REST endpoints to update logging will still work any changes made by those
+REST endpoints will be lost if Log4j reconfigures itself do to changes in the logging configuration file.
+
+Further information regarding integration of the log4j-spring-cloud-config-client can be found at
+[Log4j Spring Cloud Config Client](../log4j-spring-cloud-config/log4j-spring-cloud-config-client/index.html)
+
+## Integration with Docker
+
+Applications within a Docker container that log using a Docker logging driver can include special
+attributes in the formatted log event as described at
+[Customize Log Driver Output](https://docs.docker.com/config/containers/logging/log_tags/). Log4j
+provides similar functionality via the [Docker Loookup](lookups.html#DockerLookup). More information on
+Log4j's Docker support may also be found at [Log4j-Docker](../log4j-docker/index.html).
+
+## Appender Performance
+The numbers in the table below represent how much time was required for the application to call logger.debug
+100,000 times. These numbers only include the time taken to deliver to the specifcly noted endpoint and
+many not include the actual time required before they are availble for viewing. All measurements were
+performed on a MacBook Pro with a 2.9GHz Intel Core I9 processor with 6 physical and 12 logical cores,
+32GB of 2400 MHz DDR4 RAM, and 1TB of Apple SSD storage. The VM used by Docker was managed by VMWare Fusion
+and had 4 CPUs and 2 GB of RAM. These number should be used for relative perfomance comparisons as the
+results on another system may vary considerably.
+
+| Test | 1 Thread | 2 Threads | 4 Threads | 8 Threads |
+|------------------------ |---------:|----------:|----------:|----------:|
+|Flume Avro |||||
+|- Batch Size 1 - JSON |49.11 |46.54 |46.70 |44.92 |
+|- Batch Size 1 - RFC5424 |48.30 |45.79 |46.31 |45.50 |
+|- Batch Size 100 - JSON | 6.33 |3.87 |3.57 |3.84 |
+|- Batch Size 100 - RFC5424 | 6.08 |3.69 |3.22 |3.11 |
+|- Batch Size 1000 - JSON | 4.83 |3.20 |3.02 |2.11 |
+|- Batch Size 1000 - RFC5424 | 4.70 |2.40 |2.37 |2.37 |
+|Flume Embedded |||||
+| - RFC5424 |3.58 |2.10 |2.10 |2.70 |
+| - JSON |4.20 |2.49 |3.53 |2.90 |
+|Kafka Local JSON |||||
+| - sendSync true |58.46 |38.55 |19.59 |19.01 |
+| - sendSync false |9.8 |10.8 |12.23 |11.36 |
+|Console|||||
+| - JSON / Kubernetes |3.03 |3.11 |3.04 |2.51 |
+| - JSON |2.80 |2.74 |2.54 |2.35 |
+| - Docker fluentd driver |10.65 |9.92 |10.42 |10.27 |
+|Rolling File|||||
+| - RFC5424 |1.65 |0.94 |1.22 |1.55
+| - JSON |1.90 |0.95 |1.57 |1.94 |
+|TCP - Fluent Bit - JSON |2.34 |2.167 |1.67 |2.50 |
+|Async Logger|||||
+|- TCP - Fluent Bit - JSON|0.90 |0.58 |0.36 |0.48 |
+|- Console - JSON |0.83 |0.57 |0.55 |0.61 |
+|- Flume Avro - 1000 - JSON|0.76 |0.37 |0.45 |0.68 |
+
+Notes:
+
+1. Flume Avro - Buffering is controlled by the batch size. Each send is complete when the remote
+acknowledges the batch was written to its channel. These number seem to indicate Flume Avro could
+benefit from using a pool of RPCClients, at least for a batchSize of 1.
+1. Flume Embedded - This is essentially asynchronous as it writes to an in-memory buffer. It is
+unclear why the performance isn't closer to the AsyncLogger results.
+1. Kafka was run in standalone mode on the same laptop as the application. See sendSync set to true
+requires waiting for an ack from Kafka for each log event.
+1. Console - System.out is redirected to a file by Docker. Testing shows that it would be much
+slower if it was writing to the terminal screen.
+1. Rolling File - Test uses the default buffer size of 8K.
+1. TCP to Fluent Bit - The Socket Appender uses a default buffer size of 8K.
+1. Async Loggers - These all write to a circular buffer and return to the application. The actual
+I/O will take place on a separate thread. If writing the events is performed more slowly than
+events are being created eventually the buffer will fill up and logging will be performed at
+the same pace that log events are written.
+
+## Logging Recommendations
+
+1. Use asynchronous logging unless guaranteed delivery is absolutely required. As
+the performance numbers show, so long as the volume of logging is not high enough to fill up the
+circular buffer the overhead of logging will almost be unnoticeable to the application.
+1. If overall performance is a consideration or you require multiline events such as stack traces
+be processed properly then log via TCP to a companion container that acts as a log forwarder. Use the
+Log4j Docker Lookup to add the container information to each log event.
+1. Whenvever guaranteed delivery is required use Flume Avro with a batch size of 1 or another Appender such
+as the Kafka Appender with syncSend set to true that only return control after the downstream agent
+acknowledges receipt of the event. Beware that using an Appender that writes each event individually should
+be kept to a minimum since it is much slower than sending buffered events.
+1. Logging to files within the container is discouraged. Doing so reuires that a volume be declared in
+the Docker configuration and that the file be tailed by a log forwarder. However, it performs
+better than logging to the standard output stream. If logging via TCP is not an option and
+proper multiline handling is required then consider this option.
\ No newline at end of file
diff --git a/src/site/resources/images/DockerFluentd.drawio b/src/site/resources/images/DockerFluentd.drawio
new file mode 100644
index 0000000..8664a4e
--- /dev/null
+++ b/src/site/resources/images/DockerFluentd.drawio
@@ -0,0 +1 @@
+<mxfile modified="2019-03-31T19:39:23.743Z" host="www.draw.io" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36" etag="ga3EPmrR2nb5J0BswPKH" version="10.5.9" type="device"><diagram id="5SG5qToZW8ppSJovxSJi" name="Page-1">3VjZctsgFP0aP6ajzbL1mHhppk2mSZ1pk6cOEdiiwUJFeMvX92KBdsdprHQ69YMNB7iIc+4R4J47Wm4/CpRE1xwT1nMsvO25457jOJY7hB+F7DLEtmwrQxaCYo0VwIw+E9NRoyuKSVrpKDlnkiZVMORxTEJZwZAQfFPtNuesOmuCFqQBzELEmuh3imWkUduy [...]
\ No newline at end of file
diff --git a/src/site/resources/images/DockerFluentd.png b/src/site/resources/images/DockerFluentd.png
new file mode 100644
index 0000000..edcd350
Binary files /dev/null and b/src/site/resources/images/DockerFluentd.png differ
diff --git a/src/site/resources/images/DockerFluentdAggregator.drawio b/src/site/resources/images/DockerFluentdAggregator.drawio
new file mode 100644
index 0000000..4b312ff
--- /dev/null
+++ b/src/site/resources/images/DockerFluentdAggregator.drawio
@@ -0,0 +1 @@
+<mxfile modified="2019-03-31T20:13:53.215Z" host="www.draw.io" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36" etag="M96szTU_Fw0FbEZ1bxDf" version="10.5.9" type="device"><diagram id="5SG5qToZW8ppSJovxSJi" name="Page-1">zVfLctowFP0alun4hcFLYqCZNpkmpdMmq45iC1uNsFxZgMnX9wpLfhMyAWbKIpGOpCv7nHt05YHtr/LPHKXxHQsxHVhGmA/s6cCyLMMewz+J7ArENEyjQCJOQoVVwIK8Yj1RoWsS4qwxUTBGBUmbYMCSBAeigSHO2bY5bcloc9cURbgDLAJEu+gvEopYoaZh [...]
\ No newline at end of file
diff --git a/src/site/resources/images/DockerFluentdAggregator.png b/src/site/resources/images/DockerFluentdAggregator.png
new file mode 100644
index 0000000..846c5a6
Binary files /dev/null and b/src/site/resources/images/DockerFluentdAggregator.png differ
diff --git a/src/site/resources/images/DockerLogFile.drawio b/src/site/resources/images/DockerLogFile.drawio
new file mode 100644
index 0000000..1de4dd3
--- /dev/null
+++ b/src/site/resources/images/DockerLogFile.drawio
@@ -0,0 +1 @@
+<mxfile modified="2019-03-31T20:17:04.511Z" host="www.draw.io" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36" etag="CK1puqeNANjE1A8R_XEg" version="10.5.9" type="device"><diagram id="5SG5qToZW8ppSJovxSJi" name="Page-1">3VhNc5swEP01PrYjwNjm6BDTr3ia1jNJfFSQbNQKRIUIkF9fyQhjkB3HiZOZNodEu1qt0Hu7D5GB48flJw7TaM4QpgMboHLgXA5s2wbORP5Rnqr2WMACtWfNCdK+1rEgj7gJ1N6cIJx1AgVjVJC06wxZkuBQdHyQc1Z0w1aMdndN4RobjkUIqem9JUhE2msB [...]
\ No newline at end of file
diff --git a/src/site/resources/images/DockerLogFile.png b/src/site/resources/images/DockerLogFile.png
new file mode 100644
index 0000000..5ec366e
Binary files /dev/null and b/src/site/resources/images/DockerLogFile.png differ
diff --git a/src/site/resources/images/DockerStdout.drawio b/src/site/resources/images/DockerStdout.drawio
new file mode 100644
index 0000000..9b35764
--- /dev/null
+++ b/src/site/resources/images/DockerStdout.drawio
@@ -0,0 +1 @@
+<mxfile modified="2019-03-31T20:22:29.524Z" host="www.draw.io" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36" etag="tZjZN1zqtIJ6xoe4SOn7" version="10.5.9" type="device"><diagram id="5SG5qToZW8ppSJovxSJi" name="Page-1">3VjZctowFP0aHtPxAgY/Jga6DGnTMtOER2ELW42wXFlm6ddXwvIqOxAwSac8gHR1tfice48u7pnOeveRgii4Jx7EPUPzdj1z3DMMQzNH/EdY9qlF13QttfgUedJWGOboD8wcpTVBHowrjowQzFBUNbokDKHLKjZAKdlW3VYEV3eNgA8Vw9wFWLU+Io8F0qpr [...]
\ No newline at end of file
diff --git a/src/site/resources/images/DockerStdout.png b/src/site/resources/images/DockerStdout.png
new file mode 100644
index 0000000..546e896
Binary files /dev/null and b/src/site/resources/images/DockerStdout.png differ
diff --git a/src/site/resources/images/DockerTCP.drawio b/src/site/resources/images/DockerTCP.drawio
new file mode 100644
index 0000000..733bbf6
--- /dev/null
+++ b/src/site/resources/images/DockerTCP.drawio
@@ -0,0 +1 @@
+<mxfile modified="2019-03-31T20:25:12.743Z" host="www.draw.io" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36" etag="W_kcBCjDz_BLlawZ2mwf" version="10.5.9" type="device"><diagram id="5SG5qToZW8ppSJovxSJi" name="Page-1">3VdNc5swEP01PqYDKOBwTLBpppPMpHWnTU4dBQlQIxARcsD99V0ZYT7zMXXaQ32wtW9XK/TeapEXKMjqjxIX6bUglC8ci9QLtFo4jmOhM/jRyK5BbMu2GiSRjBisAzbsF20DDbplhJaDQCUEV6wYgpHIcxqpAYalFNUwLBZ8uGqBEzoBNhHmU/Q7Iyo1qG1Z [...]
\ No newline at end of file
diff --git a/src/site/resources/images/DockerTCP.png b/src/site/resources/images/DockerTCP.png
new file mode 100644
index 0000000..4a30200
Binary files /dev/null and b/src/site/resources/images/DockerTCP.png differ
diff --git a/src/site/resources/images/LoggerAggregator.drawio b/src/site/resources/images/LoggerAggregator.drawio
new file mode 100644
index 0000000..91c5668
--- /dev/null
+++ b/src/site/resources/images/LoggerAggregator.drawio
@@ -0,0 +1 @@
+<mxfile modified="2019-03-31T20:10:25.947Z" host="www.draw.io" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36" etag="_GAVgQ1D8SCqD5mIJyzn" version="10.5.9" type="device"><diagram id="5SG5qToZW8ppSJovxSJi" name="Page-1">tVbbcpswEP0aHtvhYtPw6Gs7nWTGrWfa5FFBC6gRiAph4359V0bcnUsnLg+29uxqJZ2zWrC8VVp9liRP7gQFbrk2rSxvbbmua3s3+KeRU404tmPXSCwZNVgH7NkfaAINWjIKxSBQCcEVy4dgKLIMQjXAiJTiOAyLBB+umpMYJsA+JHyK/mRUJQZ1bLtzfAEW [...]
\ No newline at end of file
diff --git a/src/site/resources/images/LoggerAggregator.png b/src/site/resources/images/LoggerAggregator.png
new file mode 100644
index 0000000..78ebf0a
Binary files /dev/null and b/src/site/resources/images/LoggerAggregator.png differ
diff --git a/src/site/site.xml b/src/site/site.xml
index 1a49332..7fe1c72 100644
--- a/src/site/site.xml
+++ b/src/site/site.xml
@@ -111,6 +111,7 @@
<item name="Usage" href="/manual/usage.html" collapse="true">
<item name="Static vs non-Static Loggers" href="/manual/usage.html#StaticVsNonStatic"/>
<item name="Logger Name vs Class Name" href="/manual/usage.html#LoggerVsClass"/>
+ <item name="Logging in the Cloud" href="/manual/cloud.html"/>
</item>
<item name="Web Applications and JSPs" href="/manual/webapp.html" collapse="true">
@@ -125,6 +126,7 @@
<item name="Lookups" href="/manual/lookups.html" collapse="true">
<item name="Context Map" href="/manual/lookups.html#ContextMapLookup"/>
<item name="Date" href="/manual/lookups.html#DateLookup"/>
+ <item name="Docker" href="manual/lookups.html#DockerLookup"/>
<item name="Environment" href="/manual/lookups.html#EnvironmentLookup"/>
<item name="Java" href="/manual/lookups.html#JavaLookup"/>
<item name="JNDI" href="/manual/lookups.html#JndiLookup"/>
@@ -295,6 +297,8 @@
<item name="Log4j Cassandra appender" href="log4j-cassandra/index.html"/>
<item name="Log4j IO Streams" href="log4j-iostreams/index.html"/>
<item name="Log4j Liquibase Binding" href="log4j-liquibase/index.html"/>
+ <item name="Log4j Docker Support" href="log4j-docker/index.html"/>
+ <item name="Log4j Spring Cloud Config Client" href="log4j-spring-cloud-config/log4j-spring-cloud-config-client/index.html"/>
</menu>
<menu name="Project Information" img="icon-info-sign">
diff --git a/src/site/xdoc/manual/configuration.xml.vm b/src/site/xdoc/manual/configuration.xml.vm
index 119c60e..e0291d7 100644
--- a/src/site/xdoc/manual/configuration.xml.vm
+++ b/src/site/xdoc/manual/configuration.xml.vm
@@ -2386,6 +2386,84 @@ public class AwesomeTest {
<td>Disables message pattern lookups globally when set to <tt>true</tt>.
This is equivalent to defining all message patterns using <tt>%m{nolookups}</tt>.</td>
</tr>
+ <tr>
+ <td><a name="log4j2.trustStore.location "/>log4j2.trustStore.location</td>
+ <td>LOG4J_TRUST_STORE_LOCATION</td>
+ <td></td>
+ <td>The location of the trust store. If not provided the default trust store will be used.</td>
+ </tr>
+ <tr>
+ <td><a name="log4j2.trustStore.password"/>log4j2.trustStore.password</td>
+ <td>LOG4J_TRUST_STORE_PASSWORD</td>
+ <td></td>
+ <td>Password needed to access the trust store.</td>
+ </tr>
+ <tr>
+ <td><a name="log4j2.trustStore.passwordFile"/>log4j2.trustStore.passwordFile</td>
+ <td>LOG4J_TRUST_STORE_PASSWORD_FILE</td>
+ <td></td>
+ <td>The location of a file that contains the password for the trust store.</td>
+ </tr>
+ <tr>
+ <td><a name="log4j2.trustStore.passwordEnvironmentVariable"/>log4j2.trustStore.passwordEnvironmentVariable</td>
+ <td>LOG4J_TRUST_STORE_PASSWORD_ENVIRONMENT_VARIABLE</td>
+ <td></td>
+ <td>The name of the environment variable that contains the trust store password.</td>
+ </tr>
+ <tr>
+ <td><a name="log4j2.trustStore.type"/>log4j2.trustStore.type</td>
+ <td>LOG4J_TRUST_STORE_TYPE</td>
+ <td></td>
+ <td>The type of key store used for the trust store.</td>
+ </tr>
+ <tr>
+ <td><a name="log4j2.trustStore.keyManagerFactoryAlgorithm"/>log4j2.trustStore.keyManagerFactoryAlgorithm</td>
+ <td>LOG4J_TRUST_STORE_KEY_MANAGER_FACTORY_ALGORITHM</td>
+ <td></td>
+ <td>Java cryptographic algorithm.</td>
+ </tr>
+ <tr>
+ <td><a name="log4j2.keyStore.location "/>log4j2.keyStore.location </td>
+ <td>LOG4J_KEY_STORE_LOCATION</td>
+ <td></td>
+ <td>The location of the key store. If not provided the default key store will be used.</td>
+ </tr>
+ <tr>
+ <td><a name="log4j2.keyStore.password"/>log4j2.keyStore.password</td>
+ <td>LOG4J_KEY_STORE_PASSWORD</td>
+ <td></td>
+ <td>Password needed to access the key store.</td>
+ </tr>
+ <tr>
+ <td><a name="log4j2.keyStore.passwordFile"/>log4j2.keyStore.passwordFile</td>
+ <td>LOG4J_KEY_STORE_PASSWORD_FILE</td>
+ <td></td>
+ <td>The location of a file that contains the password for the key store.</td>
+ </tr>
+ <tr>
+ <td><a name="log4j2.keyStore.passwordEnvironmentVariable"/>log4j2.keyStore.passwordEnvironmentVariable</td>
+ <td>LOG4J_KEY_STORE_PASSWORD_ENVIRONMENT_VARIABLE</td>
+ <td></td>
+ <td>The name of the environment variable that contains the key store password.</td>
+ </tr>
+ <tr>
+ <td><a name="log4j2.keyStore.type"/>log4j2.keyStore.type</td>
+ <td>LOG4J_KEY_STORE_TYPE</td>
+ <td></td>
+ <td>The type of key store.</td>
+ </tr>
+ <tr>
+ <td><a name="log4j2.keyStore.keyManagerFactoryAlgorithm"/>log4j2.keyStore.keyManagerFactoryAlgorithm</td>
+ <td>LOG4J_KEY_STORE_KEY_MANAGER_FACTORY_ALGORITHM</td>
+ <td></td>
+ <td>Java cryptographic algorithm.</td>
+ </tr>
+ <tr>
+ <td><a name="log4j2.ssl.verifyHostName"/>log4j2.ssl.verifyHostName</td>
+ <td></td>
+ <td>false</td>
+ <td>true or false if the host name should be verified</td>
+ </tr>
</table>
</subsection>
diff --git a/src/site/xdoc/manual/lookups.xml b/src/site/xdoc/manual/lookups.xml
index 255054a..051033f 100644
--- a/src/site/xdoc/manual/lookups.xml
+++ b/src/site/xdoc/manual/lookups.xml
@@ -67,6 +67,31 @@
<SizeBasedTriggeringPolicy size="500" />
</RollingFile>]]></pre>
</subsection>
+ <a name="DockerLookup"/>
+ <subsection name="Docker Lookup">
+ <p>
+ The DockerLookup can be used to lookup attributes from the Docker container the application is running
+ in.
+ </p>
+ Log4j Docker provides access to the following container attributes:
+ <table>
+ <tr><td>containerId</td><td>The full id assigned to the container.</td></tr>
+ <tr><td>containreName</td><td>The name assigned to the container.</td></tr>
+ <tr><td>imageId</td><td>The id assigned to the image.</td></tr>
+ <tr><td>imageName</td><td>The name assigned to the image.</td></tr>
+ <tr><td>shortContainerId</td><td>The first 12 characters of the container id.</td></tr>
+ <tr><td>shortImageId</td><td>The first 12 characters of the image id.</td></tr>
+ </table>
+ <pre class="prettyprint linenums"><![CDATA[
+<JsonLayout properties="true" compact="true" eventEol="true">
+ <KeyValuePair key="containerId" value="${docker:containerId}"/>
+ <KeyValuePair key="containerName" value="${docker:containerName}"/>
+ <KeyValuePair key="imageName" value="${docker:imageName}"/>
+</JsonLayout>]]></pre>
+ <p>
+ This Lookup is subject to the requirements listed at <a href="../log4j-docker/index.html">Log4j Docker Support</a>
+ </p>
+ </subsection>
<a name="EnvironmentLookup"/>
<subsection name="Environment Lookup">
<p>
diff --git a/src/site/xdoc/runtime-dependencies.xml b/src/site/xdoc/runtime-dependencies.xml
index 9429feb..4a5c118 100644
--- a/src/site/xdoc/runtime-dependencies.xml
+++ b/src/site/xdoc/runtime-dependencies.xml
@@ -81,20 +81,36 @@
</td>
</tr>
<tr>
+ <td>log4j-appserver</td>
+ <td>org.apache.logging.log4j.appserver</td>
+ <td>Automatic Module</td>
+ </tr>
+ <tr>
+ <td>log4j-cassandra</td>
+ <td>org.apache.logging.log4j.cassandra</td>
+ <td>Automatic Module</td>
+ </tr>
+ <tr>
<td>log4j-core</td>
<td>org.apache.logging.log4j.core</td>
<td>Automatic Module</td>
</tr>
<tr>
- <td>log4j-1.2-api</td>
- <td>org.apache.log4j</td>
+ <td>log4j-couchdb</td>
+ <td>org.apache.logging.log4j.couchdb</td>
<td>Automatic Module</td>
</tr>
<tr>
- <td>log4j-appserver</td>
- <td>org.apache.logging.log4j.appserver</td>
+ <td>log4j-docker</td>
+ <td>org.apache.logging.log4j.docker</td>
+ <td>Automatic Module</td>
+ </tr>
+ <tr>
+ <td>log4j-1.2-api</td>
+ <td>org.apache.log4j</td>
<td>Automatic Module</td>
</tr>
+
<tr>
<td>log4j-flume-ng</td>
<td>org.apache.logging.log4j.flume</td>
@@ -121,39 +137,30 @@
<td>Automatic Module</td>
</tr>
<tr>
- <td>log4j-couchdb</td>
- <td>org.apache.logging.log4j.couchdb</td>
- <td>Automatic Module</td>
- </tr>
- <tr>
<td>log4j-mongodb</td>
<td>org.apache.logging.log4j.mongodb</td>
<td>Automatic Module</td>
</tr>
- <tr>
- <td>log4j-cassandra</td>
- <td>org.apache.logging.log4j.cassandra</td>
- <td>Automatic Module</td>
- </tr>
+
<tr>
<td>log4j-osgi</td>
<td>org.apache.logging.log4j.osgi</td>
<td>Automatic Module. Unclear how OSGi will support Java modules.</td>
</tr>
<tr>
- <td>log4j-slf4j-impl</td>
- <td>org.apache.logging.log4j.slf4j.impl</td>
- <td>Automatic Module. May require renaming should SLF4J ever require all implementations to have the same
- module name.</td>
- </tr>
+ <td>log4j-slf4j-impl</td>
+ <td>org.apache.logging.log4j.slf4j.impl</td>
+ <td>Automatic Module. May require renaming should SLF4J ever require all implementations to have the same
+ module name.</td>
+ </tr>
<tr>
- <td>log4j-to-slf4j</td>
- <td>org.apache.logging.log4j.slf4j</td>
+ <td>log4j-taglib</td>
+ <td>org.apache.logging.log4j.taglib</td>
<td>Automatic Module</td>
</tr>
<tr>
- <td>log4j-taglib</td>
- <td>org.apache.logging.log4j.taglib</td>
+ <td>log4j-to-slf4j</td>
+ <td>org.apache.logging.log4j.slf4j</td>
<td>Automatic Module</td>
</tr>
<tr>
@@ -286,7 +293,14 @@
</td>
</tr>
</table>
-
+ <a name="log4j-docker"/>
+ <h4>log4j-docker</h4>
+ <p>
+ <a href="log4j-docker/index.html">Log4j Docker Support</a> requires
+ <a href="https://github.com/FasterXML/jackson">Jackson annotations, core, and databind</a>. See the
+ <a href="log4j-docker/dependencies.html#Dependency_Tree">Dependency Tree</a> for the exact list of
+ JAR files needed.
+ </p>
<a name="log4j-jcl" />
<h4>log4j-jcl</h4>
<p>
@@ -348,7 +362,17 @@
The persistent agent uses Berkeley DB. See the
<a href="log4j-flume-ng/dependencies.html#Dependency_Tree">Dependency Tree</a> for the exact list of JAR files needed.
</p>
-
+ <a name="log4j-spring-cloud-config-client"/>
+ <h4>log4j-spring-cloud-config-client</h4>
+ <p>
+ <a href="log4j-spring-cloud-config/log4j-spring-cloud-config-client/index.html">Log4j Spring Cloud Config Client</a> requires
+ <a href="https://spring.io/projects/spring-cloud-config">Spring Cloud Config</a>.
+ <a href="https://spring.io/projects/spring-cloud-bus">Spring Cloud Bus</a> is required if notification of logging
+ configuration changes is desired. <a href="https://spring.io/projects/spring-boot">Spring Boot</a> is required
+ but applications do not have to be packaged as a Spring Boot application.
+ <a href="log4j-spring-cloud-config/log4j-spring-cloud-config-client/dependencies.html#Dependency_Tree">Dependency Tree</a> for the exact list of
+ JAR files needed.
+ </p>
<a name="log4j-taglib" />
<h4>log4j-taglib</h4>
<p>