LOG4J2-2716 - Add the ability to lookup Kubernetes attributes in the Log4j configuration. Allow Log4j properties to be retrieved from the Spring environment if it is available.
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/LogManager.java b/log4j-api/src/main/java/org/apache/logging/log4j/LogManager.java
index 2da1e84..45b83ff 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/LogManager.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/LogManager.java
@@ -404,7 +404,7 @@
      *            used and if the caller is a class in the container's classpath then a different LoggerContext may
      *            be used.
      * @param allContexts if true all LoggerContexts that can be located will be shutdown.
-     * @since 3.0
+     * @since 2.13.0
      */
     public static void shutdown(final boolean currentContext, final boolean allContexts) {
         factory.shutdown(FQCN, null, currentContext, allContexts);
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerContextFactory.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerContextFactory.java
index 14aaa37..3dcee76 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerContextFactory.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerContextFactory.java
@@ -31,8 +31,7 @@
      * @param currentContext If true shuts down the current Context, if false shuts down the Context appropriate
      * for the caller if a more appropriate Context can be determined.
      * @param allContexts if true all LoggerContexts that can be located will be shutdown.
-     * @return true if a LoggerContext has been installed, false otherwise.
-     * @since 3.0
+     * @since 2.13.0
      */
     default void shutdown(String fqcn, ClassLoader loader, boolean currentContext, boolean allContexts) {
         if (hasContext(fqcn, loader, currentContext)) {
@@ -50,7 +49,7 @@
      * @param currentContext If true returns the current Context, if false returns the Context appropriate
      * for the caller if a more appropriate Context can be determined.
      * @return true if a LoggerContext has been installed, false otherwise.
-     * @since 3.0
+     * @since 2.13.0
      */
     default boolean hasContext(String fqcn, ClassLoader loader, boolean currentContext) {
         return false;
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesUtil.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesUtil.java
index 4feb207..6dd01b7 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesUtil.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesUtil.java
@@ -19,6 +19,9 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.charset.Charset;
+import java.time.Duration;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.TemporalUnit;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -155,6 +158,24 @@
     }
 
     /**
+     * Retrieves a property that may be prefixed by more than one string.
+     * @param prefixes The array of prefixes.
+     * @param key The key to locate.
+     * @param supplier The method to call to derive the default value. If the value is null, null will be returned
+     * if no property is found.
+     * @return The value or null if it is not found.
+     * @since 2.13.0
+     */
+    public Boolean getBooleanProperty(final String[] prefixes, String key, Supplier<Boolean> supplier) {
+        for (String prefix : prefixes) {
+            if (hasProperty(prefix + key)) {
+                return getBooleanProperty(prefix + key);
+            }
+        }
+        return supplier != null ? supplier.get() : null;
+    }
+
+    /**
      * Gets the named property as a Charset value.
      *
      * @param name the name of the property to look up
@@ -232,6 +253,24 @@
     }
 
     /**
+     * Retrieves a property that may be prefixed by more than one string.
+     * @param prefixes The array of prefixes.
+     * @param key The key to locate.
+     * @param supplier The method to call to derive the default value. If the value is null, null will be returned
+     * if no property is found.
+     * @return The value or null if it is not found.
+     * @since 2.13.0
+     */
+    public Integer getIntegerProperty(final String[] prefixes, String key, Supplier<Integer> supplier) {
+        for (String prefix : prefixes) {
+            if (hasProperty(prefix + key)) {
+                return getIntegerProperty(prefix + key, 0);
+            }
+        }
+        return supplier != null ? supplier.get() : null;
+    }
+
+    /**
      * Gets the named property as a long.
      *
      * @param name         the name of the property to look up
@@ -249,6 +288,76 @@
         }
         return defaultValue;
     }
+    /**
+     * Retrieves a property that may be prefixed by more than one string.
+     * @param prefixes The array of prefixes.
+     * @param key The key to locate.
+     * @param supplier The method to call to derive the default value. If the value is null, null will be returned
+     * if no property is found.
+     * @return The value or null if it is not found.
+     * @since 2.13.0
+     */
+    public Long getLongProperty(final String[] prefixes, String key, Supplier<Long> supplier) {
+        for (String prefix : prefixes) {
+            if (hasProperty(prefix + key)) {
+                return getLongProperty(prefix + key, 0);
+            }
+        }
+        return supplier != null ? supplier.get() : null;
+    }
+
+    /**
+     * Retrieves a Duration where the String is of the format nnn[unit] where nnn represents an integer value
+     * and unit represents a time unit.
+     * @param name The property name.
+     * @param defaultValue The default value.
+     * @return The value of the String as a Duration or the default value, which may be null.
+     * @since 2.13.0
+     */
+    public Duration getDurationProperty(final String name, Duration defaultValue) {
+        final String prop = getStringProperty(name);
+        if (prop != null) {
+            return TimeUnit.getDuration(prop);
+        }
+        return defaultValue;
+    }
+
+    /**
+     * Retrieves a property that may be prefixed by more than one string.
+     * @param prefixes The array of prefixes.
+     * @param key The key to locate.
+     * @param supplier The method to call to derive the default value. If the value is null, null will be returned
+     * if no property is found.
+     * @return The value or null if it is not found.
+     * @since 2.13.0
+     */
+    public Duration getDurationProperty(final String[] prefixes, String key, Supplier<Duration> supplier) {
+        for (String prefix : prefixes) {
+            if (hasProperty(prefix + key)) {
+                return getDurationProperty(prefix + key, null);
+            }
+        }
+        return supplier != null ? supplier.get() : null;
+    }
+
+    /**
+     * Retrieves a property that may be prefixed by more than one string.
+     * @param prefixes The array of prefixes.
+     * @param key The key to locate.
+     * @param supplier The method to call to derive the default value. If the value is null, null will be returned
+     * if no property is found.
+     * @return The value or null if it is not found.
+     * @since 2.13.0
+     */
+    public String getStringProperty(final String[] prefixes, String key, Supplier<String> supplier) {
+        for (String prefix : prefixes) {
+            String result = getStringProperty(prefix + key);
+            if (result != null) {
+                return result;
+            }
+        }
+        return supplier != null ? supplier.get() : null;
+    }
 
     /**
      * Gets the named property as a String.
@@ -387,6 +496,11 @@
             if (hasSystemProperty(key)) {
                 return System.getProperty(key);
             }
+            for (final PropertySource source : sources) {
+                if (source.containsProperty(key)) {
+                    return source.getProperty(key);
+                }
+            }
             return tokenized.get(PropertySource.Util.tokenize(key));
         }
 
@@ -463,4 +577,40 @@
         return getStringProperty("os.name", "").startsWith("Windows");
     }
 
+    private enum TimeUnit {
+        NANOS("ns,nano,nanos,nanosecond,nanoseconds", ChronoUnit.NANOS),
+        MICROS("us,micro,micros,microsecond,microseconds", ChronoUnit.MICROS),
+        MILLIS("ms,milli,millis,millsecond,milliseconds", ChronoUnit.MILLIS),
+        SECONDS("s,second,seconds", ChronoUnit.SECONDS),
+        MINUTES("m,minute,minutes", ChronoUnit.MINUTES),
+        HOURS("h,hour,hours", ChronoUnit.HOURS),
+        DAYS("d,day,days", ChronoUnit.DAYS);
+
+        private final String[] descriptions;
+        private final ChronoUnit timeUnit;
+
+        TimeUnit(String descriptions, ChronoUnit timeUnit) {
+            this.descriptions = descriptions.split(",");
+            this.timeUnit = timeUnit;
+        }
+
+        ChronoUnit getTimeUnit() {
+            return this.timeUnit;
+        }
+
+        static Duration getDuration(String time) {
+            String value = time.trim();
+            TemporalUnit temporalUnit = ChronoUnit.MILLIS;
+            long timeVal = 0;
+            for (TimeUnit timeUnit : values()) {
+                for (String suffix : timeUnit.descriptions) {
+                    if (value.endsWith(suffix)) {
+                        temporalUnit = timeUnit.timeUnit;
+                        timeVal = Long.parseLong(value.substring(0, value.length() - suffix.length()));
+                    }
+                }
+            }
+            return Duration.of(timeVal, temporalUnit);
+        }
+    }
 }
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertySource.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertySource.java
index 75399d9..77ccd4b 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertySource.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertySource.java
@@ -45,7 +45,8 @@
      *
      * @param action action to perform on each key/value pair
      */
-    void forEach(BiConsumer<String, String> action);
+    default void forEach(BiConsumer<String, String> action) {
+    }
 
     /**
      * Converts a list of property name tokens into a normal form. For example, a list of tokens such as
@@ -54,7 +55,30 @@
      * @param tokens list of property name tokens
      * @return a normalized property name using the given tokens
      */
-    CharSequence getNormalForm(Iterable<? extends CharSequence> tokens);
+    default CharSequence getNormalForm(Iterable<? extends CharSequence> tokens) {
+        return null;
+    }
+
+    /**
+     * For PropertySources that cannot iterate over all the potential properties this provides a direct lookup.
+     * @param key The key to search for.
+     * @return The value or null;
+     * @since 2.13.0
+     */
+    default String getProperty(String key) {
+        return null;
+    }
+
+
+    /**
+     * For PropertySources that cannot iterate over all the potential properties this provides a direct lookup.
+     * @param key The key to search for.
+     * @return The value or null;
+     * @since 2.13.0
+     */
+    default boolean containsProperty(String key) {
+        return false;
+    }
 
     /**
      * Comparator for ordering PropertySource instances by priority.
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jContextFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jContextFactory.java
index e00eb29..1ca8280 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jContextFactory.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jContextFactory.java
@@ -299,7 +299,7 @@
      * @param currentContext If true returns the current Context, if false returns the Context appropriate
      * for the caller if a more appropriate Context can be determined.
      * @return true if a LoggerContext has been installed, false otherwise.
-     * @since 3.0
+     * @since 2.13.0
      */
     @Override
     public boolean hasContext(String fqcn, ClassLoader loader, boolean currentContext) {
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/Interpolator.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/Interpolator.java
index 10a63b0..7003e3f 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/Interpolator.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/Interpolator.java
@@ -40,6 +40,8 @@
 
     private static final String LOOKUP_KEY_DOCKER = "docker";
 
+    private static final String LOOKUP_KEY_KUBERNETES = "kubernetes";
+
     private static final String LOOKUP_KEY_SPRING = "spring";
 
     private static final String LOOKUP_KEY_JNDI = "jndi";
@@ -145,6 +147,14 @@
         } catch (final Exception ignored) {
             handleError(LOOKUP_KEY_SPRING, ignored);
         }
+        try {
+            strLookupMap.put(LOOKUP_KEY_KUBERNETES,
+                    Loader.newCheckedInstanceOf("org.apache.logging.log4j.kubernetes.KubernetesLookup", StrLookup.class));
+        } catch (final Exception ignored) {
+            handleError(LOOKUP_KEY_KUBERNETES, ignored);
+        } catch (final NoClassDefFoundError error) {
+            handleError(LOOKUP_KEY_KUBERNETES, error);
+        }
     }
 
     public Map<String, StrLookup> getStrLookupMap() {
@@ -172,6 +182,11 @@
                 break;
             case LOOKUP_KEY_DOCKER: case LOOKUP_KEY_SPRING:
                 break;
+            case LOOKUP_KEY_KUBERNETES:
+                if (t instanceof NoClassDefFoundError) {
+                    LOGGER.warn("Unable to create Kubernetes lookup due to missing dependency: {}", t.getMessage());
+                }
+                break;
             default:
                 LOGGER.error("Unable to create Lookup for {}", lookupKey, t);
         }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/osgi/BundleContextSelector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/osgi/BundleContextSelector.java
index 7cb433f..ba0a14e 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/osgi/BundleContextSelector.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/osgi/BundleContextSelector.java
@@ -77,8 +77,6 @@
                 }
             }
         }
