Merge branch 'master' of https://github.com/apache/logging-log4j-audit into LOG4J2-2421
diff --git a/log4j-audit/log4j-audit-api/src/main/java/org/apache/logging/log4j/audit/AbstractEventLogger.java b/log4j-audit/log4j-audit-api/src/main/java/org/apache/logging/log4j/audit/AbstractEventLogger.java
index e9af187..5de2844 100644
--- a/log4j-audit/log4j-audit-api/src/main/java/org/apache/logging/log4j/audit/AbstractEventLogger.java
+++ b/log4j-audit/log4j-audit-api/src/main/java/org/apache/logging/log4j/audit/AbstractEventLogger.java
@@ -21,6 +21,7 @@
 import org.apache.logging.log4j.ThreadContext;
 import org.apache.logging.log4j.audit.catalog.CatalogManager;
 import org.apache.logging.log4j.audit.exception.AuditException;
+import org.apache.logging.log4j.audit.util.NamingUtils;
 import org.apache.logging.log4j.catalog.api.Attribute;
 import org.apache.logging.log4j.catalog.api.Constraint;
 import org.apache.logging.log4j.catalog.api.Event;
@@ -30,7 +31,7 @@
 
 import java.util.*;
 
-import static org.apache.logging.log4j.catalog.api.constant.Constants.*;
+import static java.util.Collections.*;
 
 /**
  * This class is used to log events generated remotely.
@@ -77,28 +78,24 @@
     }
 
     public void logEvent(String eventName, Map<String, String> attributes) {
-        Event event = catalogManager.getEvent(eventName);
-        if (event == null) {
-            throw new AuditException("Unable to locate definition of audit event " + eventName);
-        }
-        logEvent(eventName, attributes, event, defaultAuditExceptionHandler);
+        logEvent(eventName, null, attributes, defaultAuditExceptionHandler);
     }
 
     public void logEvent(String eventName, String catalogId, Map<String, String> attributes) {
-        Event event = catalogManager.getEvent(eventName, catalogId);
-        if (event == null) {
-            throw new AuditException("Unable to locate definition of audit event " + eventName);
-        }
-        logEvent(eventName, attributes, event, defaultAuditExceptionHandler);
+        logEvent(eventName, catalogId, attributes, defaultAuditExceptionHandler);
     }
 
     public void logEvent(String eventName, Map<String, String> attributes, AuditExceptionHandler exceptionHandler) {
-        Event event = catalogManager.getEvent(eventName);
+        logEvent(eventName, null, attributes, exceptionHandler);
+    }
 
+    private void logEvent(String eventName, String catalogId, Map<String, String> attributes, AuditExceptionHandler exceptionHandler) {
+        String eventId = NamingUtils.lowerFirst(eventName);
+        Event event = catalogId == null ? catalogManager.getEvent(eventId) : catalogManager.getEvent(eventId, catalogId);
         if (event == null) {
-            throw new AuditException("Unable to locate definition of audit event " + eventName);
+            throw new AuditException("Unable to locate definition of audit event " + eventId);
         }
-        logEvent(eventName, attributes, event, exceptionHandler);
+        logEvent(eventId, attributes, event, exceptionHandler);
     }
 
     protected abstract void logEvent(StructuredDataMessage message);
@@ -107,10 +104,15 @@
                           AuditExceptionHandler exceptionHandler) {
         AuditMessage msg = new AuditMessage(eventName, maxLength);
 
+        if (attributes == null) {
+            attributes = emptyMap();
+        }
+
         StringBuilder missingAttributes = new StringBuilder();
         StringBuilder errors = new StringBuilder();
 
-        for (EventAttribute eventAttribute : event.getAttributes()) {
+        List<EventAttribute> eventAttributes = event.getAttributes() == null ? emptyList() : event.getAttributes();
+        for (EventAttribute eventAttribute : eventAttributes) {
             Attribute attr = catalogManager.getAttribute(eventAttribute.getName(), event.getCatalogId());
             if ((!attr.isRequestContext() && (attr.isRequired()) ||
                     (eventAttribute.isRequired() != null && eventAttribute.isRequired()))) {
diff --git a/log4j-audit/log4j-audit-api/src/main/java/org/apache/logging/log4j/audit/LogEventFactory.java b/log4j-audit/log4j-audit-api/src/main/java/org/apache/logging/log4j/audit/LogEventFactory.java
index 85f2692..2362a50 100644
--- a/log4j-audit/log4j-audit-api/src/main/java/org/apache/logging/log4j/audit/LogEventFactory.java
+++ b/log4j-audit/log4j-audit-api/src/main/java/org/apache/logging/log4j/audit/LogEventFactory.java
@@ -20,10 +20,7 @@
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
@@ -87,12 +84,12 @@
      * @return Returns an instance of the Event.
      */
     @SuppressWarnings("unchecked")
