diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/ComponentUtils.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/ComponentUtils.java index 6940648..ade2f92 100644 --- a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/ComponentUtils.java +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/ComponentUtils.java @@ -33,17 +33,24 @@ public static ConfigurationNodeBuilder newNodeBuilder() { return new ConfigurationNodeBuilder(); } - public static ConfigurationNode createThresholdFilter(String level) { + public static ConfigurationNode newAppenderRef(String ref) { + return newNodeBuilder() + .setPluginName("AppenderRef") + .addAttribute("ref", ref) + .get(); + } + + public static ConfigurationNode newThresholdFilter(String level) { return newNodeBuilder() .setPluginName("ThresholdFilter") .addAttribute("level", level) - .build(); + .get(); } - public static ConfigurationNode createCompositeFilter(Iterable filters) { + public static ConfigurationNode newCompositeFilter(Iterable filters) { ConfigurationNodeBuilder builder = newNodeBuilder().setPluginName("Filters"); filters.forEach(builder::addChild); - return builder.build(); + return builder.get(); } private ComponentUtils() {} @@ -73,22 +80,23 @@ public ConfigurationNodeBuilder addAttribute(String key, boolean value) { return this; } + public ConfigurationNodeBuilder addAttribute(String key, int value) { + attributes.put(key, String.valueOf(value)); + return this; + } + public ConfigurationNodeBuilder addChild(ConfigurationNode child) { children.add(child); return this; } - public ConfigurationNode build() { + @Override + public ConfigurationNode get() { if (pluginName == null) { throw new ConfigurationConverterException("No plugin name specified"); } return new ConfigurationNodeImpl(pluginName, attributes, children); } - - @Override - public ConfigurationNode get() { - return build(); - } } private static final class ConfigurationNodeImpl implements ConfigurationNode { diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/PropertiesUtils.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/PropertiesUtils.java new file mode 100644 index 0000000..d2e3fdf --- /dev/null +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/PropertiesUtils.java @@ -0,0 +1,98 @@ +/* + * 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.converter.config.internal; + +import java.util.Map; +import java.util.Properties; +import java.util.stream.Stream; +import org.apache.logging.converter.config.ConfigurationConverterException; +import org.apache.logging.converter.config.spi.v1.PropertiesSubset; +import org.jspecify.annotations.Nullable; + +public final class PropertiesUtils { + + public static @Nullable String getAndRemove(Properties properties, String key) { + return (String) properties.remove(key); + } + + public static String getLastComponent(String name) { + int idx = name.lastIndexOf('.'); + return idx == -1 ? name : name.substring(idx + 1); + } + + public static Properties extractSubset(Properties properties, String prefix) { + Properties subset = org.apache.logging.log4j.util.PropertiesUtil.extractSubset(properties, prefix); + String value = getAndRemove(properties, prefix); + if (value != null) { + subset.setProperty("", value); + } + return subset; + } + + public static @Nullable String extractProperty(PropertiesSubset subset, String key) { + return (String) subset.getProperties().remove(key); + } + + public static PropertiesSubset extractSubset(PropertiesSubset parentSubset, String childPrefix) { + Properties parentProperties = parentSubset.getProperties(); + Properties properties = + org.apache.logging.log4j.util.PropertiesUtil.extractSubset(parentProperties, childPrefix); + String value = getAndRemove(parentProperties, childPrefix); + if (value != null) { + properties.setProperty("", value); + } + return PropertiesSubset.of(addPrefixes(parentSubset.getPrefix(), childPrefix), properties); + } + + public static Map partitionOnCommonPrefixes(Properties properties) { + return org.apache.logging.log4j.util.PropertiesUtil.partitionOnCommonPrefixes(properties, true); + } + + public static Stream partitionOnCommonPrefixes(PropertiesSubset parentSubset) { + String parentPrefix = parentSubset.getPrefix(); + String effectivePrefix = parentPrefix.isEmpty() ? parentPrefix : parentPrefix + "."; + return org.apache.logging.log4j.util.PropertiesUtil.partitionOnCommonPrefixes( + parentSubset.getProperties(), true) + .entrySet() + .stream() + .map(entry -> PropertiesSubset.of(effectivePrefix + entry.getKey(), entry.getValue())); + } + + private static String addPrefixes(String left, String right) { + return left.isEmpty() ? right : right.isEmpty() ? left : left + "." + right; + } + + public static void throwIfNotEmpty(PropertiesSubset subset) { + Properties properties = subset.getProperties(); + if (!properties.isEmpty()) { + String prefix = subset.getPrefix(); + if (properties.size() == 1) { + throw new ConfigurationConverterException("Unknown configuration property '" + + addPrefixes( + prefix, + properties.stringPropertyNames().iterator().next()) + "'."); + } + StringBuilder messageBuilder = new StringBuilder("Unknown configuration properties:"); + properties.stringPropertyNames().stream() + .map(k -> addPrefixes(prefix, k)) + .forEach(k -> messageBuilder.append("\n\t").append(k)); + throw new ConfigurationConverterException(messageBuilder.toString()); + } + } + + private PropertiesUtils() {} +} diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/StringUtils.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/StringUtils.java new file mode 100644 index 0000000..c0ebeb0 --- /dev/null +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/StringUtils.java @@ -0,0 +1,73 @@ +/* + * 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.converter.config.internal; + +import java.util.regex.Pattern; +import org.apache.logging.converter.config.ConfigurationConverterException; +import org.apache.logging.log4j.util.Strings; + +public final class StringUtils { + + private static final Pattern SUBSTITUTION_PATTERN = Pattern.compile("\\$\\{([^}]+)\\}"); + + public static final String FALSE = "false"; + public static final String TRUE = "true"; + + public static String capitalize(String value) { + if (Strings.isEmpty(value) || Character.isUpperCase(value.charAt(0))) { + return value; + } + final char[] chars = value.toCharArray(); + chars[0] = Character.toUpperCase(chars[0]); + return new String(chars); + } + + public static String decapitalize(String value) { + if (Strings.isEmpty(value) || Character.isLowerCase(value.charAt(0))) { + return value; + } + final char[] chars = value.toCharArray(); + chars[0] = Character.toLowerCase(chars[0]); + return new String(chars); + } + + public static boolean parseBoolean(String value) { + return Boolean.parseBoolean(value.trim()); + } + + public static int parseInteger(String value) { + try { + return Integer.parseInt(value.trim()); + } catch (NumberFormatException e) { + throw new ConfigurationConverterException("Invalid integer value: " + value, e); + } + } + + public static long parseLong(String value) { + try { + return Long.parseLong(value.trim()); + } catch (NumberFormatException e) { + throw new ConfigurationConverterException("Invalid long value: " + value, e); + } + } + + public static String convertPropertySubstitution(String value) { + return SUBSTITUTION_PATTERN.matcher(value).replaceAll("${sys:$1}"); + } + + private StringUtils() {} +} diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/XmlUtils.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/XmlUtils.java index 6b4b85d..11653d5 100644 --- a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/XmlUtils.java +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/XmlUtils.java @@ -24,6 +24,7 @@ import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import org.apache.logging.converter.config.ConfigurationConverterException; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -60,6 +61,41 @@ public static DocumentBuilder createDocumentBuilderV2() throws IOException { return newDocumentBuilder(factory); } + /** + * Finds an XPath expression that helps to identify a node in an XML document + * + * @param node An XML node. + * @return An XPath expression. + */ + public static String getXPathExpression(Element node) { + String tagName = node.getTagName(); + // Position of the node among siblings + int position = 1; + Node sibling = node.getPreviousSibling(); + while (sibling != null) { + if (sibling instanceof Element && tagName.equals(((Element) sibling).getTagName())) { + position++; + } + sibling = sibling.getPreviousSibling(); + } + Node parent = node.getParentNode(); + String parentExpression = parent instanceof Element ? getXPathExpression((Element) parent) : ""; + return parentExpression + "/" + tagName + "[" + position + "]"; + } + + public static void throwUnknownElement(Element node) { + throw new ConfigurationConverterException("Unknown configuration element '" + getXPathExpression(node) + "'."); + } + + public static String requireNonEmpty(Element node, String attributeName) { + String value = node.getAttribute(attributeName); + if (value.isEmpty()) { + throw new ConfigurationConverterException("Missing required attribute '" + attributeName + + "' on configuration element '" + getXPathExpression(node) + "'."); + } + return value; + } + private static void disableXIncludeAware(DocumentBuilderFactory factory) throws IOException { try { factory.setXIncludeAware(false); diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/AbstractComponentParser.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/AbstractComponentParser.java new file mode 100644 index 0000000..2eb6ecc --- /dev/null +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/AbstractComponentParser.java @@ -0,0 +1,255 @@ +/* + * 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.converter.config.internal.v1; + +import static org.apache.logging.converter.config.internal.PropertiesUtils.extractProperty; +import static org.apache.logging.converter.config.internal.XmlUtils.requireNonEmpty; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Supplier; +import org.apache.logging.converter.config.ConfigurationConverterException; +import org.apache.logging.converter.config.internal.PropertiesUtils; +import org.apache.logging.converter.config.internal.StringUtils; +import org.apache.logging.converter.config.internal.XmlUtils; +import org.apache.logging.converter.config.spi.ConfigurationNode; +import org.apache.logging.converter.config.spi.v1.Log4j1ComponentParser; +import org.apache.logging.converter.config.spi.v1.Log4j1ParserContext; +import org.apache.logging.converter.config.spi.v1.PropertiesSubset; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +/** + * Base class for Log4j 1 component parsers. + */ +public abstract class AbstractComponentParser> implements Log4j1ComponentParser { + + // XML tags + public static final String PARAM_TAG = "param"; + + // XML attributes + public static final String NAME_ATTR = "name"; + public static final String REF_ATTR = "ref"; + public static final String VALUE_ATTR = "value"; + + // V1 parameter names + protected static final String THRESHOLD_PARAM = "Threshold"; + + /** + * Creates a builder for the configuration node. + * + * @param element An XML element. + */ + protected abstract T createBuilder(Element element); + + /** + * Creates a builder for the configuration node. + * + * @param properties A subset of properties. + */ + protected abstract T createBuilder(PropertiesSubset properties); + + /** + * A map between Log4j 1 parameter names and setter methods on the builder. + */ + protected abstract Map getAttributeMap(); + + @Override + public final ConfigurationNode parseXml(Element element, Log4j1ParserContext context) + throws ConfigurationConverterException { + T builder = createBuilder(element); + handleChildrenElements(element, context, getAttributeMap(), builder); + return builder.get(); + } + + @Override + public final ConfigurationNode parseProperties(PropertiesSubset properties, Log4j1ParserContext context) + throws ConfigurationConverterException { + T builder = createBuilder(properties); + handleChildrenProperties(properties, context, getAttributeMap(), builder); + return builder.get(); + } + + /** + * Handles all child elements, except {@code }. + * + * @param childElement An XML element. + * @param context The parser context. + * @param componentBuilder A builder for the configuration node. + */ + protected void handleUnknownElement(Element childElement, Log4j1ParserContext context, T componentBuilder) { + XmlUtils.throwUnknownElement(childElement); + } + + /** + * Handles subsets of properties that are not known attributes. + * + * @param properties A subset of properties to handle. + * @param context The parser context. + * @param componentBuilder A builder for the configuration node. + */ + protected void handleUnknownProperties( + PropertiesSubset properties, Log4j1ParserContext context, T componentBuilder) { + PropertiesUtils.throwIfNotEmpty(properties); + } + + /** + * Handles configuration attributes and nested elements. + * + * @param element An XML element. + * @param context A reference to the parser context. + * @param attributeMap A map from attribute names to setters of the component builder. + * @param componentBuilder A component builder. + * @throws ConfigurationConverterException If a parsing error occurs. + */ + private void handleChildrenElements( + Node element, + Log4j1ParserContext context, + Map attributeMap, + T componentBuilder) + throws ConfigurationConverterException { + // Counts children by tag name for error handling purposes + XmlUtils.forEachChild(element, (Consumer) childElement -> { + String nodeName = childElement.getNodeName(); + if (nodeName.equals(PARAM_TAG)) { + // Handle attributes + String name = childElement.getAttribute(NAME_ATTR); + MethodHandle attributeSetter = attributeMap.get(name); + if (attributeSetter == null) { + attributeSetter = attributeMap.get(StringUtils.capitalize(name)); + } + if (attributeSetter != null) { + String value = childElement.getAttribute(VALUE_ATTR); + if (value.isEmpty()) { + throw new ConfigurationConverterException("No value specified for attribute " + name); + } + invokeAttributeSetter(componentBuilder, attributeSetter, name, value); + } else { + throw new ConfigurationConverterException("Unsupported configuration attribute " + name + + " at path " + XmlUtils.getXPathExpression(childElement) + "."); + } + } else { + handleUnknownElement(childElement, context, componentBuilder); + } + }); + } + + /** + * Handles configuration attributes and nested elements. + * + * @param properties A subset of properties. + * @param context A reference to the parser context. + * @param attributeMap A map from attribute names to setters of the component builder. + * @param componentBuilder A component builder. + * @throws ConfigurationConverterException If a parsing error occurs. + */ + private void handleChildrenProperties( + PropertiesSubset properties, + Log4j1ParserContext context, + Map attributeMap, + T componentBuilder) { + // Handle attributes + attributeMap.forEach((attributeName, attributeSetter) -> { + String value = extractProperty(properties, attributeName); + if (value == null) { + value = extractProperty(properties, StringUtils.decapitalize(attributeName)); + } + if (value != null) { + invokeAttributeSetter(componentBuilder, attributeSetter, attributeName, value); + } + }); + // Handle nested components + PropertiesUtils.partitionOnCommonPrefixes(properties) + .forEach(childProperties -> handleUnknownProperties(childProperties, context, componentBuilder)); + } + + private static void invokeAttributeSetter( + Supplier builder, MethodHandle setter, String name, String value) { + try { + setter.bindTo(builder).invokeExact(value); + } catch (Throwable e) { + throw new ConfigurationConverterException("Failed to set attribute " + name, e); + } + } + + protected static AttributeMapBuilder attributeMapBuilder( + Class> componentBuilderClass) { + return new AttributeMapBuilder(componentBuilderClass); + } + + protected static ConfigurationNode parseConfigurationElement(Log4j1ParserContext context, Element element) { + return context.getParserForClass(requireNonEmpty(element, "class")).parseXml(element, context); + } + + protected static ConfigurationNode parseConfigurationElement(Log4j1ParserContext context, PropertiesSubset subset) { + String className = extractProperty(subset, ""); + if (className == null) { + throw new ConfigurationConverterException("The required property '" + subset.getPrefix() + "' is missing."); + } + return context.getParserForClass(className).parseProperties(subset, context); + } + + protected static final class AttributeMapBuilder implements Supplier> { + + private static final MethodType ATTRIBUTE_SETTER = MethodType.methodType(void.class, String.class); + private static final MethodHandles.Lookup lookup = MethodHandles.publicLookup(); + private final Map map; + private final Class> componentBuilderClass; + + private AttributeMapBuilder(Class> componentBuilderClass) { + this.map = new HashMap<>(); + this.componentBuilderClass = componentBuilderClass; + } + + public AttributeMapBuilder add(String name) { + String setterName = getSetterName(name); + try { + map.put(name, lookup.findVirtual(componentBuilderClass, setterName, ATTRIBUTE_SETTER)); + } catch (NoSuchMethodException | IllegalAccessException e) { + throw new ExceptionInInitializerError(e); + } + return this; + } + + public AttributeMapBuilder addAll(Map attributes) { + map.putAll(attributes); + return this; + } + + private static String getSetterName(CharSequence value) { + StringBuilder builder = new StringBuilder("set"); + int len = value.length(); + if (len > 0) { + builder.append(Character.toUpperCase(value.charAt(0))); + } + if (len > 1) { + builder.append(value, 1, len); + } + return builder.toString(); + } + + @Override + public Map get() { + return Collections.unmodifiableMap(map); + } + } +} diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/AbstractV1ConfigurationParser.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/AbstractV1ConfigurationParser.java new file mode 100644 index 0000000..9121da6 --- /dev/null +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/AbstractV1ConfigurationParser.java @@ -0,0 +1,47 @@ +/* + * 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.converter.config.internal.v1; + +import aQute.bnd.annotation.Cardinality; +import aQute.bnd.annotation.spi.ServiceConsumer; +import java.util.HashMap; +import java.util.Map; +import java.util.ServiceLoader; +import org.apache.logging.converter.config.ConfigurationConverterException; +import org.apache.logging.converter.config.spi.ConfigurationParser; +import org.apache.logging.converter.config.spi.v1.Log4j1ComponentParser; +import org.apache.logging.converter.config.spi.v1.Log4j1ParserContext; + +@ServiceConsumer(value = Log4j1ComponentParser.class, cardinality = Cardinality.MULTIPLE) +public abstract class AbstractV1ConfigurationParser implements ConfigurationParser, Log4j1ParserContext { + + private static final Map componentParsers = new HashMap<>(); + + protected AbstractV1ConfigurationParser() { + ServiceLoader.load(Log4j1ComponentParser.class) + .forEach(parser -> componentParsers.put(parser.getClassName(), parser)); + } + + @Override + public Log4j1ComponentParser getParserForClass(String className) { + Log4j1ComponentParser parser = componentParsers.get(className); + if (parser == null) { + throw new ConfigurationConverterException("Unsupported Log4j 1 component class: " + className); + } + return parser; + } +} diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/LoggerConfig.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/LoggerConfig.java new file mode 100644 index 0000000..d20dbe5 --- /dev/null +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/LoggerConfig.java @@ -0,0 +1,94 @@ +/* + * 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.converter.config.internal.v1; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import org.apache.logging.converter.config.internal.ComponentUtils; +import org.apache.logging.converter.config.spi.ConfigurationNode; +import org.jspecify.annotations.Nullable; + +class LoggerConfig { + + private static final String INHERITED = "INHERITED"; + private static final String NULL = "NULL"; + + private final String name; + private @Nullable String level; + private final Collection appenderRefs = new ArrayList<>(); + private @Nullable Boolean additivity; + + LoggerConfig(String name) { + this.name = name; + } + + void setLevel(String level) { + this.level = INHERITED.equalsIgnoreCase(level) || NULL.equalsIgnoreCase(level) ? null : level; + } + + void addAppenderRef(String appenderRef) { + appenderRefs.add(appenderRef); + } + + void setLevelAndRefs(String levelAndRefs) { + String[] values = + Arrays.stream(levelAndRefs.split(",", -1)).map(String::trim).toArray(String[]::new); + if (values.length > 0) { + setLevel(values[0]); + } + appenderRefs.clear(); + for (int i = 1; i < values.length; i++) { + addAppenderRef(values[i]); + } + } + + void setAdditivity(String additivity) { + this.additivity = Boolean.parseBoolean(additivity); + } + + ConfigurationNode buildLogger() { + ComponentUtils.ConfigurationNodeBuilder builder = ComponentUtils.newNodeBuilder() + .setPluginName("Logger") + .addAttribute("name", name) + .addAttribute("level", level); + if (additivity != null) { + builder.addAttribute("additivity", additivity); + } + appenderRefs.forEach(ref -> builder.addChild(ComponentUtils.newAppenderRef(ref))); + return builder.get(); + } + + ConfigurationNode buildRoot() { + ComponentUtils.ConfigurationNodeBuilder builder = + ComponentUtils.newNodeBuilder().setPluginName("Root"); + fillRemainingParameters(builder); + return builder.get(); + } + + private void fillRemainingParameters(ComponentUtils.ConfigurationNodeBuilder builder) { + builder.addAttribute("level", level); + if (additivity != null) { + builder.addAttribute("additivity", additivity); + } + appenderRefs.forEach(ref -> { + ComponentUtils.ConfigurationNodeBuilder builder1 = + ComponentUtils.newNodeBuilder().setPluginName("AppenderRef").addAttribute("ref", ref); + builder.addChild(builder1.get()); + }); + } +} diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/PropertiesV1ConfigurationParser.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/PropertiesV1ConfigurationParser.java new file mode 100644 index 0000000..1c174c9 --- /dev/null +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/PropertiesV1ConfigurationParser.java @@ -0,0 +1,165 @@ +/* + * 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.converter.config.internal.v1; + +import static org.apache.logging.converter.config.internal.PropertiesUtils.extractProperty; +import static org.apache.logging.converter.config.internal.PropertiesUtils.extractSubset; +import static org.apache.logging.converter.config.internal.PropertiesUtils.partitionOnCommonPrefixes; + +import aQute.bnd.annotation.Resolution; +import aQute.bnd.annotation.spi.ServiceProvider; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import org.apache.logging.converter.config.ConfigurationConverterException; +import org.apache.logging.converter.config.internal.ComponentUtils; +import org.apache.logging.converter.config.internal.ComponentUtils.ConfigurationNodeBuilder; +import org.apache.logging.converter.config.internal.PropertiesUtils; +import org.apache.logging.converter.config.spi.ConfigurationNode; +import org.apache.logging.converter.config.spi.ConfigurationParser; +import org.apache.logging.converter.config.spi.v1.PropertiesSubset; + +@ServiceProvider(value = ConfigurationParser.class, resolution = Resolution.MANDATORY) +public class PropertiesV1ConfigurationParser extends AbstractV1ConfigurationParser { + + private static final String ADDITIVITY_PREFIX = "log4j.additivity"; + private static final String APPENDER_PREFIX = "log4j.appender"; + private static final String CATEGORY_PREFIX = "log4j.category"; + private static final String LOGGER_PREFIX = "log4j.logger"; + + private static final String ROOT_CATEGORY_KEY = "log4j.rootCategory"; + private static final String ROOT_LOGGER_KEY = "log4j.rootLogger"; + private static final String THRESHOLD_KEY = "log4j.threshold"; + + private static final String LOG4J_V1_PROPERTIES_FORMAT = "v1:properties"; + + @Override + public String getInputFormat() { + return LOG4J_V1_PROPERTIES_FORMAT; + } + + @Override + public ConfigurationNode parse(InputStream inputStream) throws IOException { + Properties properties = new Properties(); + properties.load(inputStream); + return parse(PropertiesSubset.of("", properties)); + } + + private ConfigurationNode parse(PropertiesSubset properties) { + ConfigurationNodeBuilder builder = ComponentUtils.newNodeBuilder().setPluginName("Configuration"); + + String level = extractProperty(properties, THRESHOLD_KEY); + + PropertiesSubset appendersProperties = extractSubset(properties, APPENDER_PREFIX); + ConfigurationNode appenders = parseAppenders(appendersProperties); + + ConfigurationNodeBuilder loggersNodeBuilder = + ComponentUtils.newNodeBuilder().setPluginName("Loggers"); + loggersNodeBuilder.addChild(parseRootLogger(properties)); + parseLoggers(properties).forEach(loggersNodeBuilder::addChild); + ConfigurationNode loggers = loggersNodeBuilder.get(); + + // Whatever is left, are user properties + builder.addChild(parseProperties(properties)); + builder.addChild(appenders); + if (level != null) { + builder.addChild(ComponentUtils.newThresholdFilter(level)); + } + builder.addChild(loggers); + return builder.get(); + } + + private ConfigurationNode parseAppenders(PropertiesSubset appendersProperties) { + ConfigurationNodeBuilder appendersBuilder = + ComponentUtils.newNodeBuilder().setPluginName("Appenders"); + partitionOnCommonPrefixes(appendersProperties) + .forEach(appenderProperties -> appendersBuilder.addChild( + AbstractComponentParser.parseConfigurationElement(this, appenderProperties))); + return appendersBuilder.get(); + } + + private ConfigurationNode parseRootLogger(PropertiesSubset globalProperties) { + PropertiesSubset rootProperties = extractSubset(globalProperties, ROOT_LOGGER_KEY); + String levelAndRefs = extractProperty(rootProperties, ""); + PropertiesUtils.throwIfNotEmpty(rootProperties); + // Check rootCategory + rootProperties = extractSubset(rootProperties, ROOT_CATEGORY_KEY); + if (levelAndRefs == null) { + levelAndRefs = extractProperty(globalProperties, ""); + } + PropertiesUtils.throwIfNotEmpty(rootProperties); + if (levelAndRefs == null) { + throw new ConfigurationConverterException("No root logger configuration found!"); + } + LoggerConfig loggerConfig = new LoggerConfig(""); + loggerConfig.setLevelAndRefs(levelAndRefs); + return loggerConfig.buildRoot(); + } + + private List parseLoggers(PropertiesSubset globalProperties) { + List loggers = new ArrayList<>(); + Map loggerConfigs = new HashMap<>(); + // Handle `log4j.logger` + extractSubset(globalProperties, LOGGER_PREFIX) + .getProperties() + .forEach((key, levelAndRefs) -> loggerConfigs.compute((String) key, (name, oldConfig) -> { + LoggerConfig config = new LoggerConfig(name); + config.setLevelAndRefs((String) levelAndRefs); + return config; + })); + // Handler `log4j.catetory` + extractSubset(globalProperties, CATEGORY_PREFIX) + .getProperties() + .forEach((key, levelAndRefs) -> loggerConfigs.compute((String) key, (name, oldConfig) -> { + if (oldConfig != null) { + throw new ConfigurationConverterException(String.format( + "Configuration file contains both a '%s.%s' and '%s.%s' key.", + LOGGER_PREFIX, key, CATEGORY_PREFIX, key)); + } + LoggerConfig config = new LoggerConfig(name); + config.setLevelAndRefs((String) levelAndRefs); + return config; + })); + // Handle `log4j.additivity` + extractSubset(globalProperties, ADDITIVITY_PREFIX) + .getProperties() + .forEach((key, additivity) -> loggerConfigs.compute((String) key, (name, oldConfig) -> { + LoggerConfig config = oldConfig != null ? oldConfig : new LoggerConfig(name); + config.setAdditivity((String) additivity); + return config; + })); + loggerConfigs.values().stream().map(LoggerConfig::buildLogger).forEach(loggers::add); + return loggers; + } + + private ConfigurationNode parseProperties(PropertiesSubset globalProperties) { + ConfigurationNodeBuilder propertiesBuilder = + ComponentUtils.newNodeBuilder().setPluginName("Properties"); + globalProperties.getProperties().forEach((name, value) -> { + ConfigurationNodeBuilder builder = ComponentUtils.newNodeBuilder() + .setPluginName("Property") + .addAttribute("name", (String) name) + .addAttribute("value", (String) value); + propertiesBuilder.addChild(builder.get()); + }); + return propertiesBuilder.get(); + } +} diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/XmlV1ConfigurationParser.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/XmlV1ConfigurationParser.java new file mode 100644 index 0000000..8425014 --- /dev/null +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/XmlV1ConfigurationParser.java @@ -0,0 +1,154 @@ +/* + * 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.converter.config.internal.v1; + +import static org.apache.logging.converter.config.internal.XmlUtils.throwUnknownElement; +import static org.apache.logging.converter.config.internal.v1.AbstractComponentParser.NAME_ATTR; +import static org.apache.logging.converter.config.internal.v1.AbstractComponentParser.VALUE_ATTR; + +import aQute.bnd.annotation.Resolution; +import aQute.bnd.annotation.spi.ServiceProvider; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Locale; +import javax.xml.parsers.DocumentBuilder; +import org.apache.logging.converter.config.internal.ComponentUtils; +import org.apache.logging.converter.config.internal.ComponentUtils.ConfigurationNodeBuilder; +import org.apache.logging.converter.config.internal.XmlUtils; +import org.apache.logging.converter.config.spi.ConfigurationNode; +import org.apache.logging.converter.config.spi.ConfigurationParser; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.xml.sax.SAXException; + +@ServiceProvider(value = ConfigurationParser.class, resolution = Resolution.MANDATORY) +public class XmlV1ConfigurationParser extends AbstractV1ConfigurationParser { + + private static final String APPENDER_TAG = "appender"; + private static final String APPENDER_REF_TAG = "appender-ref"; + private static final String CONFIGURATION_TAG = "log4j:configuration"; + private static final String OLD_CONFIGURATION_TAG = "configuration"; + private static final String LEVEL_TAG = "level"; + private static final String OLD_LEVEL_TAG = "priority"; + private static final String LOGGER_TAG = "logger"; + private static final String OLD_LOGGER_TAG = "category"; + private static final String ROOT_TAG = "root"; + + private static final String ADDITIVITY_ATTR = "additivity"; + private static final String REF_ATTR = "ref"; + private static final String THRESHOLD_ATTR = "threshold"; + + private static final String LOG4J_V1_XML_FORMAT = "v1:xml"; + + @Override + public String getInputFormat() { + return LOG4J_V1_XML_FORMAT; + } + + @Override + public ConfigurationNode parse(InputStream inputStream) throws IOException { + DocumentBuilder documentBuilder = XmlUtils.createDocumentBuilderV1(); + try { + Document document = documentBuilder.parse(inputStream); + return parse(document.getDocumentElement()); + } catch (SAXException e) { + Throwable cause = e.getCause(); + if (cause instanceof IOException) { + throw (IOException) cause; + } + throw new IOException("Unable to parse configuration file.", e); + } + } + + private ConfigurationNode parse(Element configurationElement) { + ConfigurationNodeBuilder builder = ComponentUtils.newNodeBuilder().setPluginName("Configuration"); + switch (configurationElement.getTagName()) { + case CONFIGURATION_TAG: + case OLD_CONFIGURATION_TAG: + break; + default: + throwUnknownElement(configurationElement); + } + + String level = configurationElement.getAttribute(THRESHOLD_ATTR); + + ConfigurationNodeBuilder appendersBuilder = + ComponentUtils.newNodeBuilder().setPluginName("Appenders"); + + LoggerConfig rootLogger = new LoggerConfig(""); + rootLogger.setLevel("DEBUG"); + Collection loggerConfigs = new ArrayList<>(); + XmlUtils.childStream(configurationElement).forEach(childElement -> { + switch (childElement.getTagName()) { + case APPENDER_TAG: + appendersBuilder.addChild(parseAppender(childElement)); + break; + case ROOT_TAG: + parseLoggerChildren(rootLogger, childElement); + break; + case LOGGER_TAG: + case OLD_LOGGER_TAG: + LoggerConfig loggerConfig = new LoggerConfig(childElement.getAttribute(NAME_ATTR)); + loggerConfigs.add(parseLoggerChildren(loggerConfig, childElement)); + break; + default: + throwUnknownElement(childElement); + } + }); + builder.addChild(appendersBuilder.get()); + + if (!level.isEmpty()) { + builder.addChild(ComponentUtils.newThresholdFilter(level.toUpperCase(Locale.ROOT))); + } + + ConfigurationNodeBuilder loggersNodeBuilder = + ComponentUtils.newNodeBuilder().setPluginName("Loggers"); + loggersNodeBuilder.addChild(rootLogger.buildRoot()); + loggerConfigs.stream().map(LoggerConfig::buildLogger).forEach(loggersNodeBuilder::addChild); + builder.addChild(loggersNodeBuilder.get()); + + return builder.get(); + } + + private ConfigurationNode parseAppender(Element appenderElement) { + return AbstractComponentParser.parseConfigurationElement(this, appenderElement); + } + + private LoggerConfig parseLoggerChildren(LoggerConfig loggerConfig, Element element) { + String additivity = element.getAttribute(ADDITIVITY_ATTR); + if (!additivity.isEmpty()) { + loggerConfig.setAdditivity(additivity); + } + XmlUtils.forEachChild(element, childElement -> { + String nodeName = childElement.getTagName(); + switch (nodeName) { + case APPENDER_REF_TAG: + loggerConfig.addAppenderRef(childElement.getAttribute(REF_ATTR)); + break; + case LEVEL_TAG: + case OLD_LEVEL_TAG: + loggerConfig.setLevel(childElement.getAttribute(VALUE_ATTR)); + break; + default: + throwUnknownElement(childElement); + } + }); + return loggerConfig; + } +} diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/appender/AbstractAppenderParser.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/appender/AbstractAppenderParser.java new file mode 100644 index 0000000..28fb08c --- /dev/null +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/appender/AbstractAppenderParser.java @@ -0,0 +1,153 @@ +/* + * 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.converter.config.internal.v1.appender; + +import static org.apache.logging.converter.config.internal.ComponentUtils.newCompositeFilter; +import static org.apache.logging.converter.config.internal.ComponentUtils.newThresholdFilter; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; +import org.apache.logging.converter.config.ConfigurationConverterException; +import org.apache.logging.converter.config.internal.ComponentUtils.ConfigurationNodeBuilder; +import org.apache.logging.converter.config.internal.PropertiesUtils; +import org.apache.logging.converter.config.internal.v1.AbstractComponentParser; +import org.apache.logging.converter.config.spi.ConfigurationNode; +import org.apache.logging.converter.config.spi.v1.Log4j1ParserContext; +import org.apache.logging.converter.config.spi.v1.PropertiesSubset; +import org.jspecify.annotations.Nullable; +import org.w3c.dom.Element; + +/** + * Common base for all appender parsers. + */ +public abstract class AbstractAppenderParser + extends AbstractComponentParser { + + // XML tags + private static final String FILTER_TAG = "filter"; + private static final String LAYOUT_TAG = "layout"; + + // parameters + protected static final String APPEND_PARAM = "Append"; + protected static final String BUFFERED_IO_PARAM = "BufferedIO"; + protected static final String BUFFER_SIZE_PARAM = "BufferSize"; + protected static final String IMMEDIATE_FLUSH_PARAM = "ImmediateFlush"; + + protected static String getAppenderName(PropertiesSubset properties) { + return PropertiesUtils.getLastComponent(properties.getPrefix()); + } + + protected static String getAppenderName(Element appenderElement) { + String name = appenderElement.getAttribute(NAME_ATTR); + if (name.isEmpty()) { + throw new ConfigurationConverterException("No name specified for appender " + appenderElement.getTagName()); + } + return name; + } + + @Override + protected final T createBuilder(Element element) { + return createBuilder(getAppenderName(element)); + } + + @Override + protected final T createBuilder(PropertiesSubset properties) { + return createBuilder(getAppenderName(properties)); + } + + /** + * Creates a configuration node builder. + * + * @param name The name of the appender. + */ + protected abstract T createBuilder(String name); + + @Override + protected void handleUnknownElement(Element childElement, Log4j1ParserContext context, T componentBuilder) + throws ConfigurationConverterException { + String nodeName = childElement.getTagName(); + if (nodeName.equals(LAYOUT_TAG)) { + componentBuilder.setLayout(parseConfigurationElement(context, childElement)); + } else if (nodeName.equals(FILTER_TAG)) { + componentBuilder.addFilter(parseConfigurationElement(context, childElement)); + } else { + handleUnknownElement(childElement, context, componentBuilder); + } + } + + @Override + protected void handleUnknownProperties(PropertiesSubset properties, Log4j1ParserContext context, T componentBuilder) + throws ConfigurationConverterException { + String key = PropertiesUtils.getLastComponent(properties.getPrefix()); + if (key.equals(LAYOUT_TAG)) { + componentBuilder.setLayout(parseConfigurationElement(context, properties)); + } else if (key.equals(FILTER_TAG)) { + PropertiesUtils.partitionOnCommonPrefixes(properties) + .forEach(filterProperties -> + componentBuilder.addFilter(parseConfigurationElement(context, filterProperties))); + } else { + super.handleUnknownProperties(properties, context, componentBuilder); + } + } + + public abstract static class AppenderBuilder implements Supplier { + + private final String name; + private final boolean requiresLayout; + + private @Nullable String threshold = null; + private final List filters = new ArrayList<>(); + private @Nullable ConfigurationNode layout; + + protected AppenderBuilder(String name, boolean requiresLayout) { + this.name = name; + this.requiresLayout = requiresLayout; + } + + public void setThreshold(String level) { + this.threshold = level; + } + + public void addFilter(ConfigurationNode filter) { + filters.add(filter); + } + + public void setLayout(ConfigurationNode layout) { + this.layout = layout; + } + + protected String getName() { + return name; + } + + protected ConfigurationNodeBuilder addStandardChildren(ConfigurationNodeBuilder builder) { + if (threshold != null) { + filters.add(0, newThresholdFilter(threshold)); + } + if (!filters.isEmpty()) { + builder.addChild(filters.size() > 1 ? newCompositeFilter(filters) : filters.get(0)); + } + if (layout != null) { + builder.addChild(layout); + } else if (requiresLayout) { + throw new ConfigurationConverterException("No layout provided for appender " + name); + } + return builder; + } + } +} diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/appender/AbstractFileAppenderParser.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/appender/AbstractFileAppenderParser.java new file mode 100644 index 0000000..abea495 --- /dev/null +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/appender/AbstractFileAppenderParser.java @@ -0,0 +1,108 @@ +/* + * 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.converter.config.internal.v1.appender; + +import static org.apache.logging.converter.config.internal.StringUtils.decapitalize; + +import java.lang.invoke.MethodHandle; +import java.util.Map; +import java.util.Objects; +import org.apache.logging.converter.config.ConfigurationConverterException; +import org.apache.logging.converter.config.internal.ComponentUtils; +import org.apache.logging.converter.config.internal.ComponentUtils.ConfigurationNodeBuilder; +import org.apache.logging.converter.config.internal.StringUtils; +import org.apache.logging.converter.config.spi.ConfigurationNode; +import org.jspecify.annotations.Nullable; + +/** + * Common base for all file appender parsers. + */ +public abstract class AbstractFileAppenderParser + extends AbstractAppenderParser { + + private static final String FILE_PARAM = "File"; + + protected static final Map FILE_ATTRIBUTE_MAP = attributeMapBuilder( + AbstractFileAppenderParser.AbstractFileAppenderBuilder.class) + .add(APPEND_PARAM) + .add(BUFFERED_IO_PARAM) + .add(BUFFER_SIZE_PARAM) + .add(FILE_PARAM) + .add(IMMEDIATE_FLUSH_PARAM) + .add(THRESHOLD_PARAM) + .get(); + + public abstract static class AbstractFileAppenderBuilder extends AbstractAppenderParser.AppenderBuilder { + + private String append = StringUtils.TRUE; + private boolean bufferedIO = false; + private String bufferSize = "8192"; + private @Nullable String file; + private boolean immediateFlush = true; + + protected AbstractFileAppenderBuilder(String name) { + super(name, true); + } + + public void setAppend(String append) { + this.append = append; + } + + public void setBufferedIO(String bufferedIO) { + this.bufferedIO = StringUtils.parseBoolean(bufferedIO); + } + + public void setBufferSize(String bufferSize) { + this.bufferSize = bufferSize; + } + + protected String getRequiredFile() { + return Objects.requireNonNull(file); + } + + public void setFile(String file) { + this.file = file; + } + + public void setImmediateFlush(String immediateFlush) { + this.immediateFlush = StringUtils.parseBoolean(immediateFlush); + } + + protected void addFileAttributes(ConfigurationNodeBuilder builder) { + if (bufferedIO) { + immediateFlush = false; + } + if (file == null) { + throw new ConfigurationConverterException("No file specified for appender " + getName()); + } + builder.addAttribute("name", getName()) + .addAttribute(decapitalize(APPEND_PARAM), append) + .addAttribute("bufferedIo", bufferedIO) + .addAttribute(decapitalize(BUFFER_SIZE_PARAM), bufferSize) + .addAttribute(decapitalize(IMMEDIATE_FLUSH_PARAM), immediateFlush) + .addAttribute("fileName", file); + } + + @Override + public ConfigurationNode get() { + ConfigurationNodeBuilder builder = ComponentUtils.newNodeBuilder().setPluginName("File"); + addFileAttributes(builder); + addStandardChildren(builder); + return builder.get(); + } + } +} diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/appender/AsyncAppenderParser.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/appender/AsyncAppenderParser.java new file mode 100644 index 0000000..f7a614c --- /dev/null +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/appender/AsyncAppenderParser.java @@ -0,0 +1,132 @@ +/* + * 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.converter.config.internal.v1.appender; + +import static org.apache.logging.converter.config.internal.StringUtils.decapitalize; + +import aQute.bnd.annotation.spi.ServiceProvider; +import java.lang.invoke.MethodHandle; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; +import org.apache.logging.converter.config.ConfigurationConverterException; +import org.apache.logging.converter.config.internal.ComponentUtils; +import org.apache.logging.converter.config.internal.ComponentUtils.ConfigurationNodeBuilder; +import org.apache.logging.converter.config.internal.StringUtils; +import org.apache.logging.converter.config.spi.ConfigurationNode; +import org.apache.logging.converter.config.spi.v1.Log4j1ComponentParser; +import org.apache.logging.converter.config.spi.v1.Log4j1ParserContext; +import org.w3c.dom.Element; + +/** + * Parses a + * AsyncAppender + * configuration. + */ +@ServiceProvider(Log4j1ComponentParser.class) +public class AsyncAppenderParser extends AbstractAppenderParser { + + // XML tags + private static final String APPENDER_REF_TAG = "appender-ref"; + + // Parameters + private static final String BLOCKING_PARAM = "Blocking"; + private static final String INCLUDE_LOCATION_PARAM = "IncludeLocation"; + + private static final Map ATTRIBUTE_MAP = attributeMapBuilder( + AsyncAppenderParser.AsyncAppenderBuilder.class) + .add(BLOCKING_PARAM) + .add(BUFFER_SIZE_PARAM) + .add(INCLUDE_LOCATION_PARAM) + .add(THRESHOLD_PARAM) + .get(); + + @Override + public String getClassName() { + return "org.apache.log4j.AsyncAppender"; + } + + @Override + protected AsyncAppenderBuilder createBuilder(String appenderName) { + return new AsyncAppenderBuilder(appenderName); + } + + @Override + protected Map getAttributeMap() { + return ATTRIBUTE_MAP; + } + + @Override + protected void handleUnknownElement( + Element childElement, Log4j1ParserContext context, AsyncAppenderBuilder componentBuilder) + throws ConfigurationConverterException { + if (childElement.getTagName().equals(APPENDER_REF_TAG)) { + componentBuilder.addAppenderRef(childElement.getAttribute(REF_ATTR)); + } else { + super.handleUnknownElement(childElement, context, componentBuilder); + } + } + + public static final class AsyncAppenderBuilder extends AbstractAppenderParser.AppenderBuilder { + + private final Collection appenderRefs = new ArrayList<>(); + private String blocking = StringUtils.FALSE; + private String bufferSize = "1024"; + private String includeLocation = StringUtils.FALSE; + + private AsyncAppenderBuilder(String name) { + super(name, false); + } + + public void addAppenderRef(String appenderRef) { + appenderRefs.add(appenderRef); + } + + public void setAppenderRefs(String appenderRefs) { + this.appenderRefs.clear(); + String[] array = appenderRefs.split(",", -1); + for (String appenderRef : array) { + this.appenderRefs.add(appenderRef.trim()); + } + } + + public void setBlocking(String blocking) { + this.blocking = blocking; + } + + public void setBufferSize(String bufferSize) { + this.bufferSize = bufferSize; + } + + public void setIncludeLocation(String includeLocation) { + this.includeLocation = includeLocation; + } + + @Override + public ConfigurationNode get() { + ConfigurationNodeBuilder builder = ComponentUtils.newNodeBuilder() + .setPluginName("Async") + .addAttribute("name", getName()) + .addAttribute(decapitalize(BLOCKING_PARAM), blocking) + .addAttribute(decapitalize(BUFFER_SIZE_PARAM), bufferSize) + .addAttribute(decapitalize(INCLUDE_LOCATION_PARAM), includeLocation); + addStandardChildren(builder); + appenderRefs.forEach(ref -> builder.addChild(ComponentUtils.newAppenderRef(ref))); + return builder.get(); + } + } +} diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/appender/ConsoleAppenderParser.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/appender/ConsoleAppenderParser.java new file mode 100644 index 0000000..6054179 --- /dev/null +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/appender/ConsoleAppenderParser.java @@ -0,0 +1,112 @@ +/* + * 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.converter.config.internal.v1.appender; + +import static org.apache.logging.converter.config.internal.StringUtils.decapitalize; + +import aQute.bnd.annotation.spi.ServiceProvider; +import java.lang.invoke.MethodHandle; +import java.util.Map; +import org.apache.logging.converter.config.ConfigurationConverterException; +import org.apache.logging.converter.config.internal.ComponentUtils; +import org.apache.logging.converter.config.internal.ComponentUtils.ConfigurationNodeBuilder; +import org.apache.logging.converter.config.internal.StringUtils; +import org.apache.logging.converter.config.spi.ConfigurationNode; +import org.apache.logging.converter.config.spi.v1.Log4j1ComponentParser; + +/** + * Parses a + * ConsoleAppender + * configuration. + */ +@ServiceProvider(Log4j1ComponentParser.class) +public class ConsoleAppenderParser extends AbstractAppenderParser { + + private static final String V1_SYSTEM_OUT = "System.out"; + private static final String V1_SYSTEM_ERR = "System.err"; + private static final String V2_SYSTEM_OUT = "SYSTEM_OUT"; + private static final String V2_SYSTEM_ERR = "SYSTEM_ERR"; + + // V1 configuration parameters + private static final String TARGET_PARAM = "Target"; + private static final String FOLLOW_PARAM = "Follow"; + + private static final Map ATTRIBUTE_MAP = attributeMapBuilder(ConsoleAppenderBuilder.class) + .add(FOLLOW_PARAM) + .add(IMMEDIATE_FLUSH_PARAM) + .add(TARGET_PARAM) + .add(THRESHOLD_PARAM) + .get(); + + @Override + public String getClassName() { + return "org.apache.log4j.ConsoleAppender"; + } + + @Override + protected ConsoleAppenderBuilder createBuilder(String name) { + return new ConsoleAppenderBuilder(name); + } + + @Override + protected Map getAttributeMap() { + return ATTRIBUTE_MAP; + } + + public static final class ConsoleAppenderBuilder extends AbstractAppenderParser.AppenderBuilder { + + private String follow = StringUtils.FALSE; + private String immediateFlush = StringUtils.TRUE; + private String v1Target = V1_SYSTEM_OUT; + + private ConsoleAppenderBuilder(String name) { + super(name, true); + } + + public void setFollow(String follow) { + this.follow = follow; + } + + public void setImmediateFlush(String immediateFlush) { + this.immediateFlush = immediateFlush; + } + + public void setTarget(String v1Target) { + switch (v1Target) { + case V1_SYSTEM_OUT: + case V1_SYSTEM_ERR: + this.v1Target = v1Target; + break; + default: + throw new ConfigurationConverterException( + "Invalid target attribute `" + v1Target + "` for ConsoleAppender " + getName()); + } + } + + @Override + public ConfigurationNode get() { + ConfigurationNodeBuilder builder = ComponentUtils.newNodeBuilder() + .setPluginName("Console") + .addAttribute("name", getName()) + .addAttribute(decapitalize(FOLLOW_PARAM), follow) + .addAttribute(decapitalize(IMMEDIATE_FLUSH_PARAM), immediateFlush) + .addAttribute( + decapitalize(TARGET_PARAM), V1_SYSTEM_OUT.equals(v1Target) ? V2_SYSTEM_OUT : V2_SYSTEM_ERR); + return addStandardChildren(builder).get(); + } + } +} diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/appender/DailyRollingFileAppenderParser.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/appender/DailyRollingFileAppenderParser.java new file mode 100644 index 0000000..5fb5340 --- /dev/null +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/appender/DailyRollingFileAppenderParser.java @@ -0,0 +1,92 @@ +/* + * 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.converter.config.internal.v1.appender; + +import aQute.bnd.annotation.spi.ServiceProvider; +import java.lang.invoke.MethodHandle; +import java.util.Map; +import org.apache.logging.converter.config.internal.ComponentUtils; +import org.apache.logging.converter.config.internal.ComponentUtils.ConfigurationNodeBuilder; +import org.apache.logging.converter.config.spi.ConfigurationNode; +import org.apache.logging.converter.config.spi.v1.Log4j1ComponentParser; + +/** + * Parses a + * DailyRollingFileAppender + * configuration. + */ +@ServiceProvider(Log4j1ComponentParser.class) +public class DailyRollingFileAppenderParser + extends AbstractFileAppenderParser { + + private static final String DEFAULT_DATE_PATTERN = ".yyyy-MM-dd"; + private static final String DATE_PATTERN_PARAM = "DatePattern"; + + private static final Map ATTRIBUTE_MAP = attributeMapBuilder( + DailyRollingFileAppenderBuilder.class) + .add(DATE_PATTERN_PARAM) + .addAll(FILE_ATTRIBUTE_MAP) + .get(); + + @Override + public String getClassName() { + return "org.apache.log4j.DailyRollingFileAppender"; + } + + @Override + protected DailyRollingFileAppenderBuilder createBuilder(String name) { + return new DailyRollingFileAppenderBuilder(name); + } + + @Override + protected Map getAttributeMap() { + return ATTRIBUTE_MAP; + } + + public static final class DailyRollingFileAppenderBuilder + extends AbstractFileAppenderParser.AbstractFileAppenderBuilder { + + private String datePattern = DEFAULT_DATE_PATTERN; + + private DailyRollingFileAppenderBuilder(String name) { + super(name); + } + + public void setDatePattern(String datePattern) { + this.datePattern = datePattern; + } + + @Override + public ConfigurationNode get() { + ConfigurationNodeBuilder builder = ComponentUtils.newNodeBuilder().setPluginName("RollingFile"); + addFileAttributes(builder); + addStandardChildren(builder); + + String filePattern = getRequiredFile() + "%d{" + datePattern + "}"; + return builder.addAttribute("filePattern", filePattern) + .addChild(createTriggeringPolicy()) + .get(); + } + + private ConfigurationNode createTriggeringPolicy() { + return ComponentUtils.newNodeBuilder() + .setPluginName("TimeBasedTriggeringPolicy") + .addAttribute("modulate", true) + .get(); + } + } +} diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/appender/FileAppenderParser.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/appender/FileAppenderParser.java new file mode 100644 index 0000000..961ad67 --- /dev/null +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/appender/FileAppenderParser.java @@ -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. + */ +package org.apache.logging.converter.config.internal.v1.appender; + +import aQute.bnd.annotation.spi.ServiceProvider; +import java.lang.invoke.MethodHandle; +import java.util.Map; +import org.apache.logging.converter.config.spi.v1.Log4j1ComponentParser; + +/** + * Parses a + * FileAppender + * configuration. + */ +@ServiceProvider(Log4j1ComponentParser.class) +public class FileAppenderParser extends AbstractFileAppenderParser { + @Override + public String getClassName() { + return "org.apache.log4j.FileAppender"; + } + + @Override + protected FileAppenderBuilder createBuilder(String name) { + return new FileAppenderBuilder(name); + } + + @Override + protected Map getAttributeMap() { + return FILE_ATTRIBUTE_MAP; + } + + public static final class FileAppenderBuilder extends AbstractFileAppenderParser.AbstractFileAppenderBuilder { + + private FileAppenderBuilder(String name) { + super(name); + } + } +} diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/appender/RollingFileAppenderParser.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/appender/RollingFileAppenderParser.java new file mode 100644 index 0000000..6a69e95 --- /dev/null +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/appender/RollingFileAppenderParser.java @@ -0,0 +1,133 @@ +/* + * 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.converter.config.internal.v1.appender; + +import aQute.bnd.annotation.spi.ServiceProvider; +import java.lang.invoke.MethodHandle; +import java.util.Formatter; +import java.util.Locale; +import java.util.Map; +import org.apache.logging.converter.config.internal.ComponentUtils; +import org.apache.logging.converter.config.spi.ConfigurationNode; +import org.apache.logging.converter.config.spi.v1.Log4j1ComponentParser; + +/** + * Parses a + * RollingFileAppender + * configuration. + */ +@ServiceProvider(Log4j1ComponentParser.class) +public class RollingFileAppenderParser + extends AbstractFileAppenderParser { + + private static final String MAX_BACKUP_INDEX_PARAM = "MaxBackupIndex"; + private static final String MAX_FILE_SIZE_PARAM = "MaxFileSize"; + + private static final String DEFAULT_MAX_BACKUP_INDEX = "1"; + private static final long KB = 1024; + private static final long MB = KB * KB; + private static final long GB = KB * MB; + private static final long DEFAULT_MAX_FILE_SIZE = 10 * MB; + private static final String SIZE_FORMAT = "%.2f %s"; + + private static final Map ATTRIBUTE_MAP = attributeMapBuilder( + RollingFileAppenderParser.RollingFileAppenderBuilder.class) + .add(MAX_BACKUP_INDEX_PARAM) + .add(MAX_FILE_SIZE_PARAM) + .addAll(FILE_ATTRIBUTE_MAP) + .get(); + + @Override + public String getClassName() { + return "org.apache.log4j.RollingFileAppender"; + } + + @Override + protected RollingFileAppenderBuilder createBuilder(String name) { + return new RollingFileAppenderBuilder(name); + } + + @Override + protected Map getAttributeMap() { + return ATTRIBUTE_MAP; + } + + public static final class RollingFileAppenderBuilder + extends AbstractFileAppenderParser.AbstractFileAppenderBuilder { + + private String maxBackupIndex = DEFAULT_MAX_BACKUP_INDEX; + private String maxFileSize = Long.toString(DEFAULT_MAX_FILE_SIZE); + + private RollingFileAppenderBuilder(String name) { + super(name); + } + + public void setMaxBackupIndex(String maxBackupIndex) { + this.maxBackupIndex = maxBackupIndex; + } + + public void setMaxFileSize(String maxFileSize) { + this.maxFileSize = maxFileSize; + } + + @Override + public ConfigurationNode get() { + ComponentUtils.ConfigurationNodeBuilder builder = + ComponentUtils.newNodeBuilder().setPluginName("RollingFile"); + addFileAttributes(builder); + addStandardChildren(builder); + + String filePattern = getRequiredFile() + ".%i"; + return builder.addAttribute("filePattern", filePattern) + .addChild(createTriggeringPolicy()) + .addChild(createRolloverStrategy()) + .get(); + } + + private ConfigurationNode createTriggeringPolicy() { + StringBuilder sizeBuilder = new StringBuilder(); + try { + long maxFileSize = Long.parseLong(this.maxFileSize); + Formatter sizeFormatter = new Formatter(sizeBuilder, Locale.ROOT); + if (maxFileSize > GB) { + sizeFormatter.format(SIZE_FORMAT, ((float) maxFileSize) / GB, "GB"); + } else if (maxFileSize > MB) { + sizeFormatter.format(SIZE_FORMAT, ((float) maxFileSize) / MB, "MB"); + } else if (maxFileSize > KB) { + sizeFormatter.format(SIZE_FORMAT, ((float) maxFileSize) / KB, "KB"); + } else { + sizeBuilder.append(maxFileSize); + } + } catch (NumberFormatException e) { + // The value contains property expansions + sizeBuilder.append(this.maxFileSize); + } + return ComponentUtils.newNodeBuilder() + .setPluginName("SizeBasedTriggeringPolicy") + .addAttribute("size", sizeBuilder.toString()) + .get(); + } + + private ConfigurationNode createRolloverStrategy() { + return ComponentUtils.newNodeBuilder() + .setPluginName("DefaultRolloverStrategy") + .addAttribute("max", maxBackupIndex) + .addAttribute("fileIndex", "min") + .get(); + } + } +} diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/appender/package-info.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/appender/package-info.java new file mode 100644 index 0000000..1731f8f --- /dev/null +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/appender/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ +@NullMarked +package org.apache.logging.converter.config.internal.v1.appender; + +import org.jspecify.annotations.NullMarked; diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/filter/AbstractFilterParser.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/filter/AbstractFilterParser.java new file mode 100644 index 0000000..1110708 --- /dev/null +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/filter/AbstractFilterParser.java @@ -0,0 +1,41 @@ +/* + * 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.converter.config.internal.v1.filter; + +import java.util.function.Supplier; +import org.apache.logging.converter.config.internal.ComponentUtils.ConfigurationNodeBuilder; +import org.apache.logging.converter.config.internal.v1.AbstractComponentParser; +import org.apache.logging.converter.config.spi.ConfigurationNode; + +/** + * Common base for all filter parsers. + */ +public abstract class AbstractFilterParser> extends AbstractComponentParser { + + static final String ACCEPT_ON_MATCH_PARAM = "AcceptOnMatch"; + + private static final String ACCEPT = "ACCEPT"; + private static final String DENY = "DENY"; + private static final String NEUTRAL = "NEUTRAL"; + + private static final String ON_MATCH = "onMatch"; + private static final String ON_MISMATCH = "onMismatch"; + + static void addOnMatchAndMismatch(ConfigurationNodeBuilder builder, boolean acceptOnMatch) { + builder.addAttribute(ON_MATCH, acceptOnMatch ? ACCEPT : DENY).addAttribute(ON_MISMATCH, NEUTRAL); + } +} diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/filter/DenyAllFilterParser.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/filter/DenyAllFilterParser.java new file mode 100644 index 0000000..d8c2b75 --- /dev/null +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/filter/DenyAllFilterParser.java @@ -0,0 +1,68 @@ +/* + * 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.converter.config.internal.v1.filter; + +import aQute.bnd.annotation.spi.ServiceProvider; +import java.lang.invoke.MethodHandle; +import java.util.Collections; +import java.util.Map; +import java.util.function.Supplier; +import org.apache.logging.converter.config.internal.ComponentUtils; +import org.apache.logging.converter.config.internal.v1.AbstractComponentParser; +import org.apache.logging.converter.config.spi.ConfigurationNode; +import org.apache.logging.converter.config.spi.v1.Log4j1ComponentParser; +import org.apache.logging.converter.config.spi.v1.PropertiesSubset; +import org.w3c.dom.Element; + +/** + * Parses a + * DenyAllFilter + * configuration. + */ +@ServiceProvider(Log4j1ComponentParser.class) +public class DenyAllFilterParser extends AbstractComponentParser { + + @Override + public String getClassName() { + return "org.apache.log4j.varia.DenyAllFilter"; + } + + @Override + protected DenyAllFilterBuilder createBuilder(Element element) { + return new DenyAllFilterBuilder(); + } + + @Override + protected DenyAllFilterBuilder createBuilder(PropertiesSubset properties) { + return new DenyAllFilterBuilder(); + } + + @Override + protected Map getAttributeMap() { + return Collections.emptyMap(); + } + + public static final class DenyAllFilterBuilder implements Supplier { + + @Override + public ConfigurationNode get() { + return ComponentUtils.newNodeBuilder() + .setPluginName("DenyAllFilter") + .get(); + } + } +} diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/filter/LevelMatchFilterParser.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/filter/LevelMatchFilterParser.java new file mode 100644 index 0000000..acf54bc --- /dev/null +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/filter/LevelMatchFilterParser.java @@ -0,0 +1,91 @@ +/* + * 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.converter.config.internal.v1.filter; + +import static org.apache.logging.converter.config.internal.StringUtils.parseBoolean; + +import aQute.bnd.annotation.spi.ServiceProvider; +import java.lang.invoke.MethodHandle; +import java.util.Map; +import java.util.function.Supplier; +import org.apache.logging.converter.config.internal.ComponentUtils; +import org.apache.logging.converter.config.internal.ComponentUtils.ConfigurationNodeBuilder; +import org.apache.logging.converter.config.spi.ConfigurationNode; +import org.apache.logging.converter.config.spi.v1.Log4j1ComponentParser; +import org.apache.logging.converter.config.spi.v1.PropertiesSubset; +import org.w3c.dom.Element; + +/** + * Parses a + * LevelMatchFilter + * configuration. + */ +@ServiceProvider(Log4j1ComponentParser.class) +public class LevelMatchFilterParser extends AbstractFilterParser { + + private static final String ACCEPT_ON_MATCH = "AcceptOnMatch"; + private static final String LEVEL_TO_MATCH = "LevelToMatch"; + + private static final Map ATTRIBUTE_MAP = attributeMapBuilder( + LevelMatchFilterParser.LevelMatchFilterBuilder.class) + .add(ACCEPT_ON_MATCH) + .add(LEVEL_TO_MATCH) + .get(); + + @Override + public String getClassName() { + return "org.apache.log4j.varia.LevelMatchFilter"; + } + + @Override + protected LevelMatchFilterBuilder createBuilder(Element element) { + return new LevelMatchFilterBuilder(); + } + + @Override + protected LevelMatchFilterBuilder createBuilder(PropertiesSubset properties) { + return new LevelMatchFilterBuilder(); + } + + @Override + protected Map getAttributeMap() { + return ATTRIBUTE_MAP; + } + + public static final class LevelMatchFilterBuilder implements Supplier { + + private boolean acceptOnMatch = false; + private String level = "ERROR"; + + public void setAcceptOnMatch(String acceptOnMatch) { + this.acceptOnMatch = parseBoolean(acceptOnMatch); + } + + public void setLevelToMatch(String level) { + this.level = level; + } + + @Override + public ConfigurationNode get() { + ConfigurationNodeBuilder builder = ComponentUtils.newNodeBuilder() + .setPluginName("LevelMatchFilter") + .addAttribute("level", level); + addOnMatchAndMismatch(builder, acceptOnMatch); + return builder.get(); + } + } +} diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/filter/LevelRangeFilterParser.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/filter/LevelRangeFilterParser.java new file mode 100644 index 0000000..d668738 --- /dev/null +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/filter/LevelRangeFilterParser.java @@ -0,0 +1,99 @@ +/* + * 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.converter.config.internal.v1.filter; + +import static org.apache.logging.converter.config.internal.StringUtils.parseBoolean; + +import aQute.bnd.annotation.spi.ServiceProvider; +import java.lang.invoke.MethodHandle; +import java.util.Map; +import java.util.function.Supplier; +import org.apache.logging.converter.config.internal.ComponentUtils; +import org.apache.logging.converter.config.internal.ComponentUtils.ConfigurationNodeBuilder; +import org.apache.logging.converter.config.spi.ConfigurationNode; +import org.apache.logging.converter.config.spi.v1.Log4j1ComponentParser; +import org.apache.logging.converter.config.spi.v1.PropertiesSubset; +import org.w3c.dom.Element; + +/** + * Parses a + * LevelRangeFilter + * configuration. + */ +@ServiceProvider(Log4j1ComponentParser.class) +public class LevelRangeFilterParser extends AbstractFilterParser { + + private static final String LEVEL_MAX_PARAM = "LevelMax"; + private static final String LEVEL_MIN_PARAM = "LevelMin"; + + private static final Map ATTRIBUTE_MAP = attributeMapBuilder(LevelRangeFilterBuilder.class) + .add(ACCEPT_ON_MATCH_PARAM) + .add(LEVEL_MAX_PARAM) + .add(LEVEL_MIN_PARAM) + .get(); + + @Override + public String getClassName() { + return "org.apache.log4j.varia.LevelRangeFilter"; + } + + @Override + protected LevelRangeFilterBuilder createBuilder(Element element) { + return new LevelRangeFilterBuilder(); + } + + @Override + protected LevelRangeFilterBuilder createBuilder(PropertiesSubset properties) { + return new LevelRangeFilterBuilder(); + } + + @Override + protected Map getAttributeMap() { + return ATTRIBUTE_MAP; + } + + public static final class LevelRangeFilterBuilder implements Supplier { + + private boolean acceptOnMatch = false; + private String levelMax = "OFF"; + private String levelMin = "ALL"; + + public void setAcceptOnMatch(String acceptOnMatch) { + this.acceptOnMatch = parseBoolean(acceptOnMatch); + } + + public void setLevelMax(String level) { + this.levelMax = level; + } + + public void setLevelMin(String levelMin) { + this.levelMin = levelMin; + } + + @Override + public ConfigurationNode get() { + // log4j1 order: ALL < TRACE < DEBUG < ... < FATAL < OFF + // log4j2 order: ALL > TRACE > DEBUG > ... > FATAL > OFF + ConfigurationNodeBuilder builder = ComponentUtils.newNodeBuilder() + .setPluginName("LevelRangeFilter") + .addAttribute("minLevel", levelMax) + .addAttribute("maxLevel", levelMin); + addOnMatchAndMismatch(builder, acceptOnMatch); + return builder.get(); + } + } +} diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/filter/StringMatchFilterParser.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/filter/StringMatchFilterParser.java new file mode 100644 index 0000000..0098881 --- /dev/null +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/filter/StringMatchFilterParser.java @@ -0,0 +1,94 @@ +/* + * 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.converter.config.internal.v1.filter; + +import static org.apache.logging.converter.config.internal.StringUtils.parseBoolean; + +import aQute.bnd.annotation.spi.ServiceProvider; +import java.lang.invoke.MethodHandle; +import java.util.Map; +import java.util.function.Supplier; +import org.apache.logging.converter.config.ConfigurationConverterException; +import org.apache.logging.converter.config.internal.ComponentUtils; +import org.apache.logging.converter.config.internal.ComponentUtils.ConfigurationNodeBuilder; +import org.apache.logging.converter.config.spi.ConfigurationNode; +import org.apache.logging.converter.config.spi.v1.Log4j1ComponentParser; +import org.apache.logging.converter.config.spi.v1.PropertiesSubset; +import org.jspecify.annotations.Nullable; +import org.w3c.dom.Element; + +/** + * Parses a + * StringMatchFilter + * configuration. + */ +@ServiceProvider(Log4j1ComponentParser.class) +public class StringMatchFilterParser extends AbstractFilterParser { + + private static final String STRING_TO_MATCH = "StringToMatch"; + + private static final Map ATTRIBUTE_MAP = attributeMapBuilder(StringMatchFilterBuilder.class) + .add(ACCEPT_ON_MATCH_PARAM) + .add(STRING_TO_MATCH) + .get(); + + @Override + public String getClassName() { + return "org.apache.log4j.varia.StringMatchFilter"; + } + + @Override + protected StringMatchFilterBuilder createBuilder(Element element) { + return new StringMatchFilterBuilder(); + } + + @Override + protected StringMatchFilterBuilder createBuilder(PropertiesSubset properties) { + return new StringMatchFilterBuilder(); + } + + @Override + protected Map getAttributeMap() { + return ATTRIBUTE_MAP; + } + + public static final class StringMatchFilterBuilder implements Supplier { + + private boolean acceptOnMatch = false; + private @Nullable String stringToMatch = null; + + public void setAcceptOnMatch(String acceptOnMatch) { + this.acceptOnMatch = parseBoolean(acceptOnMatch); + } + + public void setStringToMatch(String stringToMatch) { + this.stringToMatch = stringToMatch; + } + + @Override + public ConfigurationNode get() { + if (stringToMatch == null) { + throw new ConfigurationConverterException("Missing required '" + STRING_TO_MATCH + "' attribute"); + } + ConfigurationNodeBuilder builder = ComponentUtils.newNodeBuilder() + .setPluginName("StringMatchFilter") + .addAttribute("text", stringToMatch); + addOnMatchAndMismatch(builder, acceptOnMatch); + return builder.get(); + } + } +} diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/filter/package-info.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/filter/package-info.java new file mode 100644 index 0000000..2110ae4 --- /dev/null +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/filter/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ +@NullMarked +package org.apache.logging.converter.config.internal.v1.filter; + +import org.jspecify.annotations.NullMarked; diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/layout/EnhancedPatternLayoutParser.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/layout/EnhancedPatternLayoutParser.java new file mode 100644 index 0000000..7603a53 --- /dev/null +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/layout/EnhancedPatternLayoutParser.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.converter.config.internal.v1.layout; + +import aQute.bnd.annotation.spi.ServiceProvider; +import java.lang.invoke.MethodHandle; +import java.util.Map; +import org.apache.logging.converter.config.internal.v1.AbstractComponentParser; +import org.apache.logging.converter.config.spi.v1.Log4j1ComponentParser; +import org.apache.logging.converter.config.spi.v1.PropertiesSubset; +import org.w3c.dom.Element; + +/** + * Parses a + * EnhancedPatternLayout + * configuration. + */ +@ServiceProvider(Log4j1ComponentParser.class) +public class EnhancedPatternLayoutParser extends AbstractComponentParser { + + @Override + public String getClassName() { + return "org.apache.log4j.EnhancedPatternLayout"; + } + + @Override + protected PatternLayoutParser.PatternLayoutBuilder createBuilder(Element element) { + return new PatternLayoutParser.PatternLayoutBuilder(); + } + + @Override + protected PatternLayoutParser.PatternLayoutBuilder createBuilder(PropertiesSubset properties) { + return new PatternLayoutParser.PatternLayoutBuilder(); + } + + @Override + protected Map getAttributeMap() { + return PatternLayoutParser.ATTRIBUTE_MAP; + } +} diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/layout/HtmlLayoutParser.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/layout/HtmlLayoutParser.java new file mode 100644 index 0000000..83fed36 --- /dev/null +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/layout/HtmlLayoutParser.java @@ -0,0 +1,92 @@ +/* + * 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.converter.config.internal.v1.layout; + +import aQute.bnd.annotation.spi.ServiceProvider; +import java.lang.invoke.MethodHandle; +import java.util.Map; +import java.util.function.Supplier; +import org.apache.logging.converter.config.internal.ComponentUtils; +import org.apache.logging.converter.config.internal.StringUtils; +import org.apache.logging.converter.config.internal.v1.AbstractComponentParser; +import org.apache.logging.converter.config.spi.ConfigurationNode; +import org.apache.logging.converter.config.spi.v1.Log4j1ComponentParser; +import org.apache.logging.converter.config.spi.v1.PropertiesSubset; +import org.w3c.dom.Element; + +/** + * Parses a + * HTMLLayout + * configuration. + */ +@ServiceProvider(Log4j1ComponentParser.class) +public class HtmlLayoutParser extends AbstractComponentParser { + + private static final String LOCATION_INFO_PARAM = "LocationInfo"; + private static final String TITLE_PARAM = "Title"; + private static final String TITLE_DEFAULT = "Log4J Log Messages"; + + private static final Map ATTRIBUTE_MAP = attributeMapBuilder(HtmlLayoutBuilder.class) + .add(LOCATION_INFO_PARAM) + .add(TITLE_PARAM) + .get(); + + @Override + public String getClassName() { + return "org.apache.log4j.HTMLLayout"; + } + + @Override + protected HtmlLayoutBuilder createBuilder(Element element) { + return new HtmlLayoutBuilder(); + } + + @Override + protected HtmlLayoutBuilder createBuilder(PropertiesSubset properties) { + return new HtmlLayoutBuilder(); + } + + @Override + protected Map getAttributeMap() { + return ATTRIBUTE_MAP; + } + + public static final class HtmlLayoutBuilder implements Supplier { + + private String title = TITLE_DEFAULT; + private String locationInfo = StringUtils.FALSE; + + private HtmlLayoutBuilder() {} + + public void setTitle(String title) { + this.title = title; + } + + public void setLocationInfo(String locationInfo) { + this.locationInfo = locationInfo; + } + + @Override + public ConfigurationNode get() { + ComponentUtils.ConfigurationNodeBuilder builder = + ComponentUtils.newNodeBuilder().setPluginName("HtmlLayout"); + builder.addAttribute("title", title); + builder.addAttribute("locationInfo", locationInfo); + return builder.get(); + } + } +} diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/layout/PatternLayoutParser.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/layout/PatternLayoutParser.java new file mode 100644 index 0000000..6c02d66 --- /dev/null +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/layout/PatternLayoutParser.java @@ -0,0 +1,82 @@ +/* + * 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.converter.config.internal.v1.layout; + +import aQute.bnd.annotation.spi.ServiceProvider; +import java.lang.invoke.MethodHandle; +import java.util.Map; +import java.util.function.Supplier; +import org.apache.logging.converter.config.internal.ComponentUtils; +import org.apache.logging.converter.config.internal.v1.AbstractComponentParser; +import org.apache.logging.converter.config.spi.ConfigurationNode; +import org.apache.logging.converter.config.spi.v1.Log4j1ComponentParser; +import org.apache.logging.converter.config.spi.v1.PropertiesSubset; +import org.w3c.dom.Element; + +/** + * Parses a + * PatternLayout + * configuration. + */ +@ServiceProvider(Log4j1ComponentParser.class) +public class PatternLayoutParser extends AbstractComponentParser { + + private static final String CONVERSION_PATTERN_PARAM = "ConversionPattern"; + + static final Map ATTRIBUTE_MAP = attributeMapBuilder(PatternLayoutBuilder.class) + .add(CONVERSION_PATTERN_PARAM) + .get(); + + @Override + public String getClassName() { + return "org.apache.log4j.PatternLayout"; + } + + @Override + protected PatternLayoutBuilder createBuilder(Element element) { + return new PatternLayoutBuilder(); + } + + @Override + protected PatternLayoutBuilder createBuilder(PropertiesSubset properties) { + return new PatternLayoutBuilder(); + } + + @Override + protected Map getAttributeMap() { + return ATTRIBUTE_MAP; + } + + public static final class PatternLayoutBuilder implements Supplier { + + private String conversionPattern = "%m%n"; + + PatternLayoutBuilder() {} + + public void setConversionPattern(String conversionPattern) { + this.conversionPattern = conversionPattern; + } + + @Override + public ConfigurationNode get() { + return ComponentUtils.newNodeBuilder() + .setPluginName("PatternLayout") + .addAttribute("pattern", conversionPattern) + .get(); + } + } +} diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/layout/SimpleLayoutParser.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/layout/SimpleLayoutParser.java new file mode 100644 index 0000000..2795d6f --- /dev/null +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/layout/SimpleLayoutParser.java @@ -0,0 +1,70 @@ +/* + * 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.converter.config.internal.v1.layout; + +import aQute.bnd.annotation.spi.ServiceProvider; +import java.lang.invoke.MethodHandle; +import java.util.Collections; +import java.util.Map; +import java.util.function.Supplier; +import org.apache.logging.converter.config.internal.ComponentUtils; +import org.apache.logging.converter.config.internal.v1.AbstractComponentParser; +import org.apache.logging.converter.config.spi.ConfigurationNode; +import org.apache.logging.converter.config.spi.v1.Log4j1ComponentParser; +import org.apache.logging.converter.config.spi.v1.PropertiesSubset; +import org.w3c.dom.Element; + +/** + * Parses a + * SimpleLayout + * configuration. + */ +@ServiceProvider(Log4j1ComponentParser.class) +public class SimpleLayoutParser extends AbstractComponentParser { + + @Override + public String getClassName() { + return "org.apache.log4j.SimpleLayout"; + } + + @Override + protected SimpleLayoutBuilder createBuilder(Element element) { + return new SimpleLayoutBuilder(); + } + + @Override + protected SimpleLayoutBuilder createBuilder(PropertiesSubset properties) { + return new SimpleLayoutBuilder(); + } + + @Override + protected Map getAttributeMap() { + return Collections.emptyMap(); + } + + public static class SimpleLayoutBuilder implements Supplier { + + @Override + public ConfigurationNode get() { + return ComponentUtils.newNodeBuilder() + .setPluginName("PatternLayout") + .addAttribute("pattern", "%p - %m%n") + .addAttribute("alwaysWriteExceptions", "false") + .get(); + } + } +} diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/layout/TTCCLayoutParser.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/layout/TTCCLayoutParser.java new file mode 100644 index 0000000..f37d93c --- /dev/null +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/layout/TTCCLayoutParser.java @@ -0,0 +1,164 @@ +/* + * 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.converter.config.internal.v1.layout; + +import aQute.bnd.annotation.spi.ServiceProvider; +import java.lang.invoke.MethodHandle; +import java.util.Locale; +import java.util.Map; +import java.util.function.Supplier; +import org.apache.logging.converter.config.internal.ComponentUtils; +import org.apache.logging.converter.config.internal.ComponentUtils.ConfigurationNodeBuilder; +import org.apache.logging.converter.config.internal.StringUtils; +import org.apache.logging.converter.config.internal.v1.AbstractComponentParser; +import org.apache.logging.converter.config.spi.ConfigurationNode; +import org.apache.logging.converter.config.spi.v1.Log4j1ComponentParser; +import org.apache.logging.converter.config.spi.v1.PropertiesSubset; +import org.jspecify.annotations.Nullable; +import org.w3c.dom.Element; + +/** + * Parses a + * TTCCLayout + * configuration. + */ +@ServiceProvider(Log4j1ComponentParser.class) +public class TTCCLayoutParser extends AbstractComponentParser { + + private static final String CATEGORY_PREFIXING_PARAM = "CategoryPrefixing"; + private static final String CONTEXT_PRINTING_PARAM = "ContextPrinting"; + private static final String DATE_FORMAT_PARAM = "DateFormat"; + private static final String THREAD_PRINTING_PARAM = "ThreadPrinting"; + private static final String TIMEZONE_FORMAT_PARAM = "TimeZone"; + + private static final String ABSOLUTE = "ABSOLUTE"; + private static final String ABSOLUTE_FORMAT = "HH:mm:ss,SSS"; + private static final String DATE = "DATE"; + private static final String DATE_FORMAT = "dd MMM yyyy HH:mm:ss,SSS"; + private static final String ISO8601 = "ISO8601"; + private static final String ISO8601_FORMAT = "yyyy-MM-dd HH:mm:ss,SSS"; + private static final String NULL = "NULL"; + private static final String RELATIVE = "RELATIVE"; + + private static final Map ATTRIBUTE_MAP = attributeMapBuilder(TTCCLayoutBuilder.class) + .add(CATEGORY_PREFIXING_PARAM) + .add(CONTEXT_PRINTING_PARAM) + .add(DATE_FORMAT_PARAM) + .add(THREAD_PRINTING_PARAM) + .add(TIMEZONE_FORMAT_PARAM) + .get(); + + @Override + public String getClassName() { + return "org.apache.log4j.TTCCLayout"; + } + + @Override + protected TTCCLayoutBuilder createBuilder(Element element) { + return new TTCCLayoutBuilder(); + } + + @Override + protected TTCCLayoutBuilder createBuilder(PropertiesSubset properties) { + return new TTCCLayoutBuilder(); + } + + @Override + protected Map getAttributeMap() { + return ATTRIBUTE_MAP; + } + + public static final class TTCCLayoutBuilder implements Supplier { + + private boolean threadPrinting = true; + private boolean categoryPrefixing = true; + private boolean contextPrinting = true; + + private @Nullable String dateFormat = RELATIVE; + private @Nullable String timeZone = null; + + private TTCCLayoutBuilder() {} + + public void setThreadPrinting(String threadPrinting) { + this.threadPrinting = StringUtils.parseBoolean(threadPrinting); + } + + public void setCategoryPrefixing(String categoryPrefixing) { + this.categoryPrefixing = StringUtils.parseBoolean(categoryPrefixing); + } + + public void setContextPrinting(String contextPrinting) { + this.contextPrinting = StringUtils.parseBoolean(contextPrinting); + } + + public void setDateFormat(String dateFormat) { + switch (dateFormat.toUpperCase(Locale.ROOT)) { + case ABSOLUTE: + this.dateFormat = ABSOLUTE_FORMAT; + break; + case DATE: + this.dateFormat = DATE_FORMAT; + break; + case ISO8601: + this.dateFormat = ISO8601_FORMAT; + break; + case RELATIVE: + this.dateFormat = RELATIVE; + break; + case NULL: + this.dateFormat = null; + break; + default: + } + } + + public void setTimeZone(String timeZone) { + this.timeZone = timeZone; + } + + @Override + public ConfigurationNode get() { + ConfigurationNodeBuilder nodeBuilder = + ComponentUtils.newNodeBuilder().setPluginName("PatternLayout"); + StringBuilder patternBuilder = new StringBuilder(); + if (dateFormat != null) { + if (RELATIVE.equalsIgnoreCase(dateFormat)) { + patternBuilder.append("%r "); + } else { + patternBuilder.append("%d{").append(dateFormat).append("}"); + if (timeZone != null) { + patternBuilder.append("{").append(timeZone).append("}"); + } + patternBuilder.append(" "); + } + } + if (threadPrinting) { + patternBuilder.append("[%t] "); + } + patternBuilder.append("%p "); + if (categoryPrefixing) { + patternBuilder.append("%c "); + } + if (contextPrinting) { + patternBuilder.append("%notEmpty{%NDC }"); + } + patternBuilder.append("- %m%n"); + nodeBuilder.addAttribute("pattern", patternBuilder.toString()); + return nodeBuilder.get(); + } + } +} diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/layout/package-info.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/layout/package-info.java new file mode 100644 index 0000000..dcb8e4f --- /dev/null +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/layout/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ +@NullMarked +package org.apache.logging.converter.config.internal.v1.layout; + +import org.jspecify.annotations.NullMarked; diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/package-info.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/package-info.java new file mode 100644 index 0000000..2aa1cd3 --- /dev/null +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v1/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ +@NullMarked +package org.apache.logging.converter.config.internal.v1; + +import org.jspecify.annotations.NullMarked; diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v2/AbstractJacksonConfigurationMapper.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v2/AbstractJacksonConfigurationMapper.java index d9708f3..dd4b488 100644 --- a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v2/AbstractJacksonConfigurationMapper.java +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v2/AbstractJacksonConfigurationMapper.java @@ -90,7 +90,7 @@ private static ConfigurationNode parseObjectNode(ObjectNode objectNode, String f builder.addAttribute(childFieldName, childNode.asText()); } }); - return builder.setPluginName(getPluginName(objectNode, fieldName)).build(); + return builder.setPluginName(getPluginName(objectNode, fieldName)).get(); } private static void processArrayNode(ArrayNode arrayNode, String fieldName, ConfigurationNodeBuilder builder) { diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v2/PropertiesV2ConfigurationParser.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v2/PropertiesV2ConfigurationParser.java index c46595c..e1ef672 100644 --- a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v2/PropertiesV2ConfigurationParser.java +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v2/PropertiesV2ConfigurationParser.java @@ -123,9 +123,9 @@ public ConfigurationNode parse(InputStream inputStream) throws IOException { } } // Add the `Loggers` plugin - builder.addChild(loggersBuilder.build()); + builder.addChild(loggersBuilder.get()); - return builder.build(); + return builder.get(); } private static Map extractSubsetAndPartition(Properties rootProperties, String prefix) { @@ -150,13 +150,13 @@ private static Map extractSubsetAndPartition( private static ConfigurationNode processPropertyPlaceholders(final Properties propertyPlaceholders) { ConfigurationNodeBuilder builder = newNodeBuilder().setPluginName(PROPERTIES_PLUGIN_NAME); for (final String key : propertyPlaceholders.stringPropertyNames()) { - builder.addChild(newNodeBuilder() + ConfigurationNodeBuilder builder1 = newNodeBuilder() .setPluginName(PROPERTY_PLUGIN_NAME) .addAttribute(NAME_ATTRIBUTE, key) - .addAttribute(VALUE_ATTRIBUTE, propertyPlaceholders.getProperty(key)) - .build()); + .addAttribute(VALUE_ATTRIBUTE, propertyPlaceholders.getProperty(key)); + builder.addChild(builder1.get()); } - return builder.build(); + return builder.get(); } private ConfigurationNode processScripts(Map scripts) { @@ -166,20 +166,20 @@ private ConfigurationNode processScripts(Map scripts) { Properties scriptProperties = entry.getValue(); builder.addChild(processGenericComponent(scriptPrefix, "Script", scriptProperties)); } - return builder.build(); + return builder.get(); } private ConfigurationNode processCustomLevels(Properties customLevels) { ConfigurationNodeBuilder builder = newNodeBuilder().setPluginName(CUSTOM_LEVELS_PLUGIN_NAME); for (final String key : customLevels.stringPropertyNames()) { String value = validateInteger("customLevel." + key, customLevels.getProperty(key)); - builder.addChild(newNodeBuilder() + ConfigurationNodeBuilder builder1 = newNodeBuilder() .setPluginName(CUSTOM_LEVEL_PLUGIN_NAME) .addAttribute(NAME_ATTRIBUTE, key) - .addAttribute(INT_LEVEL_ATTRIBUTE, value) - .build()); + .addAttribute(INT_LEVEL_ATTRIBUTE, value); + builder.addChild(builder1.get()); } - return builder.build(); + return builder.get(); } private static String validateInteger(String key, String value) { @@ -199,7 +199,7 @@ private static ConfigurationNode processFilters(String prefix, Map filterEntry : filters.entrySet()) { builder.addChild(processFilter(prefix, filterEntry)); } - return builder.build(); + return builder.get(); } private static ConfigurationNode processFilter(String prefix, Map.Entry filterEntry) { @@ -214,7 +214,7 @@ private static ConfigurationNode processAppenders(Map append for (Map.Entry entry : appenders.entrySet()) { builder.addChild(processAppender(entry.getKey(), entry.getValue())); } - return builder.build(); + return builder.get(); } private static ConfigurationNode processAppender(String key, Properties properties) { @@ -232,7 +232,7 @@ private static ConfigurationNode processAppender(String key, Properties properti addFiltersToComponent(appenderPrefix, properties, builder); processRemainingProperties(appenderPrefix, properties, builder); - return builder.build(); + return builder.get(); } private static ConfigurationNode processLogger(String key, Properties properties) { @@ -257,7 +257,7 @@ private static ConfigurationNode processLogger(String key, Properties properties addFiltersToComponent(prefix, properties, builder); processRemainingProperties(prefix, properties, builder); - return builder.build(); + return builder.get(); } private static void addAppenderRefsToComponent( @@ -286,7 +286,7 @@ private static ConfigurationNode processAppenderRef(String prefix, Properties pr addFiltersToComponent(prefix, properties, builder); processRemainingProperties(prefix, properties, builder); - return builder.build(); + return builder.get(); } private static void addFiltersToComponent(String prefix, Properties properties, ConfigurationNodeBuilder builder) { @@ -314,7 +314,7 @@ private static ConfigurationNode processRootLogger(Properties properties) { addFiltersToComponent(prefix, properties, builder); processRemainingProperties(prefix, properties, builder); - return builder.build(); + return builder.get(); } private static @Nullable String remove(final Properties properties, final String key) { @@ -340,7 +340,7 @@ private static ConfigurationNode processGenericComponent( TYPE_ATTRIBUTE, () -> "No type attribute provided for " + componentCategory + " " + prefix)); processRemainingProperties(prefix, properties, builder); - return builder.build(); + return builder.get(); } private static void processRemainingProperties( diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v2/XmlConfigurationMapper.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v2/XmlConfigurationMapper.java index bef005c..8f87c86 100644 --- a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v2/XmlConfigurationMapper.java +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/internal/v2/XmlConfigurationMapper.java @@ -134,7 +134,7 @@ private static ConfigurationNode parseComplexElement(Element element) { // Handle child attributes NamedNodeMap nodeMap = element.getAttributes(); processAttributes(nodeMap, builder); - return builder.setPluginName(element.getTagName()).build(); + return builder.setPluginName(element.getTagName()).get(); } /** diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/spi/v1/Log4j1ComponentParser.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/spi/v1/Log4j1ComponentParser.java new file mode 100644 index 0000000..a3c544c --- /dev/null +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/spi/v1/Log4j1ComponentParser.java @@ -0,0 +1,47 @@ +/* + * 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.converter.config.spi.v1; + +import org.apache.logging.converter.config.ConfigurationConverterException; +import org.apache.logging.converter.config.spi.ConfigurationNode; +import org.w3c.dom.Element; + +public interface Log4j1ComponentParser { + + String getClassName(); + + /** + * Parses an XML element of a Log4j 1 configuration file. + * + * @param element An XML element. + * @param context A Log4j 1 parser context. + * @return A Log4j Core 2 configuration node. + * @throws ConfigurationConverterException If a parsing exception occurs. + */ + ConfigurationNode parseXml(Element element, Log4j1ParserContext context) throws ConfigurationConverterException; + + /** + * Parser a subset of configuration properties. + * + * @param properties A subset of configuration properties. + * @param context A Log4j 1 parser context. + * @return A Log4j Core 2 configuration node. + * @throws ConfigurationConverterException If a parsing exception occurs. + */ + ConfigurationNode parseProperties(PropertiesSubset properties, Log4j1ParserContext context) + throws ConfigurationConverterException; +} diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/spi/v1/Log4j1ParserContext.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/spi/v1/Log4j1ParserContext.java new file mode 100644 index 0000000..64e42d2 --- /dev/null +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/spi/v1/Log4j1ParserContext.java @@ -0,0 +1,25 @@ +/* + * 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.converter.config.spi.v1; + +/** + * Provides access to other {@link Log4j1ComponentParser} implementations. + */ +public interface Log4j1ParserContext { + + Log4j1ComponentParser getParserForClass(String className); +} diff --git a/log4j-converter-config/src/main/java/org/apache/logging/converter/config/spi/v1/PropertiesSubset.java b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/spi/v1/PropertiesSubset.java new file mode 100644 index 0000000..64d823c --- /dev/null +++ b/log4j-converter-config/src/main/java/org/apache/logging/converter/config/spi/v1/PropertiesSubset.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.converter.config.spi.v1; + +import java.util.AbstractMap; +import java.util.Map; +import java.util.Properties; + +/** + * Represents a subset of configuration properties. + */ +public final class PropertiesSubset { + + private final Map.Entry entry; + + public static PropertiesSubset of(final String prefix, final Properties properties) { + return new PropertiesSubset(prefix, properties); + } + + private PropertiesSubset(final String prefix, final Properties properties) { + this.entry = new AbstractMap.SimpleImmutableEntry<>(prefix, properties); + } + + /** + * The common prefix of this subset in the global configuration properties. + *

+ * Used for debugging messages. + *

+ */ + public String getPrefix() { + return entry.getKey(); + } + + /** + * A subset of the global configuration properties with {@link #getPrefix()} removed. + */ + public Properties getProperties() { + return entry.getValue(); + } +} diff --git a/log4j-converter-config/src/test/java/org/apache/logging/converter/config/ConfigurationConverterTest.java b/log4j-converter-config/src/test/java/org/apache/logging/converter/config/ConfigurationConverterTest.java index 749ff0a..eadf189 100644 --- a/log4j-converter-config/src/test/java/org/apache/logging/converter/config/ConfigurationConverterTest.java +++ b/log4j-converter-config/src/test/java/org/apache/logging/converter/config/ConfigurationConverterTest.java @@ -67,7 +67,14 @@ void conversionToXml(String inputResource, String inputFormat) throws IOExceptio @Test void supportedFormats() { assertThat(converter.getSupportedInputFormats()) - .containsExactlyInAnyOrder("v2:json", "v2:properties", DEFAULT_FORMAT, "v2:yaml", "v3:properties"); + .containsExactlyInAnyOrder( + "v1:properties", + "v1:xml", + "v2:json", + "v2:properties", + DEFAULT_FORMAT, + "v2:yaml", + "v3:properties"); assertThat(converter.getSupportedOutputFormats()) .containsExactlyInAnyOrder("v2:json", DEFAULT_FORMAT, "v2:yaml", "v3:properties"); } diff --git a/log4j-converter-config/src/test/java/org/apache/logging/converter/config/internal/AbstractConfigurationMapperTest.java b/log4j-converter-config/src/test/java/org/apache/logging/converter/config/internal/AbstractConfigurationMapperTest.java index 3317ef2..73d81b9 100644 --- a/log4j-converter-config/src/test/java/org/apache/logging/converter/config/internal/AbstractConfigurationMapperTest.java +++ b/log4j-converter-config/src/test/java/org/apache/logging/converter/config/internal/AbstractConfigurationMapperTest.java @@ -40,16 +40,16 @@ public abstract class AbstractConfigurationMapperTest { .setPluginName("Property") .addAttribute("name", "pattern") .addAttribute("value", "%d [%t] %-5p %c - %m%n%ex") - .build()) - .build()) + .get()) + .get()) .addChild(newNodeBuilder() .setPluginName("CustomLevels") .addChild(newNodeBuilder() .setPluginName("CustomLevel") .addAttribute("name", "CONFIG") .addAttribute("intLevel", "450") - .build()) - .build()) + .get()) + .get()) .addChild(newNodeBuilder() .setPluginName("Appenders") .addChild(newNodeBuilder() @@ -58,8 +58,8 @@ public abstract class AbstractConfigurationMapperTest { .addAttribute("fileName", "main.log") .addChild(newNodeBuilder() .setPluginName("JsonTemplateLayout") - .build()) - .build()) + .get()) + .get()) .addChild(newNodeBuilder() .setPluginName("File") .addAttribute("name", "AUDIT") @@ -67,12 +67,12 @@ public abstract class AbstractConfigurationMapperTest { .addChild(newNodeBuilder() .setPluginName("MarkerFilter") .addAttribute("marker", "AUDIT") - .build()) + .get()) .addChild(newNodeBuilder() .setPluginName("PatternLayout") .addAttribute("pattern", "${pattern}") - .build()) - .build()) + .get()) + .get()) .addChild(newNodeBuilder() .setPluginName("Console") .addAttribute("name", "CONSOLE") @@ -83,24 +83,24 @@ public abstract class AbstractConfigurationMapperTest { .addAttribute("level", "WARN") .addAttribute("onMatch", "ACCEPT") .addAttribute("onMismatch", "NEUTRAL") - .build()) + .get()) .addChild(newNodeBuilder() .setPluginName("BurstFilter") - .build()) - .build()) + .get()) + .get()) .addChild(newNodeBuilder() .setPluginName("JsonTemplateLayout") - .build()) - .build()) + .get()) + .get()) .addChild(newNodeBuilder() .setPluginName("File") .addAttribute("name", "DEBUG_LOG") .addAttribute("fileName", "debug.log") .addChild(newNodeBuilder() .setPluginName("JsonTemplateLayout") - .build()) - .build()) - .build()) + .get()) + .get()) + .get()) .addChild(newNodeBuilder() .setPluginName("Loggers") .addChild(newNodeBuilder() @@ -112,8 +112,8 @@ public abstract class AbstractConfigurationMapperTest { .addChild(newNodeBuilder() .setPluginName("MarkerFilter") .addAttribute("marker", "PRIVATE") - .build()) - .build()) + .get()) + .get()) .addChild(newNodeBuilder() .setPluginName("AppenderRef") .addAttribute("ref", "CONSOLE") @@ -124,16 +124,16 @@ public abstract class AbstractConfigurationMapperTest { .addAttribute("level", "WARN") .addAttribute("onMatch", "ACCEPT") .addAttribute("onMismatch", "NEUTRAL") - .build()) + .get()) .addChild(newNodeBuilder() .setPluginName("BurstFilter") - .build()) - .build()) - .build()) + .get()) + .get()) + .get()) .addChild(newNodeBuilder() .setPluginName("BurstFilter") - .build()) - .build()) + .get()) + .get()) .addChild(newNodeBuilder() .setPluginName("Logger") .addAttribute("name", "org.apache.logging") @@ -142,11 +142,11 @@ public abstract class AbstractConfigurationMapperTest { .addChild(newNodeBuilder() .setPluginName("AppenderRef") .addAttribute("ref", "AUDIT") - .build()) + .get()) .addChild(newNodeBuilder() .setPluginName("AppenderRef") .addAttribute("ref", "DEBUG_LOG") - .build()) + .get()) .addChild(newNodeBuilder() .setPluginName("Filters") .addChild(newNodeBuilder() @@ -154,15 +154,15 @@ public abstract class AbstractConfigurationMapperTest { .addAttribute("level", "DEBUG") .addAttribute("onMatch", "ACCEPT") .addAttribute("onMismatch", "NEUTRAL") - .build()) + .get()) .addChild(newNodeBuilder() .setPluginName("BurstFilter") .addAttribute("level", "TRACE") - .build()) - .build()) - .build()) - .build()) - .build(); + .get()) + .get()) + .get()) + .get()) + .get(); public static ConfigurationNodeAssert assertThat(ConfigurationNode node) { return new ConfigurationNodeAssert(node, false); diff --git a/log4j-converter-config/src/test/java/org/apache/logging/converter/config/internal/v1/AbstractV1ConfigurationParserTest.java b/log4j-converter-config/src/test/java/org/apache/logging/converter/config/internal/v1/AbstractV1ConfigurationParserTest.java new file mode 100644 index 0000000..071f8d8 --- /dev/null +++ b/log4j-converter-config/src/test/java/org/apache/logging/converter/config/internal/v1/AbstractV1ConfigurationParserTest.java @@ -0,0 +1,229 @@ +/* + * 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.converter.config.internal.v1; + +import static org.apache.logging.converter.config.internal.ComponentUtils.newNodeBuilder; +import static org.apache.logging.converter.config.internal.ComponentUtils.newThresholdFilter; + +import java.util.Arrays; +import org.apache.logging.converter.config.internal.AbstractConfigurationMapperTest; +import org.apache.logging.converter.config.internal.ComponentUtils; +import org.apache.logging.converter.config.internal.ComponentUtils.ConfigurationNodeBuilder; +import org.apache.logging.converter.config.spi.ConfigurationNode; + +public class AbstractV1ConfigurationParserTest extends AbstractConfigurationMapperTest { + + static ConfigurationNode EXAMPLE_V1_CONFIGURATION = newNodeBuilder() + .setPluginName("Configuration") + .addChild(newNodeBuilder() + .setPluginName("Properties") + .addChild(newNodeBuilder() + .setPluginName("Property") + .addAttribute("name", "foo") + .addAttribute("value", "bar") + .get()) + .addChild(newNodeBuilder() + .setPluginName("Property") + .addAttribute("name", "log4j.foo") + .addAttribute("value", "baz") + .get()) + .get()) + .addChild(newNodeBuilder() + .setPluginName("Appenders") + .addChild(newNodeBuilder() + .setPluginName("Async") + .addAttribute("name", "ASYNC") + .addAttribute("blocking", true) + .addAttribute("bufferSize", 512) + .addAttribute("includeLocation", true) + .addChild(newAppenderRef("CONSOLE")) + .addChild(newAppenderRef("DAILY_ROLLING")) + .addChild(newAppenderRef("FILE")) + .addChild(newAppenderRef("ROLLING")) + .get()) + .addChild(newNodeBuilder() + .setPluginName("Console") + .addAttribute("name", "CONSOLE") + .addAttribute("follow", "true") + .addAttribute("immediateFlush", "false") + .addAttribute("target", "SYSTEM_ERR") + .addChild(newThresholdFilter("WARN")) + .addChild(newSimpleLayout()) + .get()) + .addChild(newNodeBuilder() + .setPluginName("RollingFile") + .addAttribute("name", "DAILY_ROLLING") + .addAttribute("append", false) + .addAttribute("bufferSize", 1024) + .addAttribute("bufferedIo", true) + .addAttribute("fileName", "file.log") + .addAttribute("filePattern", "file.log%d{.yyyy_MM_dd}") + .addAttribute("immediateFlush", false) + .addChild(newThresholdFilter("WARN")) + .addChild(newSimpleLayout()) + .addChild(newNodeBuilder() + .setPluginName("TimeBasedTriggeringPolicy") + .addAttribute("modulate", true) + .get()) + .get()) + .addChild(newNodeBuilder() + .setPluginName("File") + .addAttribute("name", "FILE") + .addAttribute("append", false) + .addAttribute("bufferSize", 1024) + .addAttribute("bufferedIo", true) + .addAttribute("fileName", "file.log") + .addAttribute("immediateFlush", false) + .addChild(newThresholdFilter("WARN")) + .addChild(newSimpleLayout()) + .get()) + .addChild(newNodeBuilder() + .setPluginName("RollingFile") + .addAttribute("name", "ROLLING") + .addAttribute("append", false) + .addAttribute("bufferSize", 1024) + .addAttribute("bufferedIo", true) + .addAttribute("fileName", "file.log") + .addAttribute("filePattern", "file.log.%i") + .addAttribute("immediateFlush", false) + .addChild(newThresholdFilter("WARN")) + .addChild(newSimpleLayout()) + .addChild(newNodeBuilder() + .setPluginName("SizeBasedTriggeringPolicy") + .addAttribute("size", "10.00 GB") + .get()) + .addChild(newNodeBuilder() + .setPluginName("DefaultRolloverStrategy") + .addAttribute("fileIndex", "min") + .addAttribute("max", "30") + .get()) + .get()) + .addChild(newConsoleAppenderBuilder("FILTERS") + .addChild(newCompositeFilter( + newNodeBuilder() + .setPluginName("DenyAllFilter") + .get(), + newNodeBuilder() + .setPluginName("LevelMatchFilter") + .addAttribute("onMatch", "ACCEPT") + .addAttribute("onMismatch", "NEUTRAL") + .addAttribute("level", "WARN") + .get(), + newNodeBuilder() + .setPluginName("LevelRangeFilter") + .addAttribute("onMatch", "ACCEPT") + .addAttribute("onMismatch", "NEUTRAL") + .addAttribute("minLevel", "INFO") + .addAttribute("maxLevel", "DEBUG") + .get(), + newNodeBuilder() + .setPluginName("StringMatchFilter") + .addAttribute("onMatch", "ACCEPT") + .addAttribute("onMismatch", "NEUTRAL") + .addAttribute("text", "Hello") + .get())) + .addChild(newSimpleLayout()) + .get()) + .addChild(newConsoleAppenderBuilder("HTML") + .addChild(newNodeBuilder() + .setPluginName("HtmlLayout") + .addAttribute("locationInfo", true) + .addAttribute("title", "Example HTML Layout") + .get()) + .get()) + .addChild(newConsoleAppenderBuilder("PATTERN") + .addChild(newNodeBuilder() + .setPluginName("PatternLayout") + .addAttribute("pattern", "%d [%t] %-5p %c - %m%n%ex") + .get()) + .get()) + .addChild(newConsoleAppenderBuilder("EPATTERN") + .addChild(newNodeBuilder() + .setPluginName("PatternLayout") + .addAttribute("pattern", "%d [%t] %-5p %c - %m%n%ex") + .get()) + .get()) + .addChild(newConsoleAppenderBuilder("SIMPLE") + .addAttribute("name", "SIMPLE") + .addChild(newSimpleLayout()) + .get()) + .addChild(newConsoleAppenderBuilder("TTCC") + .addChild(newNodeBuilder() + .setPluginName("PatternLayout") + .addAttribute( + "pattern", + "%d{yyyy-MM-dd HH:mm:ss,SSS}{UTC} [%t] %p %c %notEmpty{%NDC }- %m%n") + .get()) + .get()) + .get()) + .addChild(newThresholdFilter("INFO")) + .addChild(newNodeBuilder() + .setPluginName("Loggers") + .addChild(newNodeBuilder() + .setPluginName("Root") + .addAttribute("level", "INFO") + .addChild(newAppenderRef("CONSOLE")) + .get()) + .addChild(newNodeBuilder() + .setPluginName("Logger") + .addAttribute("additivity", "false") + .addAttribute("level", "DEBUG") + .addAttribute("name", "org.apache.logging") + .addChild(newAppenderRef("CONSOLE")) + .addChild(newAppenderRef("DAILY_ROLLING")) + .addChild(newAppenderRef("FILE")) + .addChild(newAppenderRef("ROLLING")) + .get()) + .get()) + .get(); + + static ConfigurationNode filterOutPlugin(ConfigurationNode node, String pluginName) { + ConfigurationNodeBuilder builder = newNodeBuilder().setPluginName(node.getPluginName()); + node.getAttributes().forEach(builder::addAttribute); + for (ConfigurationNode child : node.getChildren()) { + if (!pluginName.equals(child.getPluginName())) { + builder.addChild(filterOutPlugin(child, pluginName)); + } + } + return builder.get(); + } + + private static ConfigurationNode newSimpleLayout() { + return newNodeBuilder() + .setPluginName("PatternLayout") + .addAttribute("alwaysWriteExceptions", false) + .addAttribute("pattern", "%p - %m%n") + .get(); + } + + private static ConfigurationNode newAppenderRef(String ref) { + return ComponentUtils.newAppenderRef(ref); + } + + private static ConfigurationNode newCompositeFilter(ConfigurationNode... filters) { + return ComponentUtils.newCompositeFilter(Arrays.asList(filters)); + } + + private static ConfigurationNodeBuilder newConsoleAppenderBuilder(String name) { + return newNodeBuilder() + .setPluginName("Console") + .addAttribute("name", name) + .addAttribute("follow", false) + .addAttribute("immediateFlush", true) + .addAttribute("target", "SYSTEM_OUT"); + } +} diff --git a/log4j-converter-config/src/test/java/org/apache/logging/converter/config/internal/v1/PropertiesV1ConfigurationParserTest.java b/log4j-converter-config/src/test/java/org/apache/logging/converter/config/internal/v1/PropertiesV1ConfigurationParserTest.java new file mode 100644 index 0000000..83bccaa --- /dev/null +++ b/log4j-converter-config/src/test/java/org/apache/logging/converter/config/internal/v1/PropertiesV1ConfigurationParserTest.java @@ -0,0 +1,38 @@ +/* + * 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.converter.config.internal.v1; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import org.apache.logging.converter.config.spi.ConfigurationNode; +import org.apache.logging.converter.config.spi.ConfigurationParser; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class PropertiesV1ConfigurationParserTest extends AbstractV1ConfigurationParserTest { + + @ParameterizedTest + @ValueSource(strings = {"/v1/log4j-uppercase.properties", "/v1/log4j-lowercase.properties"}) + void convertFromProperties(String resource) throws IOException { + ConfigurationParser parser = new PropertiesV1ConfigurationParser(); + try (InputStream inputStream = getClass().getResourceAsStream(resource)) { + ConfigurationNode actual = parser.parse(Objects.requireNonNull(inputStream)); + assertThat(actual).ignoringOrder().isEqualTo(filterOutPlugin(EXAMPLE_V1_CONFIGURATION, "Async")); + } + } +} diff --git a/log4j-converter-config/src/test/java/org/apache/logging/converter/config/internal/v1/XmlV1ConfigurationParserTest.java b/log4j-converter-config/src/test/java/org/apache/logging/converter/config/internal/v1/XmlV1ConfigurationParserTest.java new file mode 100644 index 0000000..82bbfce --- /dev/null +++ b/log4j-converter-config/src/test/java/org/apache/logging/converter/config/internal/v1/XmlV1ConfigurationParserTest.java @@ -0,0 +1,38 @@ +/* + * 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.converter.config.internal.v1; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import org.apache.logging.converter.config.spi.ConfigurationNode; +import org.apache.logging.converter.config.spi.ConfigurationParser; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class XmlV1ConfigurationParserTest extends AbstractV1ConfigurationParserTest { + + @ParameterizedTest + @ValueSource(strings = {"/v1/log4j-uppercase.xml", "/v1/log4j-lowercase.xml"}) + void convertFromXml(String resource) throws IOException { + ConfigurationParser parser = new XmlV1ConfigurationParser(); + try (InputStream inputStream = getClass().getResourceAsStream(resource)) { + ConfigurationNode actual = parser.parse(Objects.requireNonNull(inputStream)); + assertThat(actual).isEqualTo(filterOutPlugin(EXAMPLE_V1_CONFIGURATION, "Properties")); + } + } +} diff --git a/log4j-converter-config/src/test/resources/v1/log4j-lowercase.properties b/log4j-converter-config/src/test/resources/v1/log4j-lowercase.properties new file mode 100644 index 0000000..6448ef0 --- /dev/null +++ b/log4j-converter-config/src/test/resources/v1/log4j-lowercase.properties @@ -0,0 +1,118 @@ +# +# 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. +# +## +## +# ThresholdFilter +log4j.threshold = INFO + +## +# Properties +foo = bar +log4j.foo = baz + +## +# Appenders +log4j.appender.CONSOLE = org.apache.log4j.ConsoleAppender +log4j.appender.CONSOLE.follow = true +log4j.appender.CONSOLE.immediateFlush = false +log4j.appender.CONSOLE.target = System.err +log4j.appender.CONSOLE.threshold = WARN +log4j.appender.CONSOLE.layout = org.apache.log4j.SimpleLayout + +log4j.appender.DAILY_ROLLING = org.apache.log4j.DailyRollingFileAppender +log4j.appender.DAILY_ROLLING.append = false +log4j.appender.DAILY_ROLLING.bufferedIO = true +log4j.appender.DAILY_ROLLING.bufferSize = 1024 +log4j.appender.DAILY_ROLLING.datePattern = .yyyy_MM_dd +log4j.appender.DAILY_ROLLING.file = file.log +log4j.appender.DAILY_ROLLING.immediateFlush = false +log4j.appender.DAILY_ROLLING.threshold = WARN +log4j.appender.DAILY_ROLLING.layout = org.apache.log4j.SimpleLayout + +log4j.appender.FILE = org.apache.log4j.FileAppender +log4j.appender.FILE.append = false +log4j.appender.FILE.bufferedIO = true +log4j.appender.FILE.bufferSize = 1024 +log4j.appender.FILE.file = file.log +log4j.appender.FILE.immediateFlush = false +log4j.appender.FILE.threshold = WARN +log4j.appender.FILE.layout = org.apache.log4j.SimpleLayout + +log4j.appender.ROLLING = org.apache.log4j.RollingFileAppender +log4j.appender.ROLLING.append = false +log4j.appender.ROLLING.bufferedIO = true +log4j.appender.ROLLING.bufferSize = 1024 +log4j.appender.ROLLING.file = file.log +log4j.appender.ROLLING.immediateFlush = false +log4j.appender.ROLLING.maxBackupIndex = 30 +# Exactly 10 GiB +log4j.appender.ROLLING.maxFileSize = 10737418240 +log4j.appender.ROLLING.threshold = WARN +log4j.appender.ROLLING.layout = org.apache.log4j.SimpleLayout + +## +# Filters +log4j.appender.FILTERS = org.apache.log4j.ConsoleAppender +log4j.appender.FILTERS.layout = org.apache.log4j.SimpleLayout + +log4j.appender.FILTERS.filter.f1 = org.apache.log4j.varia.DenyAllFilter + +log4j.appender.FILTERS.filter.f2 = org.apache.log4j.varia.LevelMatchFilter +log4j.appender.FILTERS.filter.f2.acceptOnMatch = true +log4j.appender.FILTERS.filter.f2.levelToMatch = WARN + +log4j.appender.FILTERS.filter.f3 = org.apache.log4j.varia.LevelRangeFilter +log4j.appender.FILTERS.filter.f3.acceptOnMatch = true +log4j.appender.FILTERS.filter.f3.levelMin = DEBUG +log4j.appender.FILTERS.filter.f3.levelMax = INFO + +log4j.appender.FILTERS.filter.f4 = org.apache.log4j.varia.StringMatchFilter +log4j.appender.FILTERS.filter.f4.acceptOnMatch = true +log4j.appender.FILTERS.filter.f4.stringToMatch = Hello + +## +# Layouts +log4j.appender.HTML = org.apache.log4j.ConsoleAppender +log4j.appender.HTML.layout = org.apache.log4j.HTMLLayout +log4j.appender.HTML.layout.locationInfo = true +log4j.appender.HTML.layout.title = Example HTML Layout + +log4j.appender.PATTERN = org.apache.log4j.ConsoleAppender +log4j.appender.PATTERN.layout = org.apache.log4j.PatternLayout +log4j.appender.PATTERN.layout.conversionPattern = %d [%t] %-5p %c - %m%n%ex + +log4j.appender.EPATTERN = org.apache.log4j.ConsoleAppender +log4j.appender.EPATTERN.layout = org.apache.log4j.EnhancedPatternLayout +log4j.appender.EPATTERN.layout.conversionPattern = %d [%t] %-5p %c - %m%n%ex + +log4j.appender.SIMPLE = org.apache.log4j.ConsoleAppender +log4j.appender.SIMPLE.layout = org.apache.log4j.SimpleLayout + +log4j.appender.TTCC = org.apache.log4j.ConsoleAppender +log4j.appender.TTCC.layout = org.apache.log4j.TTCCLayout +log4j.appender.TTCC.layout.categoryPrefixing = true +log4j.appender.TTCC.layout.contextPrinting = true +log4j.appender.TTCC.layout.dateFormat = ISO8601 +log4j.appender.TTCC.layout.threadPrinting = true +log4j.appender.TTCC.layout.timeZone = UTC + +## +# Loggers +log4j.rootLogger = INFO, CONSOLE + +log4j.logger.org.apache.logging = DEBUG, CONSOLE, DAILY_ROLLING, FILE, ROLLING +log4j.additivity.org.apache.logging = false diff --git a/log4j-converter-config/src/test/resources/v1/log4j-lowercase.xml b/log4j-converter-config/src/test/resources/v1/log4j-lowercase.xml new file mode 100644 index 0000000..385cee4 --- /dev/null +++ b/log4j-converter-config/src/test/resources/v1/log4j-lowercase.xml @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/log4j-converter-config/src/test/resources/v1/log4j-uppercase.properties b/log4j-converter-config/src/test/resources/v1/log4j-uppercase.properties new file mode 100644 index 0000000..66bc9e0 --- /dev/null +++ b/log4j-converter-config/src/test/resources/v1/log4j-uppercase.properties @@ -0,0 +1,118 @@ +# +# 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. +# +## +## +# ThresholdFilter +log4j.threshold = INFO + +## +# Properties +foo = bar +log4j.foo = baz + +## +# Appenders +log4j.appender.CONSOLE = org.apache.log4j.ConsoleAppender +log4j.appender.CONSOLE.Follow = true +log4j.appender.CONSOLE.ImmediateFlush = false +log4j.appender.CONSOLE.Target = System.err +log4j.appender.CONSOLE.Threshold = WARN +log4j.appender.CONSOLE.layout = org.apache.log4j.SimpleLayout + +log4j.appender.DAILY_ROLLING = org.apache.log4j.DailyRollingFileAppender +log4j.appender.DAILY_ROLLING.Append = false +log4j.appender.DAILY_ROLLING.BufferedIO = true +log4j.appender.DAILY_ROLLING.BufferSize = 1024 +log4j.appender.DAILY_ROLLING.DatePattern = .yyyy_MM_dd +log4j.appender.DAILY_ROLLING.File = file.log +log4j.appender.DAILY_ROLLING.ImmediateFlush = false +log4j.appender.DAILY_ROLLING.Threshold = WARN +log4j.appender.DAILY_ROLLING.layout = org.apache.log4j.SimpleLayout + +log4j.appender.FILE = org.apache.log4j.FileAppender +log4j.appender.FILE.Append = false +log4j.appender.FILE.BufferedIO = true +log4j.appender.FILE.BufferSize = 1024 +log4j.appender.FILE.File = file.log +log4j.appender.FILE.ImmediateFlush = false +log4j.appender.FILE.Threshold = WARN +log4j.appender.FILE.layout = org.apache.log4j.SimpleLayout + +log4j.appender.ROLLING = org.apache.log4j.RollingFileAppender +log4j.appender.ROLLING.Append = false +log4j.appender.ROLLING.BufferedIO = true +log4j.appender.ROLLING.BufferSize = 1024 +log4j.appender.ROLLING.File = file.log +log4j.appender.ROLLING.ImmediateFlush = false +log4j.appender.ROLLING.MaxBackupIndex = 30 +# Exactly 10 GiB +log4j.appender.ROLLING.MaxFileSize = 10737418240 +log4j.appender.ROLLING.Threshold = WARN +log4j.appender.ROLLING.layout = org.apache.log4j.SimpleLayout + +## +# Filters +log4j.appender.FILTERS = org.apache.log4j.ConsoleAppender +log4j.appender.FILTERS.layout = org.apache.log4j.SimpleLayout + +log4j.appender.FILTERS.filter.f1 = org.apache.log4j.varia.DenyAllFilter + +log4j.appender.FILTERS.filter.f2 = org.apache.log4j.varia.LevelMatchFilter +log4j.appender.FILTERS.filter.f2.AcceptOnMatch = true +log4j.appender.FILTERS.filter.f2.LevelToMatch = WARN + +log4j.appender.FILTERS.filter.f3 = org.apache.log4j.varia.LevelRangeFilter +log4j.appender.FILTERS.filter.f3.AcceptOnMatch = true +log4j.appender.FILTERS.filter.f3.LevelMin = DEBUG +log4j.appender.FILTERS.filter.f3.LevelMax = INFO + +log4j.appender.FILTERS.filter.f4 = org.apache.log4j.varia.StringMatchFilter +log4j.appender.FILTERS.filter.f4.AcceptOnMatch = true +log4j.appender.FILTERS.filter.f4.StringToMatch = Hello + +## +# Layouts +log4j.appender.HTML = org.apache.log4j.ConsoleAppender +log4j.appender.HTML.layout = org.apache.log4j.HTMLLayout +log4j.appender.HTML.layout.LocationInfo = true +log4j.appender.HTML.layout.Title = Example HTML Layout + +log4j.appender.PATTERN = org.apache.log4j.ConsoleAppender +log4j.appender.PATTERN.layout = org.apache.log4j.PatternLayout +log4j.appender.PATTERN.layout.ConversionPattern = %d [%t] %-5p %c - %m%n%ex + +log4j.appender.EPATTERN = org.apache.log4j.ConsoleAppender +log4j.appender.EPATTERN.layout = org.apache.log4j.EnhancedPatternLayout +log4j.appender.EPATTERN.layout.ConversionPattern = %d [%t] %-5p %c - %m%n%ex + +log4j.appender.SIMPLE = org.apache.log4j.ConsoleAppender +log4j.appender.SIMPLE.layout = org.apache.log4j.SimpleLayout + +log4j.appender.TTCC = org.apache.log4j.ConsoleAppender +log4j.appender.TTCC.layout = org.apache.log4j.TTCCLayout +log4j.appender.TTCC.layout.CategoryPrefixing = true +log4j.appender.TTCC.layout.ContextPrinting = true +log4j.appender.TTCC.layout.DateFormat = ISO8601 +log4j.appender.TTCC.layout.ThreadPrinting = true +log4j.appender.TTCC.layout.TimeZone = UTC + +## +# Loggers +log4j.rootLogger = INFO, CONSOLE + +log4j.logger.org.apache.logging = DEBUG, CONSOLE, DAILY_ROLLING, FILE, ROLLING +log4j.additivity.org.apache.logging = false diff --git a/log4j-converter-config/src/test/resources/v1/log4j-uppercase.xml b/log4j-converter-config/src/test/resources/v1/log4j-uppercase.xml new file mode 100644 index 0000000..9b3447f --- /dev/null +++ b/log4j-converter-config/src/test/resources/v1/log4j-uppercase.xml @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/log4j-converter-config/src/test/resources/v1/log4j.dtd b/log4j-converter-config/src/test/resources/v1/log4j.dtd new file mode 100644 index 0000000..f37f04a --- /dev/null +++ b/log4j-converter-config/src/test/resources/v1/log4j.dtd @@ -0,0 +1,237 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/site/antora/modules/ROOT/pages/log4j-converter-config.adoc b/src/site/antora/modules/ROOT/pages/log4j-converter-config.adoc index f502670..48a16fd 100644 --- a/src/site/antora/modules/ROOT/pages/log4j-converter-config.adoc +++ b/src/site/antora/modules/ROOT/pages/log4j-converter-config.adoc @@ -78,6 +78,16 @@ The library provides an out-of-the-box support for the following configuration f |=== | Format name | Format id | Parsing support | Writing support +| {logging-services-url}/log4j/1.x/apidocs/org/apache/log4j/PropertyConfigurator.html[Log4j 1 Properties] +| v1:properties +| yes +| no + +| {logging-services-url}/log4j/1.x/apidocs/org/apache/log4j/xml/DOMConfigurator.html[Log4j 1 XML] +| v1:xml +| yes +| no + | {logging-services-url}/log4j/2.x/manual/configuration.html#xml[Log4j Core 2 XML] | v2:xml | yes