log4j-docgen: Support attributes as a union of strict type and String

This update enhances the generated XML schema by allowing each attribute to accept either its strict, expected type or a `${...}` expression. This accommodates use cases where property substitution is used, but at the same time allows IDE auto-completions.

> [!WARNING]
> This PR depends on #190 and should not be reviewed until that is merged.

Closes #136
diff --git a/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/generator/SchemaGenerator.java b/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/generator/SchemaGenerator.java
index 707a606..c947be7 100644
--- a/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/generator/SchemaGenerator.java
+++ b/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/generator/SchemaGenerator.java
@@ -32,6 +32,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.TreeSet;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import javax.inject.Named;
@@ -67,6 +68,24 @@
 
     private static final String CHARSET_NAME = "UTF-8";
 
+    private static final String PROPERTY_SUBSTITUTION_TYPE = "property-substitution";
+    private static final String BOOLEAN_TYPE = "boolean";
+    private static final String STRING_TYPE = "string";
+    private static final ScalarType BOOLEAN_SCALAR_TYPE = new ScalarType();
+
+    static {
+        BOOLEAN_SCALAR_TYPE.setClassName(BOOLEAN_TYPE);
+        final Description description = new Description();
+        description.setText(
+                "A custom boolean type that allows `true`, `false`, or a property substitution expression.");
+        BOOLEAN_SCALAR_TYPE.setDescription(description);
+        for (final Boolean value : new Boolean[] {true, false}) {
+            final ScalarValue scalarValue = new ScalarValue();
+            scalarValue.setName(value.toString());
+            BOOLEAN_SCALAR_TYPE.addValue(scalarValue);
+        }
+    }
+
     private static final Map<String, String> XML_BUILTIN_TYPES = Map.ofEntries(
             entry(BigDecimal.class.getName(), "decimal"),
             entry(BigInteger.class.getName(), "integer"),
@@ -138,6 +157,12 @@
     }
 
     private static void writeTypes(final TypeLookup lookup, final XMLStreamWriter writer) throws XMLStreamException {
+        writePropertySubstitutionType(writer);
+        // A union with member types `xsd:boolean` and `log4j:property-substitution` does not allow auto-completion
+        // in IDEs. This is why we define a `log4j:boolean` type from scratch.
+        writeScalarType(BOOLEAN_SCALAR_TYPE, writer);
+        writeUnionBuiltinTypes(writer);
+
         for (final ArtifactSourcedType sourcedType : lookup.values()) {
             final Type type = sourcedType.type;
             if (isBuiltinXmlType(type.getClassName())) {
@@ -167,12 +192,66 @@
         return XML_BUILTIN_TYPES.containsKey(className);
     }
 
+    /**
+     * A restriction of {@code string} that requires at least one property substitution expression {@code ${...}}.
+     */
+    private static void writePropertySubstitutionType(final XMLStreamWriter writer) throws XMLStreamException {
+        writer.writeStartElement(XSD_NAMESPACE, "simpleType");
+        writer.writeAttribute("name", PROPERTY_SUBSTITUTION_TYPE);
+
+        writeDocumentation("A string with a property substitution expression.", writer);
+
+        writer.writeStartElement(XSD_NAMESPACE, "restriction");
+        writer.writeAttribute("base", "string");
+
+        writer.writeEmptyElement(XSD_NAMESPACE, "pattern");
+        writer.writeAttribute("value", ".*\\$\\{.*\\}.*");
+
+        writer.writeEndElement();
+        writer.writeEndElement();
+    }
+
+    /**
+     * Define types that are the union of a builtin type and {@value PROPERTY_SUBSTITUTION_TYPE}.
+     * <p>
+     *     IDEs don't propose auto-completion for these types.
+     * </p>
+     */
+    private static void writeUnionBuiltinTypes(final XMLStreamWriter writer) throws XMLStreamException {
+        final Collection<String> types = new TreeSet<>(XML_BUILTIN_TYPES.values());
+        // `xsd:string` is a superset of PROPERTY_SUBSTITUTION_TYPE, so no union is needed.
+        types.remove(STRING_TYPE);
+        // The union of `xsd:boolean` with PROPERTY_SUBSTITUTION_TYPE does not show auto-completion in IDEs.
+        // `log4j:boolean` will be generated from an _ad-hoc_ ScalarType definition in `base-log4j-types.xml`.
+        types.remove(BOOLEAN_TYPE);
+        for (final String type : types) {
+            writeUnionBuiltinType(type, writer);
+        }
+    }
+
+    private static void writeUnionBuiltinType(final String type, final XMLStreamWriter writer)
+            throws XMLStreamException {
+        writer.writeStartElement(XSD_NAMESPACE, "simpleType");
+        writer.writeAttribute("name", type);
+
+        writeDocumentation("Union of `xsd:" + type + "` and ` " + PROPERTY_SUBSTITUTION_TYPE + "`.", writer);
+
+        writer.writeEmptyElement(XSD_NAMESPACE, "union");
+        writer.writeAttribute("memberTypes", type + " log4j:" + PROPERTY_SUBSTITUTION_TYPE);
+
+        writer.writeEndElement();
+    }
+
     private static void writeScalarType(final ScalarType type, final XMLStreamWriter writer) throws XMLStreamException {
         writer.writeStartElement(XSD_NAMESPACE, "simpleType");
         writer.writeAttribute("name", type.getClassName());
 
         writeDocumentation(type.getDescription(), writer);
 
+        writer.writeStartElement(XSD_NAMESPACE, "union");
+        writer.writeAttribute("memberTypes", "log4j:" + PROPERTY_SUBSTITUTION_TYPE);
+        writer.writeStartElement(XSD_NAMESPACE, "simpleType");
+
         writer.writeStartElement(XSD_NAMESPACE, "restriction");
         writer.writeAttribute("base", "string");
 
@@ -182,6 +261,8 @@
 
         writer.writeEndElement();
         writer.writeEndElement();
+        writer.writeEndElement();
+        writer.writeEndElement();
     }
 
     private static void writePluginType(
@@ -240,22 +321,30 @@
     private static void writeDocumentation(@Nullable final Description description, final XMLStreamWriter writer)
             throws XMLStreamException {
         if (description != null) {
-            writer.writeStartElement(XSD_NAMESPACE, "annotation");
-            writer.writeStartElement(XSD_NAMESPACE, "documentation");
-            writer.writeCharacters(description.getText());
-            writer.writeEndElement();
-            writer.writeEndElement();
+            writeDocumentation(description.getText(), writer);
         }
     }
 
+    private static void writeDocumentation(final String text, final XMLStreamWriter writer) throws XMLStreamException {
+        writer.writeStartElement(XSD_NAMESPACE, "annotation");
+        writer.writeStartElement(XSD_NAMESPACE, "documentation");
+        writer.writeCharacters(text);
+        writer.writeEndElement();
+        writer.writeEndElement();
+    }
+
     private static void writeScalarValue(final ScalarValue value, final XMLStreamWriter writer)
             throws XMLStreamException {
-        writer.writeStartElement(XSD_NAMESPACE, "enumeration");
-        writer.writeAttribute("value", value.getName());
-
-        writeDocumentation(value.getDescription(), writer);
-
-        writer.writeEndElement();
+        final Description description = value.getDescription();
+        if (description != null) {
+            writer.writeStartElement(XSD_NAMESPACE, "enumeration");
+            writer.writeAttribute("value", value.getName());
+            writeDocumentation(value.getDescription(), writer);
+            writer.writeEndElement();
+        } else {
+            writer.writeEmptyElement(XSD_NAMESPACE, "enumeration");
+            writer.writeAttribute("value", value.getName());
+        }
     }
 
     private static void writePluginElement(
@@ -303,25 +392,28 @@
     private static void writePluginAttribute(
             final TypeLookup lookup, final PluginAttribute attribute, final XMLStreamWriter writer)
             throws XMLStreamException {
-        @Nullable final String xmlType = getXmlType(lookup, attribute.getType());
-        if (xmlType == null) {
-            return;
-        }
-        writer.writeStartElement(XSD_NAMESPACE, "attribute");
-        writer.writeAttribute("name", attribute.getName());
-        writer.writeAttribute("type", xmlType);
+        final String xmlType = getXmlType(lookup, attribute.getType());
         final Description description = attribute.getDescription();
         if (description != null) {
-            writeDocumentation(description, writer);
+            writer.writeStartElement(XSD_NAMESPACE, "attribute");
+        } else {
+            writer.writeEmptyElement(XSD_NAMESPACE, "attribute");
         }
-        writer.writeEndElement();
+        writer.writeAttribute("name", attribute.getName());
+        // If the type is unknown, use `string`
+        writer.writeAttribute("type", xmlType != null ? xmlType : "string");
+        if (description != null) {
+            writeDocumentation(description, writer);
+            writer.writeEndElement();
+        }
     }
 
     @Nullable
     private static String getXmlType(final TypeLookup lookup, final String className) {
         final String builtinType = XML_BUILTIN_TYPES.get(className);
         if (builtinType != null) {
-            return builtinType;
+            // Use the union types for all built-in types, except `string`.
+            return STRING_TYPE.equals(builtinType) ? STRING_TYPE : LOG4J_PREFIX + ":" + builtinType;
         }
         final ArtifactSourcedType type = lookup.get(className);
         return type != null ? LOG4J_PREFIX + ":" + className : null;
diff --git a/log4j-docgen/src/test/resources/SchemaGeneratorTest/expected-plugins.xsd b/log4j-docgen/src/test/resources/SchemaGeneratorTest/expected-plugins.xsd
index 4cd4cb3..ec9f788 100644
--- a/log4j-docgen/src/test/resources/SchemaGeneratorTest/expected-plugins.xsd
+++ b/log4j-docgen/src/test/resources/SchemaGeneratorTest/expected-plugins.xsd
@@ -22,54 +22,137 @@
 <schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:log4j="https://logging.apache.org/xml/ns"
         elementFormDefault="qualified" targetNamespace="https://logging.apache.org/xml/ns" version="1.2.3">
   <element type="log4j:org.apache.logging.log4j.core.config.Configuration" name="Configuration"/>
+  <simpleType name="property-substitution">
+    <annotation>
+      <documentation>A string with a property substitution expression.</documentation>
+    </annotation>
+    <restriction base="string">
+      <pattern value=".*\$\{.*\}.*"/>
+    </restriction>
+  </simpleType>
+  <simpleType name="boolean">
+    <annotation>
+      <documentation>A custom boolean type that allows `true`, `false`, or a property substitution expression.
+      </documentation>
+    </annotation>
+    <union memberTypes="log4j:property-substitution">
+      <simpleType>
+        <restriction base="string">
+          <enumeration value="true"/>
+          <enumeration value="false"/>
+        </restriction>
+      </simpleType>
+    </union>
+  </simpleType>
+  <simpleType name="anyURI">
+    <annotation>
+      <documentation>Union of `xsd:anyURI` and ` property-substitution`.</documentation>
+    </annotation>
+    <union memberTypes="anyURI log4j:property-substitution"/>
+  </simpleType>
+  <simpleType name="byte">
+    <annotation>
+      <documentation>Union of `xsd:byte` and ` property-substitution`.</documentation>
+    </annotation>
+    <union memberTypes="byte log4j:property-substitution"/>
+  </simpleType>
+  <simpleType name="decimal">
+    <annotation>
+      <documentation>Union of `xsd:decimal` and ` property-substitution`.</documentation>
+    </annotation>
+    <union memberTypes="decimal log4j:property-substitution"/>
+  </simpleType>
+  <simpleType name="double">
+    <annotation>
+      <documentation>Union of `xsd:double` and ` property-substitution`.</documentation>
+    </annotation>
+    <union memberTypes="double log4j:property-substitution"/>
+  </simpleType>
+  <simpleType name="float">
+    <annotation>
+      <documentation>Union of `xsd:float` and ` property-substitution`.</documentation>
+    </annotation>
+    <union memberTypes="float log4j:property-substitution"/>
+  </simpleType>
+  <simpleType name="int">
+    <annotation>
+      <documentation>Union of `xsd:int` and ` property-substitution`.</documentation>
+    </annotation>
+    <union memberTypes="int log4j:property-substitution"/>
+  </simpleType>
+  <simpleType name="integer">
+    <annotation>
+      <documentation>Union of `xsd:integer` and ` property-substitution`.</documentation>
+    </annotation>
+    <union memberTypes="integer log4j:property-substitution"/>
+  </simpleType>
+  <simpleType name="long">
+    <annotation>
+      <documentation>Union of `xsd:long` and ` property-substitution`.</documentation>
+    </annotation>
+    <union memberTypes="long log4j:property-substitution"/>
+  </simpleType>
+  <simpleType name="short">
+    <annotation>
+      <documentation>Union of `xsd:short` and ` property-substitution`.</documentation>
+    </annotation>
+    <union memberTypes="short log4j:property-substitution"/>
+  </simpleType>
   <simpleType name="org.apache.logging.log4j.Level">
     <annotation>
       <documentation>Represents a logging level.
-NOTE: The Log4j API supports custom levels, the following list contains only the standard ones.</documentation>
+NOTE: The Log4j API supports custom levels, the following list contains only the standard ones.
+      </documentation>
     </annotation>
-    <restriction base="string">
-      <enumeration value="OFF">
-        <annotation>
-          <documentation>Special level that disables logging.
-No events should be logged at this level.</documentation>
-        </annotation>
-      </enumeration>
-      <enumeration value="FATAL">
-        <annotation>
-          <documentation>A fatal event that will prevent the application from continuing</documentation>
-        </annotation>
-      </enumeration>
-      <enumeration value="ERROR">
-        <annotation>
-          <documentation>An error in the application, possibly recoverable</documentation>
-        </annotation>
-      </enumeration>
-      <enumeration value="WARN">
-        <annotation>
-          <documentation>An event that might possible lead to an error</documentation>
-        </annotation>
-      </enumeration>
-      <enumeration value="INFO">
-        <annotation>
-          <documentation>An event for informational purposes</documentation>
-        </annotation>
-      </enumeration>
-      <enumeration value="DEBUG">
-        <annotation>
-          <documentation>A general debugging event</documentation>
-        </annotation>
-      </enumeration>
-      <enumeration value="TRACE">
-        <annotation>
-          <documentation>A fine-grained debug message, typically capturing the flow through the application</documentation>
-        </annotation>
-      </enumeration>
-      <enumeration value="ALL">
-        <annotation>
-          <documentation>Special level indicating all events should be logged</documentation>
-        </annotation>
-      </enumeration>
-    </restriction>
+    <union memberTypes="log4j:property-substitution">
+      <simpleType>
+        <restriction base="string">
+          <enumeration value="OFF">
+            <annotation>
+              <documentation>Special level that disables logging.
+No events should be logged at this level.
+              </documentation>
+            </annotation>
+          </enumeration>
+          <enumeration value="FATAL">
+            <annotation>
+              <documentation>A fatal event that will prevent the application from continuing</documentation>
+            </annotation>
+          </enumeration>
+          <enumeration value="ERROR">
+            <annotation>
+              <documentation>An error in the application, possibly recoverable</documentation>
+            </annotation>
+          </enumeration>
+          <enumeration value="WARN">
+            <annotation>
+              <documentation>An event that might possible lead to an error</documentation>
+            </annotation>
+          </enumeration>
+          <enumeration value="INFO">
+            <annotation>
+              <documentation>An event for informational purposes</documentation>
+            </annotation>
+          </enumeration>
+          <enumeration value="DEBUG">
+            <annotation>
+              <documentation>A general debugging event</documentation>
+            </annotation>
+          </enumeration>
+          <enumeration value="TRACE">
+            <annotation>
+              <documentation>A fine-grained debug message, typically capturing the flow through the application
+              </documentation>
+            </annotation>
+          </enumeration>
+          <enumeration value="ALL">
+            <annotation>
+              <documentation>Special level indicating all events should be logged</documentation>
+            </annotation>
+          </enumeration>
+        </restriction>
+      </simpleType>
+    </union>
   </simpleType>
   <group name="org.apache.logging.log4j.core.Appender">
     <annotation>
@@ -104,23 +187,28 @@
     <annotation>
       <documentation>The result that can returned from a filter method call.</documentation>
     </annotation>
-    <restriction base="string">
-      <enumeration value="ACCEPT">
-        <annotation>
-          <documentation>The event will be processed without further filtering based on the log Level.</documentation>
-        </annotation>
-      </enumeration>
-      <enumeration value="NEUTRAL">
-        <annotation>
-          <documentation>No decision could be made, further filtering should occur.</documentation>
-        </annotation>
-      </enumeration>
-      <enumeration value="DENY">
-        <annotation>
-          <documentation>The event should not be processed.</documentation>
-        </annotation>
-      </enumeration>
-    </restriction>
+    <union memberTypes="log4j:property-substitution">
+      <simpleType>
+        <restriction base="string">
+          <enumeration value="ACCEPT">
+            <annotation>
+              <documentation>The event will be processed without further filtering based on the log Level.
+              </documentation>
+            </annotation>
+          </enumeration>
+          <enumeration value="NEUTRAL">
+            <annotation>
+              <documentation>No decision could be made, further filtering should occur.</documentation>
+            </annotation>
+          </enumeration>
+          <enumeration value="DENY">
+            <annotation>
+              <documentation>The event should not be processed.</documentation>
+            </annotation>
+          </enumeration>
+        </restriction>
+      </simpleType>
+    </union>
   </simpleType>
   <group name="org.apache.logging.log4j.core.Layout">
     <annotation>
@@ -152,23 +240,23 @@
 Must be unique.</documentation>
       </annotation>
     </attribute>
-    <attribute name="ignoreExceptions" type="boolean">
+    <attribute name="ignoreExceptions" type="log4j:boolean">
       <annotation>
         <documentation>If set to `false` logging exceptions will be forwarded to the caller.</documentation>
       </annotation>
     </attribute>
-    <attribute name="bufferedIo" type="boolean">
+    <attribute name="bufferedIo" type="log4j:boolean">
       <annotation>
         <documentation>If set to `true` (default) the appender will buffer messages before sending them.
 This attribute is ignored if `immediateFlush` is set to `true`.</documentation>
       </annotation>
     </attribute>
-    <attribute name="bufferSize" type="int">
+    <attribute name="bufferSize" type="log4j:int">
       <annotation>
         <documentation>Size in bytes of the appender's buffer.</documentation>
       </annotation>
     </attribute>
-    <attribute name="immediateFlush" type="boolean">
+    <attribute name="immediateFlush" type="log4j:boolean">
       <annotation>
         <documentation>If set to `true`, the appender flushes the output stream at each message and
 buffering is disabled regardless of the value of `bufferedIo`.</documentation>
@@ -184,18 +272,22 @@
     <annotation>
       <documentation>Specifies the target of a console appender.</documentation>
     </annotation>
-    <restriction base="string">
-      <enumeration value="SYSTEM_OUT">
-        <annotation>
-          <documentation>Logs to the standard output.</documentation>
-        </annotation>
-      </enumeration>
-      <enumeration value="SYSTEM_ERR">
-        <annotation>
-          <documentation>Logs to the standard error.</documentation>
-        </annotation>
-      </enumeration>
-    </restriction>
+    <union memberTypes="log4j:property-substitution">
+      <simpleType>
+        <restriction base="string">
+          <enumeration value="SYSTEM_OUT">
+            <annotation>
+              <documentation>Logs to the standard output.</documentation>
+            </annotation>
+          </enumeration>
+          <enumeration value="SYSTEM_ERR">
+            <annotation>
+              <documentation>Logs to the standard error.</documentation>
+            </annotation>
+          </enumeration>
+        </restriction>
+      </simpleType>
+    </union>
   </simpleType>
   <complexType name="org.apache.logging.log4j.core.config.AppenderRef">
     <annotation>
@@ -283,7 +375,7 @@
         <documentation>Name of the configuration</documentation>
       </annotation>
     </attribute>
-    <attribute name="monitorInterval" type="int">
+    <attribute name="monitorInterval" type="log4j:int">
       <annotation>
         <documentation>Number of seconds between polls for configuration changes</documentation>
       </annotation>
@@ -300,7 +392,7 @@
 The shutdown hook is enabled by default, unless Log4j detects the presence of the Servlet API.</documentation>
       </annotation>
     </attribute>
-    <attribute name="shutdownTimeout" type="int">
+    <attribute name="shutdownTimeout" type="log4j:int">
       <annotation>
         <documentation>Timeout in milliseconds of the logger context shut down</documentation>
       </annotation>
@@ -310,7 +402,7 @@
         <documentation>Sets the level of the status logger</documentation>
       </annotation>
     </attribute>
-    <attribute name="strict" type="boolean">
+    <attribute name="strict" type="log4j:boolean">
       <annotation>
         <documentation>If set to `true` the configuration file will be validated using an XML schema.</documentation>
       </annotation>
@@ -325,7 +417,7 @@
         <documentation>The name of the level.</documentation>
       </annotation>
     </attribute>
-    <attribute name="intLevel" type="int">
+    <attribute name="intLevel" type="log4j:int">
       <annotation>
         <documentation>An integer determines the strength of the custom level relative to the built-in levels.</documentation>
       </annotation>
@@ -369,7 +461,7 @@
         <documentation>The level of the logger.</documentation>
       </annotation>
     </attribute>
-    <attribute name="includeLocation" type="boolean">
+    <attribute name="includeLocation" type="log4j:boolean">
       <annotation>
         <documentation>When set to `false` location information will **not** be computed.
 
@@ -407,7 +499,7 @@
         <documentation>The level of the logger.</documentation>
       </annotation>
     </attribute>
-    <attribute name="includeLocation" type="boolean">
+    <attribute name="includeLocation" type="log4j:boolean">
       <annotation>
         <documentation>When set to `false` location information will **not** be computed.
 
@@ -474,12 +566,12 @@
         <documentation>Log events less specific than this level are filtered, while events with level equal or more specific always match.</documentation>
       </annotation>
     </attribute>
-    <attribute name="rate" type="float">
+    <attribute name="rate" type="log4j:float">
       <annotation>
         <documentation>Sets the average number of events per second to allow.</documentation>
       </annotation>
     </attribute>
-    <attribute name="maxBurst" type="long">
+    <attribute name="maxBurst" type="log4j:long">
       <annotation>
         <documentation>Sets the maximum number of events that can occur before events are filtered for exceeding the average rate.</documentation>
       </annotation>
@@ -505,24 +597,24 @@
     <annotation>
       <documentation>Dummy plugin to test all types of builtin XML attributes.</documentation>
     </annotation>
-    <attribute name="BigInteger" type="integer"/>
-    <attribute name="BigDecimal" type="decimal"/>
-    <attribute name="boolean" type="boolean"/>
-    <attribute name="Boolean" type="boolean"/>
-    <attribute name="byte" type="byte"/>
-    <attribute name="Byte" type="byte"/>
-    <attribute name="double" type="double"/>
-    <attribute name="Double" type="double"/>
-    <attribute name="float" type="float"/>
-    <attribute name="Float" type="float"/>
-    <attribute name="int" type="int"/>
-    <attribute name="Integer" type="int"/>
-    <attribute name="long" type="long"/>
-    <attribute name="Long" type="long"/>
-    <attribute name="short" type="short"/>
-    <attribute name="Short" type="short"/>
+    <attribute name="BigInteger" type="log4j:integer"/>
+    <attribute name="BigDecimal" type="log4j:decimal"/>
+    <attribute name="boolean" type="log4j:boolean"/>
+    <attribute name="Boolean" type="log4j:boolean"/>
+    <attribute name="byte" type="log4j:byte"/>
+    <attribute name="Byte" type="log4j:byte"/>
+    <attribute name="double" type="log4j:double"/>
+    <attribute name="Double" type="log4j:double"/>
+    <attribute name="float" type="log4j:float"/>
+    <attribute name="Float" type="log4j:float"/>
+    <attribute name="int" type="log4j:int"/>
+    <attribute name="Integer" type="log4j:int"/>
+    <attribute name="long" type="log4j:long"/>
+    <attribute name="Long" type="log4j:long"/>
+    <attribute name="short" type="log4j:short"/>
+    <attribute name="Short" type="log4j:short"/>
     <attribute name="String" type="string"/>
-    <attribute name="URI" type="anyURI"/>
-    <attribute name="URL" type="anyURI"/>
+    <attribute name="URI" type="log4j:anyURI"/>
+    <attribute name="URL" type="log4j:anyURI"/>
   </complexType>
 </schema>
\ No newline at end of file
diff --git a/src/changelog/.0.x.x/136_union-types.xml b/src/changelog/.0.x.x/136_union-types.xml
new file mode 100644
index 0000000..a9da4ff
--- /dev/null
+++ b/src/changelog/.0.x.x/136_union-types.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<entry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns="https://logging.apache.org/xml/ns"
+       xsi:schemaLocation="https://logging.apache.org/xml/ns https://logging.apache.org/xml/ns/log4j-changelog-0.xsd"
+       type="added">
+  <issue id="136" link="https://github.com/apache/logging-log4j-tools/issues/136"/>
+  <description format="asciidoc">Add support for property substitution expressions in XML attributes.</description>
+</entry>