-
-
     }
 
     private LoggerContext getLoggerContext(final Bundle bundle) {
@@ -97,7 +95,7 @@
     @Override
     public boolean hasContext(final String fqcn, final ClassLoader loader, final boolean currentContext) {
         if (currentContext && ContextAnchor.THREAD_CONTEXT.get() != null) {
-            return true;
+            return ContextAnchor.THREAD_CONTEXT.get().isStarted();
         }
         if (loader instanceof BundleReference) {
             return hasContext(((BundleReference) loader).getBundle());
@@ -106,7 +104,7 @@
         if (callerClass != null) {
             return hasContext(FrameworkUtil.getBundle(callerClass));
         }
-        return ContextAnchor.THREAD_CONTEXT.get() != null;
+        return ContextAnchor.THREAD_CONTEXT.get() != null && ContextAnchor.THREAD_CONTEXT.get().isStarted();
     }
 
     @Override
@@ -134,7 +132,7 @@
     private static boolean hasContext(final Bundle bundle) {
         final String name = Objects.requireNonNull(bundle, "No Bundle provided").getSymbolicName();
         final AtomicReference<WeakReference<LoggerContext>> ref = CONTEXT_MAP.get(name);
-        return ref != null && ref.get() != null;
+        return ref != null && ref.get() != null && ref.get().get() != null && ref.get().get().isStarted();
     }
 
     private static LoggerContext locateContext(final Bundle bundle, final URI configLocation) {
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/BasicContextSelector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/BasicContextSelector.java
index 1006a60..aac0221 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/BasicContextSelector.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/BasicContextSelector.java
@@ -39,7 +39,8 @@
 

     @Override

     public boolean hasContext(String fqcn, ClassLoader loader, boolean currentContext) {

-        return ContextAnchor.THREAD_CONTEXT.get() != null;

+        LoggerContext ctx = ContextAnchor.THREAD_CONTEXT.get();

+        return ctx != null && ctx.isStarted();

     }

 

     @Override

diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/ClassLoaderContextSelector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/ClassLoaderContextSelector.java
index b2c7282..9a8b6c9 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/ClassLoaderContextSelector.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/ClassLoaderContextSelector.java
@@ -83,17 +83,20 @@
 
     @Override
     public boolean hasContext(final String fqcn, final ClassLoader loader, final boolean currentContext) {
+        LoggerContext ctx;
         if (currentContext) {
-            return ContextAnchor.THREAD_CONTEXT.get() != null;
+            ctx = ContextAnchor.THREAD_CONTEXT.get();
         } else if (loader != null) {
-            return findContext(loader) != null;
+            ctx = findContext(loader);
         } else {
             final Class<?> clazz = StackLocatorUtil.getCallerClass(fqcn);
             if (clazz != null) {
-                return findContext(clazz.getClassLoader()) != null;
+                ctx = findContext(clazz.getClassLoader());
+            } else {
+                ctx = ContextAnchor.THREAD_CONTEXT.get();
             }
-            return ContextAnchor.THREAD_CONTEXT.get() != null;
         }
+        return ctx != null && ctx.isStarted();
     }
 
     private LoggerContext findContext(ClassLoader loaderOrNull) {
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/ContextSelector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/ContextSelector.java
index 41f7d39..3eb0837 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/ContextSelector.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/ContextSelector.java
@@ -35,7 +35,7 @@
      * @param loader The ClassLoader to use or null.
      * @param currentContext If true returns the current Context, if false returns the Context appropriate
      * @param allContexts if true all LoggerContexts that can be located will be shutdown.
-     * @since 3.0
+     * @since 2.13.0
      */
     default void shutdown(final String fqcn, final ClassLoader loader, final boolean currentContext,
                           final boolean allContexts) {
@@ -51,7 +51,7 @@
      * @param currentContext If true returns the current Context, if false returns the Context appropriate
      * for the caller if a more appropriate Context can be determined.
      * @return true if a LoggerContext has been installed, false otherwise.
-     * @since 3.0
+     * @since 2.13.0
      */
     default boolean hasContext(String fqcn, ClassLoader loader, boolean currentContext) {
         return false;
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/JndiContextSelector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/JndiContextSelector.java
index 67c32ae..36571fd 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/JndiContextSelector.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/JndiContextSelector.java
@@ -109,14 +109,15 @@
 
     @Override
     public boolean hasContext(String fqcn, ClassLoader loader, boolean currentContext) {
-        if (ContextAnchor.THREAD_CONTEXT.get() != null) {
-            return true;
+        LoggerContext ctx = ContextAnchor.THREAD_CONTEXT.get();
+        if (ctx == null) {
+            String loggingContextName = getContextName();
+            if (loggingContextName == null) {
+                return false;
+            }
+            ctx = CONTEXT_MAP.get(loggingContextName);
         }
-        String loggingContextName = getContextName();
-        if (loggingContextName == null) {
-            return false;
-        }
-        return CONTEXT_MAP.containsKey(loggingContextName);
+        return ctx != null && ctx.isStarted();
     }
 
     @Override
diff --git a/log4j-kubernetes/pom.xml b/log4j-kubernetes/pom.xml
new file mode 100644
index 0000000..474d097
--- /dev/null
+++ b/log4j-kubernetes/pom.xml
@@ -0,0 +1,207 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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 xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.logging.log4j</groupId>
+    <artifactId>log4j</artifactId>
+    <version>3.0.0-SNAPSHOT</version>
+    <relativePath>../</relativePath>
+  </parent>
+  <artifactId>log4j-kubernetes</artifactId>
+  <packaging>jar</packaging>
+  <name>Apache Log4j Kubernetes Library</name>
+  <description>Apache Log4j Kubernetes Support</description>
+  <properties>
+    <log4jParentDir>${basedir}/..</log4jParentDir>
+    <docLabel>Log4j Kubernetes Library Documentation</docLabel>
+    <projectDir>/kubernetes</projectDir>
+    <maven.compiler.source>1.8</maven.compiler.source>
+    <maven.compiler.target>1.8</maven.compiler.target>
+    <module.name>org.apache.logging.log4j.kubernetes</module.name>
+  </properties>
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-core</artifactId>
+    </dependency>
+    <!-- Kubernetes Client -->
+    <dependency>
+      <groupId>io.fabric8</groupId>
+      <artifactId>kubernetes-client</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </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>
+        <artifactId>maven-remote-resources-plugin</artifactId>
+        <executions>
+          <execution>
+            <goals>
+              <goal>process</goal>
+            </goals>
+            <configuration>
+              <skip>false</skip>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <forkCount>1</forkCount>
+          <reuseForks>false</reuseForks>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <reporting>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-changes-plugin</artifactId>
+        <version>${changes.plugin.version}</version>
+        <reportSets>
+          <reportSet>
+            <reports>
+              <report>changes-report</report>
+            </reports>
+          </reportSet>
+        </reportSets>
+        <configuration>
+          <issueLinkTemplate>%URL%/show_bug.cgi?id=%ISSUE%</issueLinkTemplate>
+          <useJql>true</useJql>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+        <version>${checkstyle.plugin.version}</version>
+        <configuration>
+          <!--<propertiesLocation>${vfs.parent.dir}/checkstyle.properties</propertiesLocation> -->
+          <configLocation>${log4jParentDir}/checkstyle.xml</configLocation>
+          <suppressionsLocation>${log4jParentDir}/checkstyle-suppressions.xml</suppressionsLocation>
+          <enableRulesSummary>false</enableRulesSummary>
+          <propertyExpansion>basedir=${basedir}</propertyExpansion>
+          <propertyExpansion>licensedir=${log4jParentDir}/checkstyle-header.txt</propertyExpansion>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+        <version>${javadoc.plugin.version}</version>
+        <configuration>
+          <bottom><![CDATA[<p align="center">Copyright &#169; {inceptionYear}-{currentYear} {organizationName}. All Rights Reserved.<br />
+            Apache Logging, Apache Log4j, Log4j, Apache, the Apache feather logo, the Apache Logging project logo,
+            and the Apache Log4j logo are trademarks of The Apache Software Foundation.</p>]]></bottom>
+          <!-- module link generation is completely broken in the javadoc plugin for a multi-module non-aggregating
+               project -->
+          <detectOfflineLinks>false</detectOfflineLinks>
+          <linksource>true</linksource>
+        </configuration>
+        <reportSets>
+          <reportSet>
+            <id>non-aggregate</id>
+            <reports>
+              <report>javadoc</report>
+            </reports>
+          </reportSet>
+        </reportSets>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>findbugs-maven-plugin</artifactId>
+        <version>${findbugs.plugin.version}</version>
+        <configuration>
+          <fork>true</fork>
+          <jvmArgs>-Duser.language=en</jvmArgs>
+          <threshold>Normal</threshold>
+          <effort>Default</effort>
+          <excludeFilterFile>${log4jParentDir}/findbugs-exclude-filter.xml</excludeFilterFile>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jxr-plugin</artifactId>
+        <version>${jxr.plugin.version}</version>
+        <reportSets>
+          <reportSet>
+            <id>non-aggregate</id>
+            <reports>
+              <report>jxr</report>
+            </reports>
+          </reportSet>
+          <reportSet>
+            <id>aggregate</id>
+            <reports>
+              <report>aggregate</report>
+            </reports>
+          </reportSet>
+        </reportSets>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-pmd-plugin</artifactId>
+        <version>${pmd.plugin.version}</version>
+        <configuration>
+          <targetJdk>${maven.compiler.target}</targetJdk>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>net.sourceforge.maven-taglib</groupId>
+        <artifactId>maven-taglib-plugin</artifactId>
+        <version>2.4</version>
+      </plugin>
+    </plugins>
+  </reporting>
+</project>
+
diff --git a/log4j-kubernetes/src/main/java/org/apache/logging/log4j/kubernetes/KubernetesClientBuilder.java b/log4j-kubernetes/src/main/java/org/apache/logging/log4j/kubernetes/KubernetesClientBuilder.java
new file mode 100644
index 0000000..414f9e7
--- /dev/null
+++ b/log4j-kubernetes/src/main/java/org/apache/logging/log4j/kubernetes/KubernetesClientBuilder.java
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+package org.apache.logging.log4j.kubernetes;
+
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.status.StatusLogger;
+
+import io.fabric8.kubernetes.client.Config;
+import io.fabric8.kubernetes.client.ConfigBuilder;
+import io.fabric8.kubernetes.client.DefaultKubernetesClient;
+import io.fabric8.kubernetes.client.KubernetesClient;
+
+/**
+ * Builds a Kubernetes Client.
+ */
+public class KubernetesClientBuilder {
+
+    private static final Logger LOGGER = StatusLogger.getLogger();
+
+    public KubernetesClient createClient() {
+        return new DefaultKubernetesClient(kubernetesClientConfig());
+    }
+
+    private Config kubernetesClientConfig() {
+        Config base = Config.autoConfigure(null);
+        KubernetesClientProperties props = new KubernetesClientProperties(base);
+        Config properties = new ConfigBuilder(base)
+                .withApiVersion(props.getApiVersion())
+                .withCaCertData(props.getCaCertData())
+                .withCaCertFile(props.getCaCertFile())
+                .withClientCertData(props.getClientCertData())
+                .withClientCertFile(props.getClientCertFile())
+                .withClientKeyAlgo(props.getClientKeyAlgo())
+                .withClientKeyData(props.getClientKeyData())
+                .withClientKeyFile(props.getClientKeyFile())
+                .withClientKeyPassphrase(props.getClientKeyPassphrase())
+                .withConnectionTimeout(props.getConnectionTimeout())
+                .withHttpProxy(props.getHttpProxy())
+                .withHttpsProxy(props.getHttpsProxy())
+                .withMasterUrl(props.getMasterUrl())
+                .withNamespace(props.getNamespace())
+                .withNoProxy(props.getNoProxy())
+                .withPassword(props.getPassword())
+                .withProxyPassword(props.getProxyPassword())
+                .withProxyUsername(props.getProxyUsername())
+                .withRequestTimeout(props.getRequestTimeout())
+                .withRollingTimeout(props.getRollingTimeout())
+                .withTrustCerts(props.isTrustCerts())
+                .withUsername(props.getUsername())
+                .build();
+        return properties;
+    }
+}
diff --git a/log4j-kubernetes/src/main/java/org/apache/logging/log4j/kubernetes/KubernetesClientProperties.java b/log4j-kubernetes/src/main/java/org/apache/logging/log4j/kubernetes/KubernetesClientProperties.java
new file mode 100644
index 0000000..61f50e0
--- /dev/null
+++ b/log4j-kubernetes/src/main/java/org/apache/logging/log4j/kubernetes/KubernetesClientProperties.java
@@ -0,0 +1,191 @@
+/*
+ * 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.
+ */
+package org.apache.logging.log4j.kubernetes;
+
+import java.time.Duration;
+
+import org.apache.logging.log4j.util.PropertiesUtil;
+
+import io.fabric8.kubernetes.client.Config;
+
+/**
+ * Obtains properties used to configure the Kubernetes client.
+ */
+public class KubernetesClientProperties {
+
+    private static final String[] PREFIXES = {"log4j2.kubernetes.client.", "spring.cloud.kubernetes.client."};
+    private static final String API_VERSION = "apiVersion";
+    private static final String CA_CERT_FILE = "caCertFile";
+    private static final String CA_CERT_DATA = "caCertData";
+    private static final String CLIENT_CERT_FILE = "clientCertFile";
+    private static final String CLIENT_CERT_DATA = "clientCertData";
+    private static final String CLIENT_KEY_FILE = "clientKeyFile";
+    private static final String CLIENT_KEY_DATA = "cientKeyData";
+    private static final String CLIENT_KEY_ALGO = "clientKeyAlgo";
+    private static final String CLIENT_KEY_PASSPHRASE = "clientKeyPassphrase";
+    private static final String CONNECTION_TIMEOUT = "connectionTimeout";
+    private static final String HTTP_PROXY = "httpProxy";
+    private static final String HTTPS_PROXY = "httpsProxy";
+    private static final String LOGGING_INTERVAL = "loggingInterval";
+    private static final String MASTER_URL = "masterUrl";
+    private static final String NAMESPACE = "namespace";
+    private static final String NO_PROXY = "noProxy";
+    private static final String PASSWORD = "password";
+    private static final String PROXY_USERNAME = "proxyUsername";
+    private static final String PROXY_PASSWORD = "proxyPassword";
+    private static final String REQUEST_TIMEOUT = "requestTimeout";
+    private static final String ROLLING_TIMEOUT = "rollingTimeout";
+    private static final String TRUST_CERTS = "trustCerts";
+    private static final String USERNAME = "username";
+    private static final String WATCH_RECONNECT_INTERVAL = "watchReconnectInterval";
+    private static final String WATCH_RECONNECT_LIMIT = "watchReconnectLimit";
+
+    private PropertiesUtil props = PropertiesUtil.getProperties();
+    private final Config base;
+
+    public KubernetesClientProperties(Config base) {
+        this.base = base;
+    }
+
+
+    public String getApiVersion() {
+        return props.getStringProperty(PREFIXES, API_VERSION, base::getApiVersion);
+    }
+    public String getCaCertFile() {
+        return props.getStringProperty(PREFIXES, CA_CERT_FILE, base::getCaCertFile);
+    }
+
+    public String getCaCertData() {
+        return props.getStringProperty(PREFIXES, CA_CERT_DATA, base::getCaCertData);
+    }
+
+    public String getClientCertFile() {
+        return props.getStringProperty(PREFIXES, CLIENT_CERT_FILE, base::getClientCertFile);
+    }
+
+    public String getClientCertData() {
+        return props.getStringProperty(PREFIXES, CLIENT_CERT_DATA, base::getClientCertData);
+    }
+
+    public String getClientKeyFile() {
+        return props.getStringProperty(PREFIXES, CLIENT_KEY_FILE, base::getClientKeyFile);
+    }
+
+    public String getClientKeyData() {
+        return props.getStringProperty(PREFIXES, CLIENT_KEY_DATA, base::getClientKeyData);
+    }
+
+    public String getClientKeyAlgo() {
+        return props.getStringProperty(PREFIXES, CLIENT_KEY_ALGO, base::getClientKeyAlgo);
+    }
+
+    public String getClientKeyPassphrase() {
+        return props.getStringProperty(PREFIXES, CLIENT_KEY_PASSPHRASE, base::getClientKeyPassphrase);
+    }
+
+    public int getConnectionTimeout() {
+        Duration timeout = props.getDurationProperty(PREFIXES, CONNECTION_TIMEOUT, null);
+        if (timeout != null) {
+            return (int) timeout.toMillis();
+        }
+        return base.getConnectionTimeout();
+    }
+
+    public String getHttpProxy() {
+        return props.getStringProperty(PREFIXES, HTTP_PROXY, base::getHttpProxy);
+    }
+
+    public String getHttpsProxy() {
+        return props.getStringProperty(PREFIXES, HTTPS_PROXY, base::getHttpsProxy);
+    }
+
+    public int getLoggingInterval() {
+        Duration interval = props.getDurationProperty(PREFIXES, LOGGING_INTERVAL, null);
+        if (interval != null) {
+            return (int) interval.toMillis();
+        }
+        return base.getLoggingInterval();
+    }
+
+    public String getMasterUrl() {
+        return props.getStringProperty(PREFIXES, MASTER_URL, base::getMasterUrl);
+    }
+
+    public String getNamespace() {
+        return props.getStringProperty(PREFIXES, NAMESPACE, base::getNamespace);
+    }
+
+    public String[] getNoProxy() {
+        String result = props.getStringProperty(PREFIXES, NO_PROXY, null);
+        if (result != null) {
+            return result.replace("\\s", "").split(",");
+        }
+        return base.getNoProxy();
+    }
+
+    public String getPassword() {
+        return props.getStringProperty(PREFIXES, PASSWORD, base::getPassword);
+    }
+
+    public String getProxyUsername() {
+        return props.getStringProperty(PREFIXES, PROXY_USERNAME, base::getProxyUsername);
+    }
+
+    public String getProxyPassword() {
+        return props.getStringProperty(PREFIXES, PROXY_PASSWORD, base::getProxyPassword);
+    }
+
+    public int getRequestTimeout() {
+        Duration interval = props.getDurationProperty(PREFIXES, REQUEST_TIMEOUT, null);
+        if (interval != null) {
+            return (int) interval.toMillis();
+        }
+        return base.getRequestTimeout();
+    }
+
+    public long getRollingTimeout() {
+        Duration interval = props.getDurationProperty(PREFIXES, ROLLING_TIMEOUT, null);
+        if (interval != null) {
+            return interval.toMillis();
+        }
+        return base.getRollingTimeout();
+    }
+
+    public Boolean isTrustCerts() {
+        return props.getBooleanProperty(PREFIXES, TRUST_CERTS, base::isTrustCerts);
+    }
+
+    public String getUsername() {
+        return props.getStringProperty(PREFIXES, USERNAME, base::getUsername);
+    }
+
+    public int getWatchReconnectInterval() {
+        Duration interval = props.getDurationProperty(PREFIXES, WATCH_RECONNECT_INTERVAL, null);
+        if (interval != null) {
+            return (int) interval.toMillis();
+        }
+        return base.getWatchReconnectInterval();
+    }
+
+    public int getWatchReconnectLimit() {
+        Duration interval = props.getDurationProperty(PREFIXES, WATCH_RECONNECT_LIMIT, null);
+        if (interval != null) {
+            return (int) interval.toMillis();
+        }
+        return base.getWatchReconnectLimit();
+    }
+}
diff --git a/log4j-kubernetes/src/main/java/org/apache/logging/log4j/kubernetes/KubernetesLookup.java b/log4j-kubernetes/src/main/java/org/apache/logging/log4j/kubernetes/KubernetesLookup.java
new file mode 100644
index 0000000..56d7617
--- /dev/null
+++ b/log4j-kubernetes/src/main/java/org/apache/logging/log4j/kubernetes/KubernetesLookup.java
@@ -0,0 +1,221 @@
+/*
+ * 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.
+ */
+package org.apache.logging.log4j.kubernetes;
+
+import java.net.URL;
+import java.nio.file.Paths;
+import java.util.Map;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.lookup.AbstractLookup;
+import org.apache.logging.log4j.core.lookup.StrLookup;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.LoaderUtil;
+import org.apache.logging.log4j.util.Strings;
+
+import io.fabric8.kubernetes.api.model.Container;
+import io.fabric8.kubernetes.api.model.ContainerStatus;
+import io.fabric8.kubernetes.api.model.Namespace;
+import io.fabric8.kubernetes.api.model.Pod;
+import io.fabric8.kubernetes.client.Config;
+import io.fabric8.kubernetes.client.KubernetesClient;
+
+
+/**
+ * Retrieve various Kubernetes attributes. Supported keys are:
+ *  accountName, containerId, containerName, clusterName, host, hostIp, labels, labels.app,
+ *  labels.podTemplateHash, masterUrl, namespaceId, namespaceName, podId, podIp, podName,
+ *  imageId, imageName.
+ */
+@Plugin(name = "k8s", category = StrLookup.CATEGORY)
+public class KubernetesLookup extends AbstractLookup {
+
+    private static final Logger LOGGER = StatusLogger.getLogger();
+    private static final String HOSTNAME = "HOSTNAME";
+    private static final String SPRING_ENVIRONMENT_KEY = "SpringEnvironment";
+
+    private static volatile KubernetesInfo kubernetesInfo;
+    private static Lock initLock = new ReentrantLock();
+    private static boolean isSpringIncluded =
+            LoaderUtil.isClassAvailable("org.apache.logging.log4j.spring.cloud.config.client.SpringEnvironmentHolder");
+
+    private boolean initialize() {
+        if (kubernetesInfo == null || (isSpringIncluded && !kubernetesInfo.isSpringActive)) {
+            initLock.lock();
+            boolean isSpringActive = isSpringActive();
+            if (kubernetesInfo == null || (!kubernetesInfo.isSpringActive && isSpringActive)) {
+                try {
+                    KubernetesClient client = new KubernetesClientBuilder().createClient();
+                    if (client != null) {
+                        KubernetesInfo info = new KubernetesInfo();
+                        info.isSpringActive = isSpringActive;
+                        info.hostName = getHostname();
+                        Pod pod = getCurrentPod(info.hostName, client);
+                        if (pod != null) {
+                            info.app = pod.getMetadata().getLabels().get("app");
+                            final String app = info.app != null ? info.app : "";
+                            info.podTemplateHash = pod.getMetadata().getLabels().get("pod-template-hash");
+                            info.accountName = pod.getSpec().getServiceAccountName();
+                            info.clusterName = pod.getMetadata().getClusterName();
+                            info.hostIp = pod.getStatus().getHostIP();
+                            info.labels = pod.getMetadata().getLabels();
+                            info.podId = pod.getMetadata().getUid();
+                            info.podIp = pod.getStatus().getPodIP();
+                            info.podName = pod.getMetadata().getName();
+                            Container container = pod.getSpec().getContainers().stream()
+                                    .filter(c -> c.getName().equals(app)).findFirst().orElse(null);
+                            if (container != null) {
+                                info.containerName = container.getName();
+                                info.imageName = container.getImage();
+                            }
+                            info.masterUrl = client.getMasterUrl();
+                            info.namespace = pod.getMetadata().getNamespace();
+                            Namespace namespace = client.namespaces().withName(info.namespace).get();
+                            if (namespace != null) {
+                                info.namespaceId = namespace.getMetadata().getUid();
+                            }
+                            ContainerStatus containerStatus = pod.getStatus().getContainerStatuses().stream()
+                                    .filter(cs -> cs.getName().equals(app)).findFirst().orElse(null);
+                            if (containerStatus != null) {
+                                info.containerId = containerStatus.getContainerID();
+                                info.imageId = containerStatus.getImageID();
+                            }
+                            kubernetesInfo = info;
+                        }
+                    }
+                } finally {
+                    initLock.unlock();
+                }
+            }
+        }
+        return kubernetesInfo != null;
+    }
+
+    @Override
+    public String lookup(LogEvent event, String key) {
+        if (!initialize()) {
+            return null;
+        }
+        switch (key) {
+            case "accountName": {
+                return kubernetesInfo.accountName;
+            }
+            case "containerId": {
+                return kubernetesInfo.containerId;
+            }
+            case "containerName": {
+                return kubernetesInfo.containerName;
+            }
+            case "clusterName": {
+                return kubernetesInfo.clusterName;
+            }
+            case "host": {
+                return kubernetesInfo.hostName;
+            }
+            case "hostIp": {
+                return kubernetesInfo.hostIp;
+            }
+            case "labels": {
+                return kubernetesInfo.labels.toString();
+            }
+            case "labels.app": {
+                return kubernetesInfo.app;
+            }
+            case "labels.podTemplateHash": {
+                return kubernetesInfo.podTemplateHash;
+            }
+            case "masterUrl": {
+                return kubernetesInfo.masterUrl.toString();
+            }
+            case "namespaceId": {
+                return kubernetesInfo.namespaceId;
+            }
+            case "namespaceName": {
+                return kubernetesInfo.namespace;
+            }
+            case "podId": {
+                return kubernetesInfo.podId;
+            }
+            case "podIp": {
+                return kubernetesInfo.podIp;
+            }
+            case "podName": {
+                return kubernetesInfo.podName;
+            }
+            case "imageId": {
+                return kubernetesInfo.imageId;
+            }
+            case "imageName": {
+                return kubernetesInfo.imageName;
+            }
+            default:
+                return null;
+        }
+    }
+
+    private String getHostname() {
+        return System.getenv(HOSTNAME);
+    }
+
+    private Pod getCurrentPod(String hostName, KubernetesClient kubernetesClient) {
+        try {
+            if (isServiceAccount() && Strings.isNotBlank(hostName)) {
+                return kubernetesClient.pods().withName(hostName).get();
+            }
+        } catch (Throwable t) {
+            LOGGER.debug("Unable to locate pod with name {}.", hostName);
+        }
+        return null;
+    }
+
+    private boolean isServiceAccount() {
+        return Paths.get(Config.KUBERNETES_SERVICE_ACCOUNT_TOKEN_PATH).toFile().exists()
+                && Paths.get(Config.KUBERNETES_SERVICE_ACCOUNT_CA_CRT_PATH).toFile().exists();
+    }
+
+    private boolean isSpringActive() {
+        return isSpringIncluded && LogManager.getFactory() != null
+            && LogManager.getFactory().hasContext(KubernetesLookup.class.getName(), null, false)
+            && LogManager.getContext(false).getObject(SPRING_ENVIRONMENT_KEY) != null;
+    }
+
+    private static class KubernetesInfo {
+        boolean isSpringActive;
+        String accountName;
+        String app;
+        String clusterName;
+        String containerId;
+        String containerName;
+        String hostName;
+        String hostIp;
+        String imageId;
+        String imageName;
+        Map<String, String> labels;
+        URL masterUrl;
+        String namespace;
+        String namespaceId;
+        String podId;
+        String podIp;
+        String podName;
+        String podTemplateHash;
+    }
+}
diff --git a/log4j-kubernetes/src/site/markdown/index.md.vm b/log4j-kubernetes/src/site/markdown/index.md.vm
new file mode 100644
index 0000000..d51e757
--- /dev/null
+++ b/log4j-kubernetes/src/site/markdown/index.md.vm
@@ -0,0 +1,104 @@
+<!-- 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 Kubernetes Support
+
+Log4j supports Kubernetes by providing a Lookup to retrieve container information.
+
+$h2 Accessing Kubernetes
+
+The Log4j Kubernetes support requires access to the Docker REST interface. In many cases the REST service
+can be accessed automatically. If needed the Kubernetes client can be configured any of the standard
+Log4j configuration locations or via the Spring Boot configuration. Note, however, that since Spring Boot
+causes logging to initialize 3 times and since the Spring environment is only available during the last
+Log4j initialization Spring properties will only be available to Log4j in the last initialization.
+
+$h2 Lookup Attributes
+
+Log4j Kubernetes provides access to the following container attributes:
+
+* accountName - The service account name.
+* clusterName - The name of the cluster the application is running in.
+* containerId - The full id assigned to the container.
+* containerName - The name assigned to the container.
+* host - The name of the host.
+* hostIp - The host's ip address.
+* imageId - The id assigned to the image.
+* imageName - The name assigned to the image.
+* labels - All labels formatted in a list.
+* labels.app - The application name.
+* labels.podTemplateHash - The pod's template hash value.
+* masterUrl - The url needed to access the API server.
+* namespaceId - The id of the namespace the various kubernetes components are located within.
+* namespaceName - The namespace the various kubernetes components are located within.
+* podId - The pod's id number.
+* podIp - The pod's ip address.
+* podName - The name of the pod.
+
+#set( $D = '${' )
+#set( $container = 'k8s:containerId}')
+Attributes may be accessed by adding
+```
+$D$container
+```
+to the configuration. Note that kubernetes variables are only resolved once during logging initialization so they
+shouldn't be referenced with more than one '$' character.
+
+$h2 Configuration
+
+Much of the configuration needed to access the Kubernetes API server is provided automatically by Kubernetes.
+However, it is not uncommon to need to provide the url required to access the Kubernetes API server or the
+namespace the application is assigned to. The properties below may either be configured using the Log4j
+variable names and located by Log4j's normal property resolution mechansim or Log4j will resolve the
+spring properties when the application is running in Spring Boot and the Spring Environment has been created.
+Note that Spring Boot initializes logging 3 times and only the last will have a Spring Environment present.
+
+| Log4j Property Name     | Spring Property Name  | Default   | Description |
+|------------------------ |----------------------:|----------:|------------:|
+| log4j2.kubernetes.client.apiVersion | spring.cloud.kubernetes.client.apiVersion | v1 | Kubernetes API Version |
+| log4j2.kubernetes.client.caCertData | spring.cloud.kubernetes.client.caCertData | | Kubernetes API CACertData |
+| log4j2.kubernetes.client.caCertFile | spring.cloud.kubernetes.client.caCertFile | | Kubernetes API CACertFile |
+| log4j2.kubernetes.client.clientCertData | spring.cloud.kubernetes.client.clientCertData | | Kubernetes API ClientCertData |
+| log4j2.kubernetes.client.clientCertFile | spring.cloud.kubernetes.client.clientCertFile | | Kubernetes API ClientCertFile |
+| log4j2.kubernetes.client.clientKeyAlgo | spring.cloud.kubernetes.client.clientKeyAlgo | RSA | Kubernetes API ClientKeyAlgo |
+| log4j2.kubernetes.client.clientKeyData | spring.cloud.kubernetes.client.clientKeyData | | Kubernetes API ClientKeyData |
+| log4j2.kubernetes.client.clientKeyFile | spring.cloud.kubernetes.client.clientKeyFile | | Kubernetes API ClientKeyFile |
+| log4j2.kubernetes.client.clientKeyPassPhrase | spring.cloud.kubernetes.client.clientKeyPassphrase | changeit | Kubernetes API ClientKeyPassphrase |
+| log4j2.kubernetes.client.connectionTimeout | spring.cloud.kubernetes.client.connectionTimeout | 10s | Connection timeout |
+| log4j2.kubernetes.client.httpProxy | spring.cloud.kubernetes.client.http-proxy | | |
+| log4j2.kubernetes.client.httpsProxy | spring.cloud.kubernetes.client.https-proxy | | |
+| log4j2.kubernetes.client.loggingInberval | spring.cloud.kubernetes.client.loggingInterval | 20s | Logging interval |
+| log4j2.kubernetes.client.masterUrl | spring.cloud.kubernetes.client.masterUrl | kubernetes.default.svc | Kubernetes API Master Node URL |
+| log4j2.kubernetes.client.namespacce | spring.cloud.kubernetes.client.namespace | default | Kubernetes Namespace |
+| log4j2.kubernetes.client.noProxy | spring.cloud.kubernetes.client.noProxy | | |
+| log4j2.kubernetes.client.password | spring.cloud.kubernetes.client.password | | Kubernetes API Password |
+| log4j2.kubernetes.client.proxyPassword | spring.cloud.kubernetes.client.proxyPassword | | |
+| log4j2.kubernetes.client.proxyUsername | spring.cloud.kubernetes.client.proxyUsername | | |
+| log4j2.kubernetes.client.requestTimeout | spring.cloud.kubernetes.client.requestTimeout | 10s | Request timeout |
+| log4j2.kubernetes.client.rollingTimeout | spring.cloud.kubernetes.client.rollingTimeout | 900s | Rolling timeout |
+| log4j2.kubernetes.client.trustCerts | spring.cloud.kubernetes.client.trustCerts | false | Kubernetes API Trust Certificates |
+| log4j2.kubernetes.client.username | spring.cloud.kubernetes.client.username | | Kubernetes API Username |
+| log4j2.kubernetes.client.watchReconnectInterval | spring.cloud.kubernetes.client.watchReconnectInterval | 1s | Reconnect Interval |
+| log4j2.kubernetes.client.watchReconnectLimit | spring.cloud.kubernetes.client.watchReconnectLimit | -1 | Reconnect Interval limit retries |
+
+$h2 Requirements
+Log4j Kubernetes requires Log4j Core, Log4j API and a minimum of Java 8.
+For more information, see [Runtime Dependencies](../runtime-dependencies.html).
diff --git a/log4j-kubernetes/src/site/site.xml b/log4j-kubernetes/src/site/site.xml
new file mode 100644
index 0000000..7322f3b
--- /dev/null
+++ b/log4j-kubernetes/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-spring-cloud-config/log4j-spring-cloud-config-client/src/main/java/org/apache/logging/log4j/spring/cloud/config/client/Log4j2CloudConfigLoggingSystem.java b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/java/org/apache/logging/log4j/spring/cloud/config/client/Log4j2CloudConfigLoggingSystem.java
index 5c4aea0..cf2a220 100644
--- a/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/java/org/apache/logging/log4j/spring/cloud/config/client/Log4j2CloudConfigLoggingSystem.java
+++ b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/java/org/apache/logging/log4j/spring/cloud/config/client/Log4j2CloudConfigLoggingSystem.java
@@ -16,6 +16,7 @@
  */
 package org.apache.logging.log4j.spring.cloud.config.client;
 
+import javax.net.ssl.HttpsURLConnection;
 import java.io.File;
 import java.io.IOException;
 import java.net.URISyntaxException;
@@ -24,7 +25,6 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.Properties;
-import javax.net.ssl.HttpsURLConnection;
 
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.core.LoggerContext;
@@ -68,7 +68,6 @@
         super.initialize(initializationContext, configLocation, logFile);
     }
 
-
     @Override
     protected String[] getStandardConfigLocations() {
         String[] locations = super.getStandardConfigLocations();
@@ -77,7 +76,7 @@
         if (location != null) {
             List<String> list = Arrays.asList(super.getStandardConfigLocations());
             list.add(location);
-            locations = list.toArray(new String[list.size()]);
+            locations = list.toArray(new String[0]);
         }
         return locations;
     }
diff --git a/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/java/org/apache/logging/log4j/spring/cloud/config/client/Log4j2EventListener.java b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/java/org/apache/logging/log4j/spring/cloud/config/client/Log4j2EventListener.java
index 96812d2..eb21607 100644
--- a/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/java/org/apache/logging/log4j/spring/cloud/config/client/Log4j2EventListener.java
+++ b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/java/org/apache/logging/log4j/spring/cloud/config/client/Log4j2EventListener.java
@@ -19,7 +19,6 @@
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-
 import org.springframework.cloud.bus.ConditionalOnBusEnabled;
 import org.springframework.cloud.bus.SpringCloudBusClient;
 import org.springframework.cloud.bus.event.RemoteApplicationEvent;
diff --git a/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/java/org/apache/logging/log4j/spring/cloud/config/client/SpringEnvironmentHolder.java b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/java/org/apache/logging/log4j/spring/cloud/config/client/SpringEnvironmentHolder.java
new file mode 100644
index 0000000..c633e8a
--- /dev/null
+++ b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/java/org/apache/logging/log4j/spring/cloud/config/client/SpringEnvironmentHolder.java
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+package org.apache.logging.log4j.spring.cloud.config.client;
+
+import org.apache.logging.log4j.LogManager;
+import org.springframework.core.env.Environment;
+
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Provides access to the Spring Environment.
+ */
+public class SpringEnvironmentHolder {
+
+    private volatile Environment environment;
+    private Lock lock = new ReentrantLock();
+
+
+    protected Environment getEnvironment() {
+        if (environment == null && LogManager.getFactory() != null && LogManager.getFactory().hasContext(SpringEnvironmentHolder.class.getName(), null, false)) {
+            lock.lock();
+            try {
+                if (environment == null) {
+                    Object obj = LogManager.getContext(false).getObject(Log4j2CloudConfigLoggingSystem.ENVIRONMENT_KEY);
+                    environment = obj instanceof Environment ? (Environment) obj : null;
+                }
+            } finally {
+                lock.unlock();
+            }
+        }
+        return environment;
+    }
+}
diff --git a/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/java/org/apache/logging/log4j/spring/cloud/config/client/SpringLookup.java b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/java/org/apache/logging/log4j/spring/cloud/config/client/SpringLookup.java
index 53c97ea..076f3b1 100644
--- a/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/java/org/apache/logging/log4j/spring/cloud/config/client/SpringLookup.java
+++ b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/java/org/apache/logging/log4j/spring/cloud/config/client/SpringLookup.java
@@ -16,7 +16,6 @@
  */
 package org.apache.logging.log4j.spring.cloud.config.client;
 
-import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.lookup.StrLookup;
 import org.apache.logging.log4j.plugins.Plugin;
@@ -26,19 +25,17 @@
  * Lookup for Spring properties.
  */
 @Plugin(name = "spring", category = StrLookup.CATEGORY)
-public class SpringLookup implements StrLookup {
-
-    private final Environment environment;
+public class SpringLookup extends SpringEnvironmentHolder implements StrLookup {
 
     public SpringLookup() {
-        Object obj = LogManager.getContext(false).getObject(Log4j2CloudConfigLoggingSystem.ENVIRONMENT_KEY);
-        environment = obj instanceof Environment ? (Environment) obj : null;
+        getEnvironment();
     }
 
     @Override
     public String lookup(String key) {
-        if (environment != null) {
-            return environment.getProperty(key);
+        Environment env = getEnvironment();
+        if (env != null) {
+            return env.getProperty(key);
         }
         return null;
     }
diff --git a/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/java/org/apache/logging/log4j/spring/cloud/config/client/SpringPropertySource.java b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/java/org/apache/logging/log4j/spring/cloud/config/client/SpringPropertySource.java
new file mode 100644
index 0000000..8ffed65
--- /dev/null
+++ b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/java/org/apache/logging/log4j/spring/cloud/config/client/SpringPropertySource.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+package org.apache.logging.log4j.spring.cloud.config.client;
+
+import org.apache.logging.log4j.util.PropertySource;
+import org.springframework.core.env.Environment;
+
+/**
+ * Returns properties from Spring.
+ */
+public class SpringPropertySource extends SpringEnvironmentHolder implements PropertySource {
+
+    /**
+     * System properties take precendence followed by properties in Log4j properties files. Spring properties
+     * follow.
+     * @return This PropertySource's priority.
+     */
+    @Override
+    public int getPriority() {
+        return -50;
+    }
+
+    @Override
+    public String getProperty(String key) {
+        Environment environment = getEnvironment();
+        if (environment != null) {
+            return environment.getProperty(key);
+        }
+        return null;
+    }
+
+    @Override
+    public boolean containsProperty(String key) {
+        Environment environment = getEnvironment();
+        if (environment != null) {
+            return environment.containsProperty(key);
+        }
+        return false;
+    }
+}
diff --git a/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/Dockerfile b/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/Dockerfile
index 89d1ee9..d4402e8 100644
--- a/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/Dockerfile
+++ b/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/Dockerfile
@@ -1,3 +1,19 @@
+#
+# 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.
+#
 # Alpine Linux with OpenJDK
 #FROM openjdk:8-jdk-alpine
 FROM openjdk:11-jdk-slim
@@ -13,4 +29,5 @@
 EXPOSE 8080
 
 #CMD java "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005" -jar sampleapp.jar
-CMD java -jar -Xmx2G sampleapp.jar
+ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar -Xmx1G sampleapp.jar"]
+
diff --git a/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/k8s/deploy.sh b/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/k8s/deploy.sh
new file mode 100755
index 0000000..9f67de2
--- /dev/null
+++ b/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/k8s/deploy.sh
@@ -0,0 +1,36 @@
+#!/usr/bin/env bash
+#
+#
+# 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.
+#
+
+echo "Building, (re)creating, starting, and attaching to containers for a service."
+
+imageName=sampleapp
+containerName=sampleapp-container
+networkName=docker_sampleapp
+debug_port=5005
+#debug_expose="-p $debug_port:$debug_port"
+exposed_ports="-p 8080:8090 $debug_expose"
+
+mvn clean package -DskipTests=true
+docker build --no-cache -t $imageName -f Dockerfile  .
+
+docker tag $imageName localhost:5000/$imageName
+docker push localhost:5000/$imageName
+kubectl apply -f k8s/sampleapp-deployment.yaml
\ No newline at end of file
diff --git a/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/k8s/sampleapp-deployment.yaml b/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/k8s/sampleapp-deployment.yaml
new file mode 100644
index 0000000..fe5d2b7
--- /dev/null
+++ b/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/k8s/sampleapp-deployment.yaml
@@ -0,0 +1,44 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: sampleapp
+  labels:
+    app: sampleapp
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app: sampleapp
+  template:
+    metadata:
+      labels:
+        app: sampleapp
+    spec:
+      containers:
+        - name: sampleapp
+          image: localhost:5000/sampleapp:latest
+          imagePullPolicy: Always
+          ports:
+            - containerPort: 8080
+            - containerPort: 5005
+          env:
+            - name: JAVA_OPTS
+              value: "-Delastic.search.host=host.docker.internal"
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: sampleapp
+spec:
+  type: NodePort
+  selector:
+    app: sampleapp
+  ports:
+    - protocol: TCP
+      port: 8080
+      nodePort: 30011
+      name: http
+    - protocol: TCP
+      port: 5005
+      nodePort: 30012
+      name: debug
diff --git a/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/k8s/undeploy.sh b/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/k8s/undeploy.sh
new file mode 100755
index 0000000..c5255c9
--- /dev/null
+++ b/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/k8s/undeploy.sh
@@ -0,0 +1,29 @@
+#!/usr/bin/env bash
+#
+#
+# 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.
+#
+
+echo "Building, (re)creating, starting, and attaching to containers for a service."
+
+imageName=sampleapp
+containerName=sampleapp-container
+networkName=docker_sampleapp
+
+kubectl delete deploy/$imageName svc/$imageName
+
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 412ea24..1fd45b4 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
@@ -92,6 +92,11 @@
       <artifactId>log4j-docker</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-kubernetes</artifactId>
+      <version>${project.version}</version>
+    </dependency>
     <!-- Required for Async Loggers -->
     <dependency>
       <groupId>com.lmax</groupId>
diff --git a/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/src/main/resources/application.yml b/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/src/main/resources/application.yml
index 71ca238..7e9838e 100644
--- a/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/src/main/resources/application.yml
+++ b/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/src/main/resources/application.yml
@@ -27,4 +27,7 @@
     addresses: rabbit
     port: 5672
     username: guest
-    password: guest
\ No newline at end of file
+    password: guest
+
+log4j2:
+  debug: true
\ 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/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 40d3098..2e9dcfa 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
@@ -1,15 +1,27 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+
+-->
 <Configuration status="DEBUG" monitorInterval="0">
   <Properties>
     <Property name="spring.application.name">sampleapp</Property>
   </Properties>
   <Appenders>
-    <Console name="console" target="SYSTEM_OUT">
-      <JsonLayout properties="true" compact="true" eventEol="true" stackTraceAsString="true">
-      </JsonLayout>
-    </Console>
-    <Flume name="flume" ignoreExceptions="false" type="Embedded" compress="false">
+    <!--<Flume name="flume" ignoreExceptions="false" type="Embedded" compress="false">
       <Property name="channel.type">memory</Property>
       <Property name="channel.capacity">100000</Property>
       <Property name="channel.transactionCapacity">5000</Property>
@@ -88,20 +100,53 @@
       </RFC5424Layout>
       <SizeBasedTriggeringPolicy size="10MB" />
       <DefaultRolloverStrategy max="5"/>
-    </RollingFile>
+    </RollingFile>-->
+    <Socket name="Elastic" host="\${sys:elastic.search.host:-localhost}" port="12222" protocol="tcp" bufferedIo="true" ignoreExceptions="false">
+      <GelfLayout includeStackTrace="true" host="${hostName}" includeThreadContext="true" includeNullDelimiter="true"
+                  compressionType="OFF">
+        <ThreadContextIncludes>requestId,sessionId,loginId,userId,ipAddress,corpAcctNumber,callingHost,ohBehalfOf,onBehalfOfAccount</ThreadContextIncludes>
+        <MessagePattern>%d [%t] %-5p %X{requestId, sessionId, loginId, userId, ipAddress, corpAcctNumber} %C{1.}.%M:%L - %m%n</MessagePattern>
+        <KeyValuePair key="docker.containerId" value="\${docker:containerId:-}"/>
+        <KeyValuePair key="application" value="$\${lower:\${spring:spring.application.name}}"/>
+        <KeyValuePair key="kubernetes.serviceAccountName" value="\${k8s:accountName:-}"/>
+        <KeyValuePair key="kubernetes.containerId" value="\${k8s:containerId:-}"/>
+        <KeyValuePair key="kubernetes.containerName" value="\${k8s:containerName:-}"/>
+        <KeyValuePair key="kubernetes.host" value="\${k8s:host:-}"/>
+        <KeyValuePair key="kubernetes.labels.app" value="\${k8s:labels.app:-}"/>
+        <KeyValuePair key="kubernetes.labels.pod-template-hash" value="\${k8s:labels.podTemplateHash:-}"/>
+        <KeyValuePair key="kubernetes.master_url" value="\${k8s:masterUrl:-}"/>
+        <KeyValuePair key="kubernetes.namespaceId" value="\${k8s:namespaceId:-}"/>
+        <KeyValuePair key="kubernetes.namespaceName" value="\${k8s:namespaceName:-}"/>
+        <KeyValuePair key="kubernetes.podID" value="\${k8s:podId:-}"/>
+        <KeyValuePair key="kubernetes.podIP" value="\${k8s:podIp:-}"/>
+        <KeyValuePair key="kubernetes.podName" value="\${k8s:podName:-}"/>
+        <KeyValuePair key="kubernetes.imageId" value="\${k8s:imageId:-}"/>
+        <KeyValuePair key="kubernetes.imageName" value="\${k8s:imageName:-}"/>
+      </GelfLayout>
+    </Socket>
+    <Console name="Console" target="SYSTEM_OUT">
+      <RFC5424Layout enterpriseNumber="50177" includeMDC="true" mdcId="RequestContext" appName="SalesforceGateway"
+                     mdcPrefix="" newLine="true" mdcIncludes="requestId,sessionId,loginId,userId,ipAddress,corpAcctNumber"/>
+    </Console>
+    <Failover name="log4j" primary="Elastic">
+      <Failovers>
+        <AppenderRef ref="Console"/>
+      </Failovers>
+    </Failover>
   </Appenders>
+
   <Loggers>
     <Logger name="org.apache.kafka" level="warn" additivity="false">
-      <AppenderRef ref="console"/>
+      <AppenderRef ref="log4j"/>
     </Logger>
     <Logger name="org.apache.flume" level="warn" additivity="false">
-      <AppenderRef ref="console"/>
+      <AppenderRef ref="log4j"/>
     </Logger>
     <Logger name="org.apache.avro" level="warn" additivity="false">
-      <AppenderRef ref="console"/>
+      <AppenderRef ref="log4j"/>
     </Logger>
     <Root level="DEBUG">
-      <AppenderRef ref="console"/>
+      <AppenderRef ref="log4j"/>
     </Root>
   </Loggers>
 </Configuration>
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 425915c..59e7307 100644
--- a/pom.xml
+++ b/pom.xml
@@ -192,6 +192,7 @@
     <jackson1Version>1.9.13</jackson1Version>

     <jackson2Version>2.9.9</jackson2Version>

     <springVersion>3.2.18.RELEASE</springVersion>

+    <kubernetes-client.version>4.6.1</kubernetes-client.version>

     <flumeVersion>1.9.0</flumeVersion>

     <disruptorVersion>3.4.2</disruptorVersion>

     <conversantDisruptorVersion>1.2.15</conversantDisruptorVersion>

@@ -745,6 +746,11 @@
         <version>${springVersion}</version>

       </dependency>

       <dependency>

+        <groupId>io.fabric8</groupId>

+        <artifactId>kubernetes-client</artifactId>

+        <version>${kubernetes-client.version}</version>

+      </dependency>

+      <dependency>

         <groupId>org.hsqldb</groupId>

         <artifactId>hsqldb</artifactId>

         <version>2.4.1</version>

@@ -1265,6 +1271,13 @@
             <!-- Other -->

             <exclude>felix-cache/**</exclude>

             <exclude>RELEASE-NOTES.md</exclude>

+            <exclude>**/*.yml</exclude>

+            <exclude>**/*.yaml</exclude>

+            <exclude>**/*.json</exclude>

+            <excllude>**/images/*.drawio</excllude>

+            <exclude>**/fluent-bit.conf</exclude>

+            <exclude>**/rabbitmq.config</exclude>

+            <exclude>**/MANIFEST.MF</exclude>

           </excludes>

         </configuration>

       </plugin>

@@ -1448,6 +1461,13 @@
             <!-- Other -->

             <exclude>felix-cache/**</exclude>

             <exclude>RELEASE-NOTES.txt</exclude>

+            <exclude>**/*.yml</exclude>

+            <exclude>**/*.yaml</exclude>

+            <exclude>**/*.json</exclude>

+            <excllude>**/images/*.drawio</excllude>

+            <exclude>**/fluent-bit.conf</exclude>

+            <exclude>**/rabbitmq.config</exclude>

+            <exclude>**/MANIFEST.MF</exclude>

           </excludes>

         </configuration>

       </plugin>

@@ -1515,6 +1535,7 @@
     <module>log4j-smtp</module>

     <module>log4j-osgi</module>

     <module>log4j-docker</module>

+    <module>log4j-kubernetes</module>

     <module>log4j-spring-cloud-config</module>

   </modules>

   <profiles>

@@ -1634,6 +1655,13 @@
                 <!-- Other -->

                 <exclude>felix-cache/**</exclude>

                 <exclude>RELEASE-NOTES.md</exclude>

+                <exclude>**/*.yml</exclude>

+                <exclude>**/*.yaml</exclude>

+                <exclude>**/*.json</exclude>

+                <excllude>**/images/*.drawio</excllude>

+                <exclude>**/fluent-bit.conf</exclude>

+                <exclude>**/rabbitmq.config</exclude>

+                <exclude>**/MANIFEST.MF</exclude>

               </excludes>

             </configuration>

             <executions>

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 5ecb436..e7ab73f 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -157,6 +157,10 @@
       </action>
     </release>
     <release version="2.13.0" date="2019-MM-DD" description="GA Release 2.13.0">
+      <action issue="LOG4J2-2716" dev="rgoers" type="add">
+        Add the ability to lookup Kubernetes attributes in the Log4j configuration. Allow Log4j properties to
+        be retrieved from the Spring environment if it is available.
+      </action>
       <action issue="LOG4J2-2710" dev="rgoers" type="add">
         Allow Spring Boot application properties to be accessed in the Log4j 2 configuraiton. Add
         lower and upper case Lookups.
diff --git a/src/site/asciidoc/manual/lookups.adoc b/src/site/asciidoc/manual/lookups.adoc
index cea14ff..601b67f 100644
--- a/src/site/asciidoc/manual/lookups.adoc
+++ b/src/site/asciidoc/manual/lookups.adoc
@@ -80,7 +80,7 @@
 |containerId
 |The full id assigned to the container.
 
-|containreName
+|containerName
 |The name assigned to the container.
 
 |imageId
@@ -221,6 +221,58 @@
 
 *Java's JMX module is not available on Android or on Google App Engine.*
 
+[#KubernetesLookup]
+== Kubernetes Lookup
+
+The KubernetesLookup can be used to lookup attributes from the Kubernetes environment for the container
+the application is running in.
+
+Log4j Kubernetes provides access to the following container attributes:
+[cols="1m,4a"]
+|===
+|Attribute |Description
+|accountName|The service account name
+|clusterName|The name of the cluster the application is deployed in
+|containerId|>The full id assigned to the container
+|containerName|The name assigned to the container
+|host|The name assigned to the host operating system
+|hostIp|The host's ip address
+|imageId|The id assigned to the container image
+|imageName|The name assigned to the container image
+|labels|All labels formatted in a list
+|labesl.app|The application name
+|labels.podTemplateHash|The pod's template hash value
+|masterUrl|The URL used to access the API server
+|namespaceId|The id of the namespace the various kubernetes components are located within
+|namespaceName|The namespace the various kubernetes components are located within
+|podId|The pod's ip number
+|podIp|The pod's ip address
+|podName|The name of the pod
+
+      <GelfLayout includeStackTrace="true" host="${hostName}" includeThreadContext="true" includeNullDelimiter="true" compressionType="OFF">
+        <ThreadContextIncludes>requestId,sessionId,loginId,userId,ipAddress,callingHost</ThreadContextIncludes>
+        <MessagePattern>%d [%t] %-5p %X{requestId, sessionId, loginId, userId, ipAddress} %C{1.}.%M:%L - %m%n</MessagePattern>
+        <KeyValuePair key="docker.containerId" value="${docker:containerId:-}"/>
+        <KeyValuePair key="application" value="$${lower:${spring:spring.application.name}}"/>
+        <KeyValuePair key="kubernetes.serviceAccountName" value="${k8s:accountName:-}"/>
+        <KeyValuePair key="kubernetes.clusterName" value="${k8s:clusterName:-}/>
+        <KeyValuePair key="kubernetes.containerId" value="${k8s:containerId:-}"/>
+        <KeyValuePair key="kubernetes.containerName" value="${k8s:containerName:-}"/>
+        <KeyValuePair key="kubernetes.host" value="${k8s:host:-}"/>
+        <KeyValuePair key="kubernetes.labels.app" value="${k8s:labels.app:-}"/>
+        <KeyValuePair key="kubernetes.labels.pod-template-hash" value="${k8s:labels.podTemplateHash:-}"/>
+        <KeyValuePair key="kubernetes.master_url" value="${k8s:masterUrl:-}"/>
+        <KeyValuePair key="kubernetes.namespaceId" value="${k8s:namespaceId:-}"/>
+        <KeyValuePair key="kubernetes.namespaceName" value="${k8s:namespaceName:-}"/>
+        <KeyValuePair key="kubernetes.podID" value="${k8s:podId:-}"/>
+        <KeyValuePair key="kubernetes.podIP" value="${k8s:podIp:-}"/>
+        <KeyValuePair key="kubernetes.podName" value="${k8s:podName:-}"/>
+        <KeyValuePair key="kubernetes.imageId" value="${k8s:imageId:-}"/>
+        <KeyValuePair key="kubernetes.imageName" value="${k8s:imageName:-}"/>
+      </GelfLayout>]]></pre>
+
+This Lookup is subject to the configuration requirements listed at link:../log4j-kubernetes/index.html[Log4j Kubernetes Support]
+
 [#Log4jConfigLookup]
 == Log4j Configuration Location Lookup
 
diff --git a/src/site/markdown/manual/cloud.md b/src/site/markdown/manual/cloud.md
index 9336914..e71753e 100644
--- a/src/site/markdown/manual/cloud.md
+++ b/src/site/markdown/manual/cloud.md
@@ -45,7 +45,6 @@
     OutputBenchmark.file      thrpt   20  654584.309 ± 59399.092  ops/s
     OutputBenchmark.redirect  thrpt   20   70284.576 ±  7452.167  ops/s
     ```
-
 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
@@ -134,6 +133,20 @@
         <MessagePattern>%d [%t] %-5p %X{requestId, sessionId, loginId, userId, ipAddress} %C{1.}.%M:%L - %m%n</MessagePattern>
         <KeyValuePair key="containerId" value="${docker:containerId:-}"/>
         <KeyValuePair key="application" value="$${lower:${spring:spring.application.name:-spring}}"/>
+        <KeyValuePair key="kubernetes.serviceAccountName" value="${k8s:accountName:-}"/>
+        <KeyValuePair key="kubernetes.containerId" value="${k8s:containerId:-}"/>
+        <KeyValuePair key="kubernetes.containerName" value="${k8s:containerName:-}"/>
+        <KeyValuePair key="kubernetes.host" value="${k8s:host:-}"/>
+        <KeyValuePair key="kubernetes.labels.app" value="${k8s:labels.app:-}"/>
+        <KeyValuePair key="kubernetes.labels.pod-template-hash" value="${k8s:labels.podTemplateHash:-}"/>
+        <KeyValuePair key="kubernetes.master_url" value="${k8s:masterUrl:-}"/>
+        <KeyValuePair key="kubernetes.namespaceId" value="${k8s:namespaceId:-}"/>
+        <KeyValuePair key="kubernetes.namespaceName" value="${k8s:namespaceName:-}"/>
+        <KeyValuePair key="kubernetes.podID" value="${k8s:podId:-}"/>
+        <KeyValuePair key="kubernetes.podIP" value="${k8s:podIp:-}"/>
+        <KeyValuePair key="kubernetes.podName" value="${k8s:podName:-}"/>
+        <KeyValuePair key="kubernetes.imageId" value="${k8s:imageId:-}"/>
+        <KeyValuePair key="kubernetes.imageName" value="${k8s:imageName:-}"/>
       </GelfLayout>
     </Socket>
 
@@ -182,12 +195,11 @@
 a file Appender. The ThreadContext attributes, custome fields, thread name, etc. will all be available as attributes
 on each log event that can be used for filtering.
 
-
 ## 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 features of
+by Spring. While this works in a lot of cases it does not support any of the more advanced filtering features 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 
@@ -220,13 +232,20 @@
 provides similar functionality via the [Docker Lookup](lookups.html#DockerLookup). More information on
 Log4j's Docker support may also be found at [Log4j-Docker](../log4j-docker/index.html). 
 
+## Integration with Kubernetes
+
+Applications managed by Kubernetes can bypass the Docker/Kubernetes logging infrastructure and log directly to 
+either a sidecar forwarder or a logging aggragator cluster while still including all the kubernetes 
+attributes by using the Log4j 2 [Kubernetes Lookup](lookups.html#KubernetesLookup). More information on
+Log4j's Kubernetes support may also be found at [Log4j-Kubernetes](../log4j-kubernetes/index.html). 
+
 ## Appender Performance
-The numbers in the table below represent how much time in seconds was required for the application to
-call logger.debug 100,000 times. These numbers only include the time taken to deliver to the specifically
-noted endpoint and many not include the actual time required before they are available for viewing. All
+The numbers in the table below represent how much time in seconds was required for the application to 
+call logger.debug 100,000 times. These numbers only include the time taken to deliver to the specifically 
+noted endpoint and many not include the actual time required before they are available 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 performance comparisons
+by VMWare Fusion and had 4 CPUs and 2 GB of RAM. These number should be used for relative performance comparisons 
 as the results on another system may vary considerably.
 
 The sample application used can be found under the log4j-spring-cloud-config/log4j-spring-cloud-config-samples
@@ -285,9 +304,9 @@
 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 or directly
-to a log aggregator as shown above in [Logging with ELK](#ELK). Use the 
+to a log aggregator as shown above in [Logging with ELK](#ELK). Use the  
 Log4j Docker Lookup to add the container information to each log event.
-1. Whenever guaranteed delivery is required use Flume Avro with a batch size of 1 or another Appender such
+1. Whenever 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.