-	public static <T> T getEvent(Class<T> intrface) {
+	public static <T extends AuditEvent> T getEvent(Class<T> intrface) {
 
 		Class<?>[] interfaces = new Class<?>[] { intrface };
 
         String eventId = NamingUtils.lowerFirst(intrface.getSimpleName());
-        int msgLength = intrface.getAnnotation(MaxLength.class).value();
+        int msgLength = getMaxLength(intrface);
         AuditMessage msg = new AuditMessage(eventId, msgLength);
 		AuditEvent audit = (AuditEvent) Proxy.newProxyInstance(intrface
 				.getClassLoader(), interfaces, new AuditProxy(msg, intrface));
@@ -100,6 +97,11 @@
 		return (T) audit;
 	}
 
+    private static <T> int getMaxLength(Class<T> intrface) {
+        MaxLength maxLength = intrface.getAnnotation(MaxLength.class);
+        return maxLength == null ? DEFAULT_MAX_LENGTH : maxLength.value();
+    }
+
     /**
      *
      * This method is used to construct and AuditMessage from a set of properties and the Event interface
@@ -123,12 +125,16 @@
         validateContextConstraints(intrface, errors);
 
         String eventId = NamingUtils.lowerFirst(intrface.getSimpleName());
-        int maxLength = intrface.getAnnotation(MaxLength.class).value();
+        int maxLength = getMaxLength(intrface);
         AuditMessage msg = new AuditMessage(eventId, maxLength);
+
+        if (properties == null) {
+            properties = Collections.emptyMap();
+        }
         List<Property> props = getProperties(intrface);
         Map<String, Property> propertyMap = new HashMap<>();
 
-        for (Property property : props ) {
+        for (Property property : props) {
             propertyMap.put(property.name, property);
             if (property.isRequired && !properties.containsKey(property.name)) {
                 if (errors.length() > 0) {
@@ -382,6 +388,11 @@
     }
 
     private static void validateContextConstraint(RequestContext constraint, StringBuilder errors) {
+        if (constraint == null) {
+            // the request context is not mandatory
+            return;
+        }
+
         String value = ThreadContext.get(constraint.key());
         if (value != null) {
             validateConstraints(true, constraint.constraints(), constraint.key(), value, errors);
diff --git a/log4j-audit/log4j-audit-api/src/main/java/org/apache/logging/log4j/audit/catalog/CatalogManagerImpl.java b/log4j-audit/log4j-audit-api/src/main/java/org/apache/logging/log4j/audit/catalog/CatalogManagerImpl.java
index 538d4ca..1cd9831 100644
--- a/log4j-audit/log4j-audit-api/src/main/java/org/apache/logging/log4j/audit/catalog/CatalogManagerImpl.java
+++ b/log4j-audit/log4j-audit-api/src/main/java/org/apache/logging/log4j/audit/catalog/CatalogManagerImpl.java
@@ -36,6 +36,7 @@
 import org.apache.logging.log4j.catalog.api.CatalogReader;
 import org.apache.logging.log4j.catalog.api.EventAttribute;
 
+import static java.util.Collections.emptyList;
 import static org.apache.logging.log4j.catalog.api.constant.Constants.DEFAULT_CATALOG;
 
 /**
@@ -83,11 +84,12 @@
     public Map<String, Attribute> getAttributes(String eventName, String catalogId) {
         Event event = getEvent(eventName, catalogId);
         if (event == null) {
-            logger.warn("No event named {} counld be found in catalog {}", eventName, catalogId);
+            logger.warn("The event named {} could not be found in catalog {}", eventName, catalogId);
             return null;
         }
-        Map<String, Attribute> attributes = new HashMap<>(event.getAttributes().size());
-        for (EventAttribute eventAttribute : event.getAttributes()) {
+        List<EventAttribute> eventAttributes = event.getAttributes() == null ? emptyList() : event.getAttributes();
+        Map<String, Attribute> attributes = new HashMap<>(eventAttributes.size());
+        for (EventAttribute eventAttribute : eventAttributes) {
             Attribute attr = getAttribute(eventAttribute.getName(), event.getCatalogId());
             if (attr != null) {
                 attributes.put(attr.getName(), attr);
@@ -128,22 +130,27 @@
     }
 
     private Map<String, Map<String, CatalogInfo>> initializeData(CatalogReader catalogReader) throws Exception {
-        String catalog = catalogReader.readCatalog();
         JsonFactory factory = new JsonFactory();
         factory.enable(JsonParser.Feature.ALLOW_COMMENTS);
         ObjectMapper mapper = new ObjectMapper(factory);
+
+        String catalog = catalogReader.readCatalog();
         catalogData = mapper.readValue(catalog, CatalogData.class);
-        for (Attribute attr : catalogData.getAttributes()) {
-            if (attr.isRequestContext()) {
-                requestContextAttributes.put(attr.getName(), attr);
+
+        if (catalogData.getAttributes() != null) {
+            for (Attribute attr : catalogData.getAttributes()) {
+                if (attr.isRequestContext()) {
+                    requestContextAttributes.put(attr.getName(), attr);
+                }
+                Map<String, Attribute> attrMap = attributeMap.get(attr.getCatalogId());
+                if (attrMap == null) {
+                    attrMap = new HashMap<>();
+                    attributeMap.put(attr.getCatalogId(), attrMap);
+                }
+                attrMap.put(attr.getName(), attr);
             }
-            Map<String, Attribute> attrMap = attributeMap.get(attr.getCatalogId());
-            if (attrMap == null) {
-                attrMap = new HashMap<>();
-                attributeMap.put(attr.getCatalogId(), attrMap);
-            }
-            attrMap.put(attr.getName(), attr);
         }
+
         Map<String, Map<String, CatalogInfo>> map = new HashMap<>();
         map.put(DEFAULT_CATALOG, new HashMap<>());
         for (Event event : catalogData.getEvents()) {
diff --git a/log4j-audit/log4j-audit-api/src/main/java/org/apache/logging/log4j/audit/rest/RequestContextFilter.java b/log4j-audit/log4j-audit-api/src/main/java/org/apache/logging/log4j/audit/rest/RequestContextFilter.java
index 28f922a..f60263b 100644
--- a/log4j-audit/log4j-audit-api/src/main/java/org/apache/logging/log4j/audit/rest/RequestContextFilter.java
+++ b/log4j-audit/log4j-audit-api/src/main/java/org/apache/logging/log4j/audit/rest/RequestContextFilter.java
@@ -79,11 +79,11 @@
         if (servletRequest instanceof HttpServletRequest) {
             HttpServletRequest request = (HttpServletRequest) servletRequest;
             HttpServletResponse response = (HttpServletResponse) servletResponse;
-            logger.info("Starting request {}" + request.getRequestURI());
+            logger.trace("Starting request {}", request.getRequestURI());
             try {
-                Enumeration headers = request.getHeaderNames();
+                Enumeration<String> headers = request.getHeaderNames();
                 while (headers.hasMoreElements()) {
-                    String name = (String) headers.nextElement();
+                    String name = headers.nextElement();
                     RequestContextMapping mapping = mappings.getMappingByHeader(name);
                     logger.debug("Got Mapping:{} for Header:{}", mapping, name);
                     if (mapping != null) {
@@ -99,12 +99,17 @@
                         }
                     }
                 }
-                long start = System.nanoTime();
+                long start = 0;
+                if (logger.isTraceEnabled()) {
+                    start = System.nanoTime();
+                }
                 filterChain.doFilter(servletRequest, servletResponse);
-                long elapsed = System.nanoTime() - start;
-                StringBuilder sb = new StringBuilder("Request ").append(request.getRequestURI()).append(" completed in ");
-                ElapsedUtil.addElapsed(elapsed, sb);
-                logger.info(sb.toString());
+                if (logger.isTraceEnabled()) {
+                    long elapsed = System.nanoTime() - start;
+                    StringBuilder sb = new StringBuilder("Request ").append(request.getRequestURI()).append(" completed in ");
+                    ElapsedUtil.addElapsed(elapsed, sb);
+                    logger.trace(sb.toString());
+                }
             } catch (Throwable e) {
                 logger.error("Application cascaded error", e);
                 response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
diff --git a/log4j-audit/log4j-audit-api/src/main/java/org/apache/logging/log4j/audit/rest/RequestContextHandlerInterceptor.java b/log4j-audit/log4j-audit-api/src/main/java/org/apache/logging/log4j/audit/rest/RequestContextHandlerInterceptor.java
index 173461a..114dbf5 100644
--- a/log4j-audit/log4j-audit-api/src/main/java/org/apache/logging/log4j/audit/rest/RequestContextHandlerInterceptor.java
+++ b/log4j-audit/log4j-audit-api/src/main/java/org/apache/logging/log4j/audit/rest/RequestContextHandlerInterceptor.java
@@ -44,10 +44,10 @@
 
     @Override
     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
-        logger.info("Starting request {}" + request.getRequestURI());
-        Enumeration headers = request.getHeaderNames();
+        logger.trace("Starting request {}", request.getRequestURI());
+        Enumeration<String> headers = request.getHeaderNames();
         while (headers.hasMoreElements()) {
-            String name = (String) headers.nextElement();
+            String name = headers.nextElement();
             RequestContextMapping mapping = mappings.getMappingByHeader(name);
             logger.debug("Got Mapping:{} for Header:{}", mapping, name);
             if (mapping != null) {
@@ -63,17 +63,21 @@
                 }
             }
         }
-        startTime.set(System.nanoTime());
+        if (logger.isTraceEnabled()) {
+            startTime.set(System.nanoTime());
+        }
         return true;
     }
 
     @Override
     public void postHandle(HttpServletRequest request, HttpServletResponse response, Object o, ModelAndView modelAndView) throws Exception {
-        long elapsed = System.nanoTime() - startTime.get();
-        StringBuilder sb = new StringBuilder("Request ").append(request.getRequestURI()).append(" completed in ");
-        ElapsedUtil.addElapsed(elapsed, sb);
-        logger.info(sb.toString());
-        startTime.remove();
+        if (logger.isTraceEnabled()) {
+            long elapsed = System.nanoTime() - startTime.get();
+            StringBuilder sb = new StringBuilder("Request ").append(request.getRequestURI()).append(" completed in ");
+            ElapsedUtil.addElapsed(elapsed, sb);
+            logger.trace(sb.toString());
+            startTime.remove();
+        }
     }
 
     @Override
diff --git a/log4j-audit/log4j-audit-api/src/test/java/org/apache/logging/log4j/audit/AuditLoggerTest.java b/log4j-audit/log4j-audit-api/src/test/java/org/apache/logging/log4j/audit/AuditLoggerTest.java
index eab5dd1..5d925d5 100644
--- a/log4j-audit/log4j-audit-api/src/test/java/org/apache/logging/log4j/audit/AuditLoggerTest.java
+++ b/log4j-audit/log4j-audit-api/src/test/java/org/apache/logging/log4j/audit/AuditLoggerTest.java
@@ -23,6 +23,8 @@
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.audit.catalog.StringCatalogReader;
 import org.apache.logging.log4j.audit.exception.AuditException;
+import org.apache.logging.log4j.catalog.api.CatalogReader;
+import org.apache.logging.log4j.catalog.api.dao.ClassPathCatalogReader;
 import org.apache.logging.log4j.core.Appender;
 import org.apache.logging.log4j.core.LoggerContext;
 import org.apache.logging.log4j.core.config.Configuration;
@@ -31,6 +33,7 @@
 import org.junit.BeforeClass;
 import org.junit.Test;
 
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -44,18 +47,15 @@
  */
 public class AuditLoggerTest {
 
-    private static AbstractEventLogger auditLogger;
-
-    private static CatalogManager catalogManager;
-
+    private static CatalogReader catalogReader;
     private static LoggerContext ctx;
     private static ListAppender app;
 
+    private AbstractEventLogger auditLogger;
+
     @BeforeClass
     public static void setupClass() throws Exception {
-        catalogManager = new CatalogManagerImpl(new StringCatalogReader());
-        auditLogger = new AuditLogger();
-        auditLogger.setCatalogManager(catalogManager);
+        catalogReader = new StringCatalogReader();
         ctx = (LoggerContext) LogManager.getContext(false);
         Configuration config = ctx.getConfiguration();
         for (Map.Entry<String, Appender> entry : config.getAppenders().entrySet()) {
@@ -67,6 +67,13 @@
         assertNotNull("No Appender", app);
     }
 
+    private AbstractEventLogger buildAuditLogger(CatalogReader catalogReader) throws Exception {
+        CatalogManager catalogManager = new CatalogManagerImpl(catalogReader);
+        AuditLogger auditLogger = new AuditLogger();
+        auditLogger.setCatalogManager(catalogManager);
+        return auditLogger;
+    }
+
     @Before
     public void before() {
         app.clear();
@@ -74,7 +81,9 @@
     }
 
     @Test
-    public void testAuditLogger() {
+    public void testAuditLogger() throws Exception {
+        auditLogger = buildAuditLogger(catalogReader);
+
         ThreadContext.put("accountNumber", "12345");
         ThreadContext.put("companyId", "12345");
         ThreadContext.put("userId", "JohnDoe");
@@ -88,7 +97,7 @@
         properties.put("fromAccount", "111111");
         properties.put("amount", "111.55");
         try {
-            auditLogger.logEvent("transfer", properties);
+            auditLogger.logEvent("Transfer", properties);
         } catch (Exception ex) {
             ex.printStackTrace();
             fail();
@@ -97,13 +106,16 @@
         assertNotNull("No messages", msgs);
         assertTrue("No messages", msgs.size() == 1);
         String msg = msgs.get(0);
+        assertTrue("Normalized event name", msg.contains("transfer@"));
         assertTrue("No companyId", msg.contains("companyId=\"12345\""));
         assertTrue("No ipAddress", msg.contains("ipAddress=\"127.0.0.1\""));
         assertTrue("No toAccount", msg.contains("toAccount=\"123456\""));
     }
 
     @Test(expected = AuditException.class)
-    public void testMissingRequestContextAttribute() {
+    public void testMissingRequestContextAttribute() throws Exception {
+        auditLogger = buildAuditLogger(catalogReader);
+
         Map<String, String> properties = new HashMap<String, String>();
         properties.put("toAccount", "123456");
         properties.put("fromAccount", "111111");
@@ -112,7 +124,9 @@
     }
 
     @Test(expected = AuditException.class)
-    public void testMissingEventAttribute() {
+    public void testMissingEventAttribute() throws Exception {
+        auditLogger = buildAuditLogger(catalogReader);
+
         ThreadContext.put("companyId", "12345");
         ThreadContext.put("ipAddress", "127.0.0.1");
         ThreadContext.put("environment", "dev");
@@ -122,6 +136,13 @@
         Map<String, String> properties = new HashMap<String, String>();
         properties.put("toAccount", "123456");
         properties.put("amount", "111.55");
-        auditLogger.logEvent("transfer", properties);
+        auditLogger.logEvent("Transfer", properties);
+    }
+
+    @Test
+    public void testAuditLoggerWithBasicCatalog() throws Exception {
+        auditLogger = buildAuditLogger(new ClassPathCatalogReader(Collections.singletonMap("catalogFile", "basicCatalog.json")));
+
+        auditLogger.logEvent("login", null);
     }
 }
diff --git a/log4j-audit/log4j-audit-api/src/test/java/org/apache/logging/log4j/audit/BaseEventTest.java b/log4j-audit/log4j-audit-api/src/test/java/org/apache/logging/log4j/audit/BaseEventTest.java
new file mode 100644
index 0000000..01aca3e
--- /dev/null
+++ b/log4j-audit/log4j-audit-api/src/test/java/org/apache/logging/log4j/audit/BaseEventTest.java
@@ -0,0 +1,39 @@
+package org.apache.logging.log4j.audit;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.test.appender.ListAppender;
+import org.junit.Before;
+import org.junit.BeforeClass;
+
+import java.util.Map;
+
+import static org.junit.Assert.assertNotNull;
+
+public class BaseEventTest {
+
+    protected static LoggerContext ctx;
+    protected static ListAppender app;
+
+    @BeforeClass
+    public static void setupClass() throws Exception {
+        ctx = (LoggerContext) LogManager.getContext(false);
+        Configuration config = ctx.getConfiguration();
+        for (Map.Entry<String, Appender> entry : config.getAppenders().entrySet()) {
+            if (entry.getKey().equals("List")) {
+                app = (ListAppender) entry.getValue();
+                break;
+            }
+        }
+        assertNotNull("No Appender", app);
+    }
+
+    @Before
+    public void before() {
+        app.clear();
+        ThreadContext.clearMap();
+    }
+}
\ No newline at end of file
diff --git a/log4j-audit/log4j-audit-api/src/test/java/org/apache/logging/log4j/audit/LoginTest.java b/log4j-audit/log4j-audit-api/src/test/java/org/apache/logging/log4j/audit/LoginTest.java
new file mode 100644
index 0000000..190ccd6
--- /dev/null
+++ b/log4j-audit/log4j-audit-api/src/test/java/org/apache/logging/log4j/audit/LoginTest.java
@@ -0,0 +1,34 @@
+package org.apache.logging.log4j.audit;
+
+import org.apache.logging.log4j.audit.event.Login;
+import org.junit.Test;
+
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class LoginTest extends BaseEventTest {
+    @Test
+    public void testAuditClass() {
+        Login event = LogEventFactory.getEvent(Login.class);
+
+        event.logEvent();
+
+        event.setCompletionStatus("Success");
+
+        event.logEvent();
+
+        List<String> msgs = app.getMessages();
+        assertNotNull("No messages", msgs);
+        assertTrue("No messages", msgs.size() == 2);
+    }
+
+    @Test
+    public void testAuditLog() {
+        LogEventFactory.logEvent(Login.class, null);
+
+        LogEventFactory.logEvent(Login.class, Collections.emptyMap());
+    }
+}
\ No newline at end of file
diff --git a/log4j-audit/log4j-audit-api/src/test/java/org/apache/logging/log4j/audit/TransferTest.java b/log4j-audit/log4j-audit-api/src/test/java/org/apache/logging/log4j/audit/TransferTest.java
index ebc46ba..7b1cd47 100644
--- a/log4j-audit/log4j-audit-api/src/test/java/org/apache/logging/log4j/audit/TransferTest.java
+++ b/log4j-audit/log4j-audit-api/src/test/java/org/apache/logging/log4j/audit/TransferTest.java
@@ -40,29 +40,7 @@
 /**
  *
  */
-public class TransferTest {
-
-    private static LoggerContext ctx;
-    private static ListAppender app;
-
-    @BeforeClass
-    public static void setupClass() throws Exception {
-        ctx = (LoggerContext) LogManager.getContext(false);
-        Configuration config = ctx.getConfiguration();
-        for (Map.Entry<String, Appender> entry : config.getAppenders().entrySet()) {
-            if (entry.getKey().equals("List")) {
-                app = (ListAppender) entry.getValue();
-                break;
-            }
-        }
-        assertNotNull("No Appender", app);
-    }
-
-    @Before
-    public void before() {
-        app.clear();
-        ThreadContext.clearMap();
-    }
+public class TransferTest extends BaseEventTest {
 
     @Test(expected = ConstraintValidationException.class)
     public void testValidationFailureForMissingRequestContextAttribute() {
diff --git a/log4j-audit/log4j-audit-api/src/test/java/org/apache/logging/log4j/audit/event/Login.java b/log4j-audit/log4j-audit-api/src/test/java/org/apache/logging/log4j/audit/event/Login.java
new file mode 100644
index 0000000..8a641f4
--- /dev/null
+++ b/log4j-audit/log4j-audit-api/src/test/java/org/apache/logging/log4j/audit/event/Login.java
@@ -0,0 +1,6 @@
+package org.apache.logging.log4j.audit.event;
+
+import org.apache.logging.log4j.audit.AuditEvent;
+
+public interface Login extends AuditEvent {
+}
diff --git a/log4j-audit/log4j-audit-api/src/test/resources/basicCatalog.json b/log4j-audit/log4j-audit-api/src/test/resources/basicCatalog.json
new file mode 100644
index 0000000..297a36b
--- /dev/null
+++ b/log4j-audit/log4j-audit-api/src/test/resources/basicCatalog.json
@@ -0,0 +1,5 @@
+{
+  "events" : [ {
+    "name" : "login"
+  } ]
+}
\ No newline at end of file
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index bec27d3..59ad26b 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -30,6 +30,20 @@
          - "update" - Change
          - "remove" - Removed
     -->
+    <release version="1.0.1" date="YYYY-MM-DD" description="Release 1.0.1">
+      <action issue="LOG4J2-2417" dev="rgoers" type="fix" due-to="Andrei Ivanov">
+        Better handling of optional properties.
+      </action>
+      <action issue="LOG4J2-2420" dev="rgoers" type="fix" due-to="Andrei Ivanov">
+        RequestContextFilter logging cleanup.
+      </action>
+      <action issue="LOG4J2-2442" dev="rgoers" type="fix" due-to="Andrei Ivanov">
+        Normalize the event names logged through AbstractEventLogger.logEvent.
+      </action>
+      <action issue="LOG4J2-2431" dev="rgoers" type="fix" due-to="Andrei Ivanov">
+        Narrow the return type of getEvent.
+      </action>
+    </release>
     <release version="1.0.0" date="2018-06-10" description="Release 1.0.0">
     </release>
   </body>