LOG4J2-2993 Support stack trace truncation in JsonTemplateLayout. (#458)
diff --git a/log4j-layout-template-json/revapi.json b/log4j-layout-template-json/revapi.json
index 20bd8ed..77f8cde 100644
--- a/log4j-layout-template-json/revapi.json
+++ b/log4j-layout-template-json/revapi.json
@@ -32,6 +32,408 @@
"code": "java.class.removed",
"old": "class org.apache.logging.log4j.layout.template.json.JsonTemplateLayout.EventTemplateAdditionalFields",
"justification": "LOG4J2-2961 Refactored for simplicity since it was already broken due to missing @PluginBuilderAttribute annotations"
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method java.lang.String org.apache.logging.log4j.layout.template.json.JsonTemplateLayoutDefaults::getMdcKeyPattern()",
+ "justification": "Removed unused property getter."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method java.lang.String org.apache.logging.log4j.layout.template.json.JsonTemplateLayoutDefaults::getNdcPattern()",
+ "justification": "Removed unused property getter."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method int java.lang.Enum<E extends java.lang.Enum<E extends java.lang.Enum<E>>>::compareTo(E) @ org.apache.logging.log4j.layout.template.json.JsonTemplateLayoutDefaults",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method java.lang.Class<E> java.lang.Enum<E extends java.lang.Enum<E extends java.lang.Enum<E>>>::getDeclaringClass() @ org.apache.logging.log4j.layout.template.json.JsonTemplateLayoutDefaults",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method java.lang.String java.lang.Enum<E extends java.lang.Enum<E extends java.lang.Enum<E>>>::name() @ org.apache.logging.log4j.layout.template.json.JsonTemplateLayoutDefaults",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method int java.lang.Enum<E extends java.lang.Enum<E extends java.lang.Enum<E>>>::ordinal() @ org.apache.logging.log4j.layout.template.json.JsonTemplateLayoutDefaults",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.class.noLongerImplementsInterface",
+ "old": "enum org.apache.logging.log4j.layout.template.json.util.Uris",
+ "new": "class org.apache.logging.log4j.layout.template.json.util.Uris",
+ "interface": "java.lang.Comparable<org.apache.logging.log4j.layout.template.json.util.Uris>",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.class.noLongerImplementsInterface",
+ "old": "enum org.apache.logging.log4j.layout.template.json.util.Uris",
+ "new": "class org.apache.logging.log4j.layout.template.json.util.Uris",
+ "interface": "java.io.Serializable",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method <T extends java.lang.Enum<T extends java.lang.Enum<T>>> T java.lang.Enum<E extends java.lang.Enum<E extends java.lang.Enum<E>>>::valueOf(java.lang.Class<T>, java.lang.String) @ org.apache.logging.log4j.layout.template.json.JsonTemplateLayoutDefaults",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method org.apache.logging.log4j.layout.template.json.JsonTemplateLayoutDefaults org.apache.logging.log4j.layout.template.json.JsonTemplateLayoutDefaults::valueOf(java.lang.String)",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method org.apache.logging.log4j.layout.template.json.JsonTemplateLayoutDefaults[] org.apache.logging.log4j.layout.template.json.JsonTemplateLayoutDefaults::values()",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.class.noLongerInheritsFromClass",
+ "old": "enum org.apache.logging.log4j.layout.template.json.JsonTemplateLayoutDefaults",
+ "new": "class org.apache.logging.log4j.layout.template.json.JsonTemplateLayoutDefaults",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.class.kindChanged",
+ "old": "enum org.apache.logging.log4j.layout.template.json.JsonTemplateLayoutDefaults",
+ "new": "class org.apache.logging.log4j.layout.template.json.JsonTemplateLayoutDefaults",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.class.noLongerImplementsInterface",
+ "old": "enum org.apache.logging.log4j.layout.template.json.JsonTemplateLayoutDefaults",
+ "new": "class org.apache.logging.log4j.layout.template.json.JsonTemplateLayoutDefaults",
+ "interface": "java.lang.Comparable<org.apache.logging.log4j.layout.template.json.JsonTemplateLayoutDefaults>",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.class.noLongerImplementsInterface",
+ "old": "enum org.apache.logging.log4j.layout.template.json.JsonTemplateLayoutDefaults",
+ "new": "class org.apache.logging.log4j.layout.template.json.JsonTemplateLayoutDefaults",
+ "interface": "java.io.Serializable",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method int java.lang.Enum<E extends java.lang.Enum<E extends java.lang.Enum<E>>>::compareTo(E) @ org.apache.logging.log4j.layout.template.json.resolver.TemplateResolvers",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method java.lang.Class<E> java.lang.Enum<E extends java.lang.Enum<E extends java.lang.Enum<E>>>::getDeclaringClass() @ org.apache.logging.log4j.layout.template.json.resolver.TemplateResolvers",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method java.lang.String java.lang.Enum<E extends java.lang.Enum<E extends java.lang.Enum<E>>>::name() @ org.apache.logging.log4j.layout.template.json.resolver.TemplateResolvers",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method int java.lang.Enum<E extends java.lang.Enum<E extends java.lang.Enum<E>>>::ordinal() @ org.apache.logging.log4j.layout.template.json.resolver.TemplateResolvers",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method <T extends java.lang.Enum<T extends java.lang.Enum<T>>> T java.lang.Enum<E extends java.lang.Enum<E extends java.lang.Enum<E>>>::valueOf(java.lang.Class<T>, java.lang.String) @ org.apache.logging.log4j.layout.template.json.resolver.TemplateResolvers",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method org.apache.logging.log4j.layout.template.json.resolver.TemplateResolvers org.apache.logging.log4j.layout.template.json.resolver.TemplateResolvers::valueOf(java.lang.String)",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method org.apache.logging.log4j.layout.template.json.resolver.TemplateResolvers[] org.apache.logging.log4j.layout.template.json.resolver.TemplateResolvers::values()",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.class.noLongerInheritsFromClass",
+ "old": "enum org.apache.logging.log4j.layout.template.json.resolver.TemplateResolvers",
+ "new": "class org.apache.logging.log4j.layout.template.json.resolver.TemplateResolvers",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.class.kindChanged",
+ "old": "enum org.apache.logging.log4j.layout.template.json.resolver.TemplateResolvers",
+ "new": "class org.apache.logging.log4j.layout.template.json.resolver.TemplateResolvers",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.class.noLongerImplementsInterface",
+ "old": "enum org.apache.logging.log4j.layout.template.json.resolver.TemplateResolvers",
+ "new": "class org.apache.logging.log4j.layout.template.json.resolver.TemplateResolvers",
+ "interface": "java.lang.Comparable<org.apache.logging.log4j.layout.template.json.resolver.TemplateResolvers>",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.class.noLongerImplementsInterface",
+ "old": "enum org.apache.logging.log4j.layout.template.json.resolver.TemplateResolvers",
+ "new": "class org.apache.logging.log4j.layout.template.json.resolver.TemplateResolvers",
+ "interface": "java.io.Serializable",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method int java.lang.Enum<E extends java.lang.Enum<E extends java.lang.Enum<E>>>::compareTo(E) @ org.apache.logging.log4j.layout.template.json.util.RecyclerFactories",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method java.lang.Class<E> java.lang.Enum<E extends java.lang.Enum<E extends java.lang.Enum<E>>>::getDeclaringClass() @ org.apache.logging.log4j.layout.template.json.util.RecyclerFactories",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method java.lang.String java.lang.Enum<E extends java.lang.Enum<E extends java.lang.Enum<E>>>::name() @ org.apache.logging.log4j.layout.template.json.util.RecyclerFactories",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method int java.lang.Enum<E extends java.lang.Enum<E extends java.lang.Enum<E>>>::ordinal() @ org.apache.logging.log4j.layout.template.json.util.RecyclerFactories",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method <T extends java.lang.Enum<T extends java.lang.Enum<T>>> T java.lang.Enum<E extends java.lang.Enum<E extends java.lang.Enum<E>>>::valueOf(java.lang.Class<T>, java.lang.String) @ org.apache.logging.log4j.layout.template.json.util.RecyclerFactories",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method org.apache.logging.log4j.layout.template.json.util.RecyclerFactories org.apache.logging.log4j.layout.template.json.util.RecyclerFactories::valueOf(java.lang.String)",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method org.apache.logging.log4j.layout.template.json.util.RecyclerFactories[] org.apache.logging.log4j.layout.template.json.util.RecyclerFactories::values()",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.class.noLongerInheritsFromClass",
+ "old": "enum org.apache.logging.log4j.layout.template.json.util.RecyclerFactories",
+ "new": "class org.apache.logging.log4j.layout.template.json.util.RecyclerFactories",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.class.kindChanged",
+ "old": "enum org.apache.logging.log4j.layout.template.json.util.RecyclerFactories",
+ "new": "class org.apache.logging.log4j.layout.template.json.util.RecyclerFactories",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.class.noLongerImplementsInterface",
+ "old": "enum org.apache.logging.log4j.layout.template.json.util.RecyclerFactories",
+ "new": "class org.apache.logging.log4j.layout.template.json.util.RecyclerFactories",
+ "interface": "java.lang.Comparable<org.apache.logging.log4j.layout.template.json.util.RecyclerFactories>",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.class.noLongerImplementsInterface",
+ "old": "enum org.apache.logging.log4j.layout.template.json.util.RecyclerFactories",
+ "new": "class org.apache.logging.log4j.layout.template.json.util.RecyclerFactories",
+ "interface": "java.io.Serializable",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method int java.lang.Enum<E extends java.lang.Enum<E extends java.lang.Enum<E>>>::compareTo(E) @ org.apache.logging.log4j.layout.template.json.util.StringParameterParser.Values",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method java.lang.Class<E> java.lang.Enum<E extends java.lang.Enum<E extends java.lang.Enum<E>>>::getDeclaringClass() @ org.apache.logging.log4j.layout.template.json.util.StringParameterParser.Values",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method java.lang.String java.lang.Enum<E extends java.lang.Enum<E extends java.lang.Enum<E>>>::name() @ org.apache.logging.log4j.layout.template.json.util.StringParameterParser.Values",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method int java.lang.Enum<E extends java.lang.Enum<E extends java.lang.Enum<E>>>::ordinal() @ org.apache.logging.log4j.layout.template.json.util.StringParameterParser.Values",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method <T extends java.lang.Enum<T extends java.lang.Enum<T>>> T java.lang.Enum<E extends java.lang.Enum<E extends java.lang.Enum<E>>>::valueOf(java.lang.Class<T>, java.lang.String) @ org.apache.logging.log4j.layout.template.json.util.StringParameterParser.Values",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method org.apache.logging.log4j.layout.template.json.util.StringParameterParser.Values org.apache.logging.log4j.layout.template.json.util.StringParameterParser.Values::valueOf(java.lang.String)",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method org.apache.logging.log4j.layout.template.json.util.StringParameterParser.Values[] org.apache.logging.log4j.layout.template.json.util.StringParameterParser.Values::values()",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.class.noLongerInheritsFromClass",
+ "old": "enum org.apache.logging.log4j.layout.template.json.util.StringParameterParser.Values",
+ "new": "class org.apache.logging.log4j.layout.template.json.util.StringParameterParser.Values",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.class.kindChanged",
+ "old": "enum org.apache.logging.log4j.layout.template.json.util.StringParameterParser.Values",
+ "new": "class org.apache.logging.log4j.layout.template.json.util.StringParameterParser.Values",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.class.noLongerImplementsInterface",
+ "old": "enum org.apache.logging.log4j.layout.template.json.util.StringParameterParser.Values",
+ "new": "class org.apache.logging.log4j.layout.template.json.util.StringParameterParser.Values",
+ "interface": "java.lang.Comparable<org.apache.logging.log4j.layout.template.json.util.StringParameterParser.Values>",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.class.noLongerImplementsInterface",
+ "old": "enum org.apache.logging.log4j.layout.template.json.util.StringParameterParser.Values",
+ "new": "class org.apache.logging.log4j.layout.template.json.util.StringParameterParser.Values",
+ "interface": "java.io.Serializable",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method int java.lang.Enum<E extends java.lang.Enum<E extends java.lang.Enum<E>>>::compareTo(E) @ org.apache.logging.log4j.layout.template.json.util.StringParameterParser",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method java.lang.Class<E> java.lang.Enum<E extends java.lang.Enum<E extends java.lang.Enum<E>>>::getDeclaringClass() @ org.apache.logging.log4j.layout.template.json.util.StringParameterParser",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method java.lang.String java.lang.Enum<E extends java.lang.Enum<E extends java.lang.Enum<E>>>::name() @ org.apache.logging.log4j.layout.template.json.util.StringParameterParser",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method int java.lang.Enum<E extends java.lang.Enum<E extends java.lang.Enum<E>>>::ordinal() @ org.apache.logging.log4j.layout.template.json.util.StringParameterParser",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method <T extends java.lang.Enum<T extends java.lang.Enum<T>>> T java.lang.Enum<E extends java.lang.Enum<E extends java.lang.Enum<E>>>::valueOf(java.lang.Class<T>, java.lang.String) @ org.apache.logging.log4j.layout.template.json.util.StringParameterParser",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method org.apache.logging.log4j.layout.template.json.util.StringParameterParser org.apache.logging.log4j.layout.template.json.util.StringParameterParser::valueOf(java.lang.String)",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method org.apache.logging.log4j.layout.template.json.util.StringParameterParser[] org.apache.logging.log4j.layout.template.json.util.StringParameterParser::values()",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.class.noLongerInheritsFromClass",
+ "old": "enum org.apache.logging.log4j.layout.template.json.util.StringParameterParser",
+ "new": "class org.apache.logging.log4j.layout.template.json.util.StringParameterParser",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.class.kindChanged",
+ "old": "enum org.apache.logging.log4j.layout.template.json.util.StringParameterParser",
+ "new": "class org.apache.logging.log4j.layout.template.json.util.StringParameterParser",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.class.noLongerImplementsInterface",
+ "old": "enum org.apache.logging.log4j.layout.template.json.util.StringParameterParser",
+ "new": "class org.apache.logging.log4j.layout.template.json.util.StringParameterParser",
+ "interface": "java.lang.Comparable<org.apache.logging.log4j.layout.template.json.util.StringParameterParser>",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.class.noLongerImplementsInterface",
+ "old": "enum org.apache.logging.log4j.layout.template.json.util.StringParameterParser",
+ "new": "class org.apache.logging.log4j.layout.template.json.util.StringParameterParser",
+ "interface": "java.io.Serializable",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method int java.lang.Enum<E extends java.lang.Enum<E extends java.lang.Enum<E>>>::compareTo(E) @ org.apache.logging.log4j.layout.template.json.util.Uris",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method java.lang.Class<E> java.lang.Enum<E extends java.lang.Enum<E extends java.lang.Enum<E>>>::getDeclaringClass() @ org.apache.logging.log4j.layout.template.json.util.Uris",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method java.lang.String java.lang.Enum<E extends java.lang.Enum<E extends java.lang.Enum<E>>>::name() @ org.apache.logging.log4j.layout.template.json.util.Uris",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method int java.lang.Enum<E extends java.lang.Enum<E extends java.lang.Enum<E>>>::ordinal() @ org.apache.logging.log4j.layout.template.json.util.Uris",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method <T extends java.lang.Enum<T extends java.lang.Enum<T>>> T java.lang.Enum<E extends java.lang.Enum<E extends java.lang.Enum<E>>>::valueOf(java.lang.Class<T>, java.lang.String) @ org.apache.logging.log4j.layout.template.json.util.Uris",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method org.apache.logging.log4j.layout.template.json.util.Uris org.apache.logging.log4j.layout.template.json.util.Uris::valueOf(java.lang.String)",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method org.apache.logging.log4j.layout.template.json.util.Uris[] org.apache.logging.log4j.layout.template.json.util.Uris::values()",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.class.noLongerInheritsFromClass",
+ "old": "enum org.apache.logging.log4j.layout.template.json.util.Uris",
+ "new": "class org.apache.logging.log4j.layout.template.json.util.Uris",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.class.kindChanged",
+ "old": "enum org.apache.logging.log4j.layout.template.json.util.Uris",
+ "new": "class org.apache.logging.log4j.layout.template.json.util.Uris",
+ "justification": "Replaced 'enum' singletons with 'final class'es."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method char[] org.apache.logging.log4j.layout.template.json.util.TruncatingBufferedPrintWriter::getBuffer()",
+ "justification": "LOG4J2-2993 Massaged (internal) API to make method names more Java-like and restrict access if possible."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method int org.apache.logging.log4j.layout.template.json.util.TruncatingBufferedPrintWriter::getCapacity()",
+ "justification": "LOG4J2-2993 Massaged (internal) API to make method names more Java-like and restrict access if possible."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method int org.apache.logging.log4j.layout.template.json.util.TruncatingBufferedPrintWriter::getPosition()",
+ "justification": "LOG4J2-2993 Massaged (internal) API to make method names more Java-like and restrict access if possible."
+ },
+ {
+ "code": "java.method.removed",
+ "old": "method boolean org.apache.logging.log4j.layout.template.json.util.TruncatingBufferedPrintWriter::isTruncated()",
+ "justification": "LOG4J2-2993 Massaged (internal) API to make method names more Java-like and restrict access if possible."
+ },
+ {
+ "code": "java.class.visibilityReduced",
+ "old": "class org.apache.logging.log4j.layout.template.json.util.TruncatingBufferedWriter",
+ "new": "class org.apache.logging.log4j.layout.template.json.util.TruncatingBufferedWriter",
+ "justification": "LOG4J2-2993 Massaged (internal) API to make method names more Java-like and restrict access if possible."
}
]
}
diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayout.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayout.java
index ffec0df..9b8830c 100644
--- a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayout.java
+++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayout.java
@@ -137,8 +137,8 @@
final String eventTemplate = readEventTemplate(builder);
final float maxByteCountPerChar = builder.charset.newEncoder().maxBytesPerChar();
final int maxStringByteCount =
- Math.toIntExact(Math.round(
- maxByteCountPerChar * builder.maxStringLength));
+ Math.toIntExact(Math.round(Math.ceil(
+ maxByteCountPerChar * builder.maxStringLength)));
final EventTemplateAdditionalField[] eventTemplateAdditionalFields =
builder.eventTemplateAdditionalFields != null
? builder.eventTemplateAdditionalFields
@@ -151,6 +151,7 @@
.setJsonWriter(jsonWriter)
.setRecyclerFactory(builder.recyclerFactory)
.setMaxStringByteCount(maxStringByteCount)
+ .setTruncatedStringSuffix(builder.truncatedStringSuffix)
.setLocationInfoEnabled(builder.locationInfoEnabled)
.setStackTraceEnabled(builder.stackTraceEnabled)
.setStackTraceElementObjectResolver(stackTraceElementObjectResolver)
diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/EventResolverContext.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/EventResolverContext.java
index 8f7107b..e1d2cb6 100644
--- a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/EventResolverContext.java
+++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/EventResolverContext.java
@@ -41,6 +41,8 @@
private final int maxStringByteCount;
+ private final String truncatedStringSuffix;
+
private final boolean locationInfoEnabled;
private final boolean stackTraceEnabled;
@@ -58,6 +60,7 @@
this.jsonWriter = builder.jsonWriter;
this.recyclerFactory = builder.recyclerFactory;
this.maxStringByteCount = builder.maxStringByteCount;
+ this.truncatedStringSuffix = builder.truncatedStringSuffix;
this.locationInfoEnabled = builder.locationInfoEnabled;
this.stackTraceEnabled = builder.stackTraceEnabled;
this.stackTraceObjectResolver = stackTraceEnabled
@@ -103,6 +106,10 @@
return maxStringByteCount;
}
+ String getTruncatedStringSuffix() {
+ return truncatedStringSuffix;
+ }
+
boolean isLocationInfoEnabled() {
return locationInfoEnabled;
}
@@ -141,6 +148,8 @@
private int maxStringByteCount;
+ private String truncatedStringSuffix;
+
private boolean locationInfoEnabled;
private boolean stackTraceEnabled;
@@ -185,6 +194,15 @@
return this;
}
+ public String getTruncatedStringSuffix() {
+ return truncatedStringSuffix;
+ }
+
+ public Builder setTruncatedStringSuffix(final String truncatedStringSuffix) {
+ this.truncatedStringSuffix = truncatedStringSuffix;
+ return this;
+ }
+
public Builder setLocationInfoEnabled(final boolean locationInfoEnabled) {
this.locationInfoEnabled = locationInfoEnabled;
return this;
diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/ExceptionInternalResolverFactory.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/ExceptionInternalResolverFactory.java
deleted file mode 100644
index 31b70cf..0000000
--- a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/ExceptionInternalResolverFactory.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-package org.apache.logging.log4j.layout.template.json.resolver;
-
-/**
- * Exception resolver factory.
- *
- * <h3>Configuration</h3>
- *
- * <pre>
- * config = field , [ stringified ]
- * field = "field" -> ( "className" | "message" | "stackTrace" )
- * stringified = "stringified" -> boolean
- * </pre>
- */
-abstract class ExceptionInternalResolverFactory {
-
- private static final EventResolver NULL_RESOLVER =
- (ignored, jsonGenerator) -> jsonGenerator.writeNull();
-
- EventResolver createInternalResolver(
- final EventResolverContext context,
- final TemplateResolverConfig config) {
- final String fieldName = config.getString("field");
- switch (fieldName) {
- case "className": return createClassNameResolver();
- case "message": return createMessageResolver(context);
- case "stackTrace": return createStackTraceResolver(context, config);
- }
- throw new IllegalArgumentException("unknown field: " + config);
-
- }
-
- abstract EventResolver createClassNameResolver();
-
- abstract EventResolver createMessageResolver(EventResolverContext context);
-
- private EventResolver createStackTraceResolver(
- final EventResolverContext context,
- final TemplateResolverConfig config) {
- if (!context.isStackTraceEnabled()) {
- return NULL_RESOLVER;
- }
- final boolean stringified = config.getBoolean("stringified", false);
- return stringified
- ? createStackTraceStringResolver(context)
- : createStackTraceObjectResolver(context);
- }
-
- abstract EventResolver createStackTraceStringResolver(EventResolverContext context);
-
- abstract EventResolver createStackTraceObjectResolver(EventResolverContext context);
-
-}
diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/ExceptionResolver.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/ExceptionResolver.java
index 415104a..4cdcd28 100644
--- a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/ExceptionResolver.java
+++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/ExceptionResolver.java
@@ -16,76 +16,112 @@
*/
package org.apache.logging.log4j.layout.template.json.resolver;
+import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.layout.template.json.JsonTemplateLayout;
+import org.apache.logging.log4j.layout.template.json.JsonTemplateLayoutDefaults;
import org.apache.logging.log4j.layout.template.json.util.JsonWriter;
+import org.apache.logging.log4j.status.StatusLogger;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
/**
* Exception resolver.
- * <p>
- * Note that this resolver is toggled by {@link
- * JsonTemplateLayout.Builder#setStackTraceEnabled(boolean)}.
*
- * @see ExceptionInternalResolverFactory
+ * <h3>Configuration</h3>
+ *
+ * <pre>
+ * config = field , [ stringified ] , [ stackTrace ]
+ * field = "field" -> ( "className" | "message" | "stackTrace" )
+ *
+ * stackTrace = "stackTrace" -> stringified
+ * stringified = "stringified" -> ( boolean | truncation )
+ * truncation = "truncation" -> (
+ * [ suffix ]
+ * , [ pointMatcherStrings ]
+ * , [ pointMatcherRegexes ]
+ * )
+ * suffix = "suffix" -> string
+ * pointMatcherStrings = "pointMatcherStrings" -> string[]
+ * pointMatcherRegexes = "pointMatcherRegexes" -> string[]
+ * </pre>
+ *
+ * <tt>stringified</tt> is set to <tt>false</tt> by default.
+ * <tt>stringified</tt> at the root level is <b>deprecated</b> in favor of
+ * <tt>stackTrace.stringified</tt>, which has precedence if both are provided.
+ * <p>
+ * <tt>pointMatcherStrings</tt> and <tt>pointMatcherRegexes</tt> enable the
+ * truncation of stringified stack traces after the given matching point. If
+ * both parameters are provided, <tt>pointMatcherStrings</tt> will be checked
+ * first.
+ * <p>
+ * If a stringified stack trace truncation takes place, it will be indicated
+ * with <tt>suffix</tt>, which by default is set to the configured
+ * <tt>truncatedStringSuffix</tt> in the layout, unless explicitly provided.
+ *
+ * <h3>Examples</h3>
+ *
+ * Resolve <tt>logEvent.getThrown().getClass().getCanonicalName()</tt>:
+ *
+ * <pre>
+ * {
+ * "$resolver": "exception",
+ * "field": "className"
+ * }
+ * </pre>
+ *
+ * Resolve the stack trace into a list of <tt>StackTraceElement</tt> objects:
+ *
+ * <pre>
+ * {
+ * "$resolver": "exception",
+ * "field": "stackTrace"
+ * }
+ * </pre>
+ *
+ * Resolve the stack trace into a string field:
+ *
+ * <pre>
+ * {
+ * "$resolver": "exception",
+ * "field": "stackTrace",
+ * "stackTrace": {
+ * "stringified": true
+ * }
+ * }
+ * </pre>
+ *
+ * Resolve the stack trace into a string field
+ * such that the content will be truncated by the given point matcher:
+ *
+ * <pre>
+ * {
+ * "$resolver": "exception",
+ * "field": "stackTrace",
+ * "stackTrace": {
+ * "stringified": {
+ * "truncation": {
+ * "suffix": ">",
+ * "pointMatcherStrings": ["at javax.servlet.http.HttpServlet.service"]
+ * }
+ * }
+ * }
+ * }
+ * </pre>
+ *
+ * @see JsonTemplateLayout.Builder#getTruncatedStringSuffix()
+ * @see JsonTemplateLayoutDefaults#getTruncatedStringSuffix()
+ * @see ExceptionRootCauseResolver
*/
class ExceptionResolver implements EventResolver {
- private static final ExceptionInternalResolverFactory INTERNAL_RESOLVER_FACTORY =
- new ExceptionInternalResolverFactory() {
+ private static final Logger LOGGER = StatusLogger.getLogger();
- @Override
- EventResolver createClassNameResolver() {
- return (final LogEvent logEvent, final JsonWriter jsonWriter) -> {
- final Throwable exception = logEvent.getThrown();
- if (exception == null) {
- jsonWriter.writeNull();
- } else {
- String exceptionClassName = exception.getClass().getCanonicalName();
- jsonWriter.writeString(exceptionClassName);
- }
- };
- }
-
- @Override
- EventResolver createMessageResolver(final EventResolverContext context) {
- return (final LogEvent logEvent, final JsonWriter jsonWriter) -> {
- final Throwable exception = logEvent.getThrown();
- if (exception == null) {
- jsonWriter.writeNull();
- } else {
- String exceptionMessage = exception.getMessage();
- jsonWriter.writeString(exceptionMessage);
- }
- };
- }
-
- @Override
- EventResolver createStackTraceStringResolver(final EventResolverContext context) {
- StackTraceStringResolver stackTraceStringResolver =
- new StackTraceStringResolver(context);
- return (final LogEvent logEvent, final JsonWriter jsonWriter) -> {
- final Throwable exception = logEvent.getThrown();
- if (exception == null) {
- jsonWriter.writeNull();
- } else {
- stackTraceStringResolver.resolve(exception, jsonWriter);
- }
- };
- }
-
- @Override
- EventResolver createStackTraceObjectResolver(final EventResolverContext context) {
- return (final LogEvent logEvent, final JsonWriter jsonWriter) -> {
- final Throwable exception = logEvent.getThrown();
- if (exception == null) {
- jsonWriter.writeNull();
- } else {
- context.getStackTraceObjectResolver().resolve(exception, jsonWriter);
- }
- };
- }
-
- };
+ private static final EventResolver NULL_RESOLVER =
+ (ignored, jsonGenerator) -> jsonGenerator.writeNull();
private final boolean stackTraceEnabled;
@@ -95,8 +131,176 @@
final EventResolverContext context,
final TemplateResolverConfig config) {
this.stackTraceEnabled = context.isStackTraceEnabled();
- this.internalResolver = INTERNAL_RESOLVER_FACTORY
- .createInternalResolver(context, config);
+ this.internalResolver = createInternalResolver(context, config);
+ }
+
+ EventResolver createInternalResolver(
+ final EventResolverContext context,
+ final TemplateResolverConfig config) {
+ final String fieldName = config.getString("field");
+ switch (fieldName) {
+ case "className": return createClassNameResolver();
+ case "message": return createMessageResolver();
+ case "stackTrace": return createStackTraceResolver(context, config);
+ }
+ throw new IllegalArgumentException("unknown field: " + config);
+
+ }
+
+ private EventResolver createClassNameResolver() {
+ return (final LogEvent logEvent, final JsonWriter jsonWriter) -> {
+ final Throwable exception = extractThrowable(logEvent);
+ if (exception == null) {
+ jsonWriter.writeNull();
+ } else {
+ String exceptionClassName = exception.getClass().getCanonicalName();
+ jsonWriter.writeString(exceptionClassName);
+ }
+ };
+ }
+
+ private EventResolver createMessageResolver() {
+ return (final LogEvent logEvent, final JsonWriter jsonWriter) -> {
+ final Throwable exception = extractThrowable(logEvent);
+ if (exception == null) {
+ jsonWriter.writeNull();
+ } else {
+ String exceptionMessage = exception.getMessage();
+ jsonWriter.writeString(exceptionMessage);
+ }
+ };
+ }
+
+ private EventResolver createStackTraceResolver(
+ final EventResolverContext context,
+ final TemplateResolverConfig config) {
+ if (!context.isStackTraceEnabled()) {
+ return NULL_RESOLVER;
+ }
+ final boolean stringified = isStackTraceStringified(config);
+ return stringified
+ ? createStackTraceStringResolver(context, config)
+ : createStackTraceObjectResolver(context);
+ }
+
+ private static boolean isStackTraceStringified(
+ final TemplateResolverConfig config) {
+ final Boolean stringifiedOld = config.getBoolean("stringified");
+ if (stringifiedOld != null) {
+ LOGGER.warn(
+ "\"stringified\" flag at the root level of an exception " +
+ "[root cause] resolver is deprecated in favor of " +
+ "\"stackTrace.stringified\"");
+ }
+ final Object stringifiedNew =
+ config.getObject(new String[]{"stackTrace", "stringified"});
+ if (stringifiedOld == null && stringifiedNew == null) {
+ return false;
+ } else if (stringifiedNew == null) {
+ return stringifiedOld;
+ } else {
+ return !(stringifiedNew instanceof Boolean) || (boolean) stringifiedNew;
+ }
+ }
+
+ private EventResolver createStackTraceStringResolver(
+ final EventResolverContext context,
+ final TemplateResolverConfig config) {
+
+ // Read the configuration.
+ final String truncationSuffix =
+ readTruncationSuffix(context, config);
+ final List<String> truncationPointMatcherStrings =
+ readTruncationPointMatcherStrings(config);
+ final List<String> truncationPointMatcherRegexes =
+ readTruncationPointMatcherRegexes(config);
+
+ // Create the resolver.
+ final StackTraceStringResolver resolver =
+ new StackTraceStringResolver(
+ context,
+ truncationSuffix,
+ truncationPointMatcherStrings,
+ truncationPointMatcherRegexes);
+
+ // Create the null-protected resolver.
+ return (final LogEvent logEvent, final JsonWriter jsonWriter) -> {
+ final Throwable exception = extractThrowable(logEvent);
+ if (exception == null) {
+ jsonWriter.writeNull();
+ } else {
+ resolver.resolve(exception, jsonWriter);
+ }
+ };
+
+ }
+
+ private static String readTruncationSuffix(
+ final EventResolverContext context,
+ final TemplateResolverConfig config) {
+ final String suffix = config.getString(
+ new String[]{"stackTrace", "stringified", "truncation", "suffix"});
+ return suffix != null
+ ? suffix
+ : context.getTruncatedStringSuffix();
+ }
+
+ private static List<String> readTruncationPointMatcherStrings(
+ final TemplateResolverConfig config) {
+ List<String> strings = config.getList(
+ new String[]{"stackTrace", "stringified", "truncation", "pointMatcherStrings"},
+ String.class);
+ if (strings == null) {
+ strings = Collections.emptyList();
+ }
+ return strings;
+ }
+
+ private static List<String> readTruncationPointMatcherRegexes(
+ final TemplateResolverConfig config) {
+
+ // Extract the regexes.
+ List<String> regexes = config.getList(
+ new String[]{"stackTrace", "stringified", "truncation", "pointMatcherRegexes"},
+ String.class);
+ if (regexes == null) {
+ regexes = Collections.emptyList();
+ }
+
+ // Check the regex syntax.
+ for (int i = 0; i < regexes.size(); i++) {
+ final String regex = regexes.get(i);
+ try {
+ Pattern.compile(regex);
+ } catch (final PatternSyntaxException error) {
+ final String message = String.format(
+ "invalid truncation point matcher regex at index %d: %s",
+ i, regex);
+ throw new IllegalArgumentException(message, error);
+ }
+ }
+
+ // Return the extracted regexes.
+ return regexes;
+
+ }
+
+ private EventResolver createStackTraceObjectResolver(
+ final EventResolverContext context) {
+ return (final LogEvent logEvent, final JsonWriter jsonWriter) -> {
+ final Throwable exception = extractThrowable(logEvent);
+ if (exception == null) {
+ jsonWriter.writeNull();
+ } else {
+ context
+ .getStackTraceObjectResolver()
+ .resolve(exception, jsonWriter);
+ }
+ };
+ }
+
+ Throwable extractThrowable(final LogEvent logEvent) {
+ return logEvent.getThrown();
}
static String getName() {
diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/ExceptionRootCauseResolver.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/ExceptionRootCauseResolver.java
index 5218284..37119ca 100644
--- a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/ExceptionRootCauseResolver.java
+++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/ExceptionRootCauseResolver.java
@@ -19,7 +19,6 @@
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.util.Throwables;
import org.apache.logging.log4j.layout.template.json.JsonTemplateLayout;
-import org.apache.logging.log4j.layout.template.json.util.JsonWriter;
/**
* Exception root cause resolver.
@@ -27,81 +26,14 @@
* Note that this resolver is toggled by {@link
* JsonTemplateLayout.Builder#setStackTraceEnabled(boolean)}.
*
- * @see ExceptionInternalResolverFactory
+ * @see ExceptionResolver
*/
-final class ExceptionRootCauseResolver implements EventResolver {
-
- private static final ExceptionInternalResolverFactory INTERNAL_RESOLVER_FACTORY =
- new ExceptionInternalResolverFactory() {
-
- @Override
- EventResolver createClassNameResolver() {
- return (final LogEvent logEvent, final JsonWriter jsonWriter) -> {
- final Throwable exception = logEvent.getThrown();
- if (exception == null) {
- jsonWriter.writeNull();
- } else {
- final Throwable rootCause = Throwables.getRootCause(exception);
- final String rootCauseClassName = rootCause.getClass().getCanonicalName();
- jsonWriter.writeString(rootCauseClassName);
- }
- };
- }
-
- @Override
- EventResolver createMessageResolver(final EventResolverContext context) {
- return (final LogEvent logEvent, final JsonWriter jsonWriter) -> {
- final Throwable exception = logEvent.getThrown();
- if (exception == null) {
- jsonWriter.writeNull();
- } else {
- final Throwable rootCause = Throwables.getRootCause(exception);
- final String rootCauseMessage = rootCause.getMessage();
- jsonWriter.writeString(rootCauseMessage);
- }
- };
- }
-
- @Override
- EventResolver createStackTraceStringResolver(final EventResolverContext context) {
- final StackTraceStringResolver stackTraceStringResolver =
- new StackTraceStringResolver(context);
- return (final LogEvent logEvent, final JsonWriter jsonWriter) -> {
- final Throwable exception = logEvent.getThrown();
- if (exception == null) {
- jsonWriter.writeNull();
- } else {
- final Throwable rootCause = Throwables.getRootCause(exception);
- stackTraceStringResolver.resolve(rootCause, jsonWriter);
- }
- };
- }
-
- @Override
- EventResolver createStackTraceObjectResolver(EventResolverContext context) {
- return (final LogEvent logEvent, final JsonWriter jsonWriter) -> {
- final Throwable exception = logEvent.getThrown();
- if (exception == null) {
- jsonWriter.writeNull();
- } else {
- final Throwable rootCause = Throwables.getRootCause(exception);
- context.getStackTraceObjectResolver().resolve(rootCause, jsonWriter);
- }
- };
- }
-
- };
-
- private final boolean stackTraceEnabled;
-
- private final EventResolver internalResolver;
+final class ExceptionRootCauseResolver extends ExceptionResolver {
ExceptionRootCauseResolver(
final EventResolverContext context,
final TemplateResolverConfig config) {
- this.stackTraceEnabled = context.isStackTraceEnabled();
- this.internalResolver = INTERNAL_RESOLVER_FACTORY
- .createInternalResolver(context, config);
+ super(context, config);
}
static String getName() {
@@ -109,20 +41,9 @@
}
@Override
- public boolean isResolvable() {
- return stackTraceEnabled;
- }
-
- @Override
- public boolean isResolvable(final LogEvent logEvent) {
- return stackTraceEnabled && logEvent.getThrown() != null;
- }
-
- @Override
- public void resolve(
- final LogEvent logEvent,
- final JsonWriter jsonWriter) {
- internalResolver.resolve(logEvent, jsonWriter);
+ Throwable extractThrowable(final LogEvent logEvent) {
+ final Throwable thrown = logEvent.getThrown();
+ return thrown != null ? Throwables.getRootCause(thrown) : null;
}
}
diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/StackTraceStringResolver.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/StackTraceStringResolver.java
index 725ac1e..61be39e 100644
--- a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/StackTraceStringResolver.java
+++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/StackTraceStringResolver.java
@@ -20,19 +20,52 @@
import org.apache.logging.log4j.layout.template.json.util.JsonWriter;
import org.apache.logging.log4j.layout.template.json.util.Recycler;
+import java.util.List;
import java.util.function.Supplier;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
final class StackTraceStringResolver implements StackTraceResolver {
private final Recycler<TruncatingBufferedPrintWriter> writerRecycler;
- StackTraceStringResolver(final EventResolverContext context) {
+ private final boolean truncationEnabled;
+
+ private final String truncationSuffix;
+
+ private final List<String> truncationPointMatcherStrings;
+
+ private final List<Pattern> groupedTruncationPointMatcherRegexes;
+
+ StackTraceStringResolver(
+ final EventResolverContext context,
+ final String truncationSuffix,
+ final List<String> truncationPointMatcherStrings,
+ final List<String> truncationPointMatcherRegexes) {
final Supplier<TruncatingBufferedPrintWriter> writerSupplier =
() -> TruncatingBufferedPrintWriter.ofCapacity(
context.getMaxStringByteCount());
this.writerRecycler = context
.getRecyclerFactory()
.create(writerSupplier, TruncatingBufferedPrintWriter::close);
+ this.truncationEnabled =
+ !truncationPointMatcherStrings.isEmpty() ||
+ !truncationPointMatcherRegexes.isEmpty();
+ this.truncationSuffix = truncationSuffix;
+ this.truncationPointMatcherStrings = truncationPointMatcherStrings;
+ this.groupedTruncationPointMatcherRegexes =
+ groupTruncationPointMatcherRegexes(truncationPointMatcherRegexes);
+ }
+
+ private static List<Pattern> groupTruncationPointMatcherRegexes(
+ final List<String> regexes) {
+ return regexes
+ .stream()
+ .map(regex -> Pattern.compile(
+ "^.*(" + regex + ")(.*)$",
+ Pattern.MULTILINE | Pattern.DOTALL))
+ .collect(Collectors.toList());
}
@Override
@@ -42,10 +75,53 @@
final TruncatingBufferedPrintWriter writer = writerRecycler.acquire();
try {
throwable.printStackTrace(writer);
- jsonWriter.writeString(writer.getBuffer(), 0, writer.getPosition());
+ truncate(writer);
+ jsonWriter.writeString(writer.buffer(), 0, writer.position());
} finally {
writerRecycler.release(writer);
}
}
+ private void truncate(final TruncatingBufferedPrintWriter writer) {
+
+ // Short-circuit if truncation is not enabled.
+ if (!truncationEnabled) {
+ return;
+ }
+
+ // Check for string matches.
+ // noinspection ForLoopReplaceableByForEach (avoid iterator allocation)
+ for (int i = 0; i < truncationPointMatcherStrings.size(); i++) {
+ final String matcher = truncationPointMatcherStrings.get(i);
+ final int matchIndex = writer.indexOf(matcher);
+ if (matchIndex > 0) {
+ final int truncationPointIndex = matchIndex + matcher.length();
+ truncate(writer, truncationPointIndex);
+ return;
+ }
+ }
+
+ // Check for regex matches.
+ // noinspection ForLoopReplaceableByForEach (avoid iterator allocation)
+ for (int i = 0; i < groupedTruncationPointMatcherRegexes.size(); i++) {
+ final Pattern pattern = groupedTruncationPointMatcherRegexes.get(i);
+ final Matcher matcher = pattern.matcher(writer);
+ final boolean matched = matcher.matches();
+ if (matched) {
+ final int lastGroup = matcher.groupCount();
+ final int truncationPointIndex = matcher.start(lastGroup);
+ truncate(writer, truncationPointIndex);
+ return;
+ }
+ }
+
+ }
+
+ private void truncate(
+ final TruncatingBufferedPrintWriter writer,
+ final int index) {
+ writer.position(index);
+ writer.print(truncationSuffix);
+ }
+
}
diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/util/MapAccessor.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/util/MapAccessor.java
index cc3ef76..2f97070 100644
--- a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/util/MapAccessor.java
+++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/util/MapAccessor.java
@@ -17,6 +17,7 @@
package org.apache.logging.log4j.layout.template.json.util;
import java.util.Arrays;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -71,10 +72,58 @@
}
public boolean exists(final String[] path) {
- final Object value = getObject(path, Object.class);
+ final Object value = getObject(path);
return value != null;
}
+ public <E> List<E> getList(final String key, final Class<E> clazz) {
+ final String[] path = {key};
+ return getList(path, clazz);
+ }
+
+ public <E> List<E> getList(final String[] path, final Class<E> clazz) {
+
+ // Access the object.
+ final Object value = getObject(path);
+ if (value == null) {
+ return null;
+ }
+
+ // Check the type.
+ if (!(value instanceof List)) {
+ final String message = String.format(
+ "was expecting a List<%s> at path %s: %s (of type %s)",
+ clazz,
+ Arrays.asList(path),
+ value,
+ value.getClass().getCanonicalName());
+ throw new IllegalArgumentException(message);
+ }
+
+ // Check the element types.
+ @SuppressWarnings("unchecked")
+ final List<Object> items = (List<Object>) value;
+ for (int itemIndex = 0; itemIndex < items.size(); itemIndex++) {
+ final Object item = items.get(itemIndex);
+ if (!clazz.isInstance(item)) {
+ final String message = String.format(
+ "was expecting a List<%s> item at path %s and index %d: %s (of type %s)",
+ clazz,
+ Arrays.asList(path),
+ itemIndex,
+ item,
+ item != null ? item.getClass().getCanonicalName() : null);
+ throw new IllegalArgumentException(message);
+ }
+ }
+
+ // Return the typed list.
+ @SuppressWarnings("unchecked")
+ final List<E> typedItems = (List<E>) items;
+ return typedItems;
+
+ }
+
public Object getObject(final String key) {
final String[] path = {key};
return getObject(path, Object.class);
diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/util/TruncatingBufferedPrintWriter.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/util/TruncatingBufferedPrintWriter.java
index 8d7cb1e..7e9aa3c 100644
--- a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/util/TruncatingBufferedPrintWriter.java
+++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/util/TruncatingBufferedPrintWriter.java
@@ -17,8 +17,11 @@
package org.apache.logging.log4j.layout.template.json.util;
import java.io.PrintWriter;
+import java.util.Objects;
-public final class TruncatingBufferedPrintWriter extends PrintWriter {
+public final class TruncatingBufferedPrintWriter
+ extends PrintWriter
+ implements CharSequence {
private final TruncatingBufferedWriter writer;
@@ -36,20 +39,44 @@
return new TruncatingBufferedPrintWriter(writer);
}
- public char[] getBuffer() {
- return writer.getBuffer();
+ public char[] buffer() {
+ return writer.buffer();
}
- public int getPosition() {
- return writer.getPosition();
+ public int position() {
+ return writer.position();
}
- public int getCapacity() {
- return writer.getCapacity();
+ public void position(final int index) {
+ writer.position(index);
}
- public boolean isTruncated() {
- return writer.isTruncated();
+ public int capacity() {
+ return writer.capacity();
+ }
+
+ public boolean truncated() {
+ return writer.truncated();
+ }
+
+ public int indexOf(final CharSequence seq) {
+ Objects.requireNonNull(seq, "seq");
+ return writer.indexOf(seq);
+ }
+
+ @Override
+ public int length() {
+ return writer.length();
+ }
+
+ @Override
+ public char charAt(final int index) {
+ return writer.charAt(index);
+ }
+
+ @Override
+ public CharSequence subSequence(final int startIndex, final int endIndex) {
+ return writer.subSequence(startIndex, endIndex);
}
@Override
@@ -57,4 +84,9 @@
writer.close();
}
+ @Override
+ public String toString() {
+ return writer.toString();
+ }
+
}
diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/util/TruncatingBufferedWriter.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/util/TruncatingBufferedWriter.java
index ea50f77..1b88f12 100644
--- a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/util/TruncatingBufferedWriter.java
+++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/util/TruncatingBufferedWriter.java
@@ -19,7 +19,7 @@
import java.io.Writer;
import java.util.Objects;
-public final class TruncatingBufferedWriter extends Writer {
+final class TruncatingBufferedWriter extends Writer implements CharSequence {
private final char[] buffer;
@@ -33,19 +33,26 @@
this.truncated = false;
}
- char[] getBuffer() {
+ char[] buffer() {
return buffer;
}
- int getPosition() {
+ int position() {
return position;
}
- int getCapacity() {
+ void position(final int index) {
+ if (index < 0 || index >= buffer.length) {
+ throw new IllegalArgumentException("invalid index: " + index);
+ }
+ position = index;
+ }
+
+ int capacity() {
return buffer.length;
}
- boolean isTruncated() {
+ boolean truncated() {
return truncated;
}
@@ -196,6 +203,53 @@
}
+ int indexOf(final CharSequence seq) {
+
+ // Short-circuit if there is nothing to match.
+ final int seqLength = seq.length();
+ if (seqLength == 0) {
+ return 0;
+ }
+
+ // Short-circuit if the given input is longer than the buffer.
+ if (seqLength > position) {
+ return -1;
+ }
+
+ // Perform the search.
+ for (int bufferIndex = 0; bufferIndex < position; bufferIndex++) {
+ boolean found = true;
+ for (int seqIndex = 0; seqIndex < seqLength; seqIndex++) {
+ final char s = seq.charAt(seqIndex);
+ final char b = buffer[bufferIndex + seqIndex];
+ if (s != b) {
+ found = false;
+ break;
+ }
+ }
+ if (found) {
+ return bufferIndex;
+ }
+ }
+ return -1;
+
+ }
+
+ @Override
+ public int length() {
+ return position + 1;
+ }
+
+ @Override
+ public char charAt(final int index) {
+ return buffer[index];
+ }
+
+ @Override
+ public String subSequence(final int startIndex, final int endIndex) {
+ return new String(buffer, startIndex, endIndex - startIndex);
+ }
+
@Override
public void flush() {}
@@ -205,4 +259,9 @@
truncated = false;
}
+ @Override
+ public String toString() {
+ return new String(buffer, 0, position);
+ }
+
}
diff --git a/log4j-layout-template-json/src/main/resources/EcsLayout.json b/log4j-layout-template-json/src/main/resources/EcsLayout.json
index dee7a84..708b27b 100644
--- a/log4j-layout-template-json/src/main/resources/EcsLayout.json
+++ b/log4j-layout-template-json/src/main/resources/EcsLayout.json
@@ -41,6 +41,8 @@
"error.stack_trace": {
"$resolver": "exception",
"field": "stackTrace",
- "stringified": true
+ "stackTrace": {
+ "stringified": true
+ }
}
}
diff --git a/log4j-layout-template-json/src/main/resources/GelfLayout.json b/log4j-layout-template-json/src/main/resources/GelfLayout.json
index dd43cc8..4281bba 100644
--- a/log4j-layout-template-json/src/main/resources/GelfLayout.json
+++ b/log4j-layout-template-json/src/main/resources/GelfLayout.json
@@ -8,7 +8,9 @@
"full_message": {
"$resolver": "exception",
"field": "stackTrace",
- "stringified": true
+ "stackTrace": {
+ "stringified": true
+ }
},
"timestamp": {
"$resolver": "timestamp",
diff --git a/log4j-layout-template-json/src/main/resources/LogstashJsonEventLayoutV1.json b/log4j-layout-template-json/src/main/resources/LogstashJsonEventLayoutV1.json
index 3225930..809f705 100644
--- a/log4j-layout-template-json/src/main/resources/LogstashJsonEventLayoutV1.json
+++ b/log4j-layout-template-json/src/main/resources/LogstashJsonEventLayoutV1.json
@@ -15,7 +15,9 @@
"stacktrace": {
"$resolver": "exception",
"field": "stackTrace",
- "stringified": true
+ "stackTrace": {
+ "stringified": true
+ }
}
},
"line_number": {
diff --git a/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutTest.java b/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutTest.java
index 057bff7..68152ea 100644
--- a/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutTest.java
+++ b/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutTest.java
@@ -473,7 +473,8 @@
"ex_stacktrace", asMap(
"$resolver", "exception",
"field", "stackTrace",
- "stringified", true),
+ "stackTrace", asMap(
+ "stringified", true)),
"root_ex_class", asMap(
"$resolver", "exceptionRootCause",
"field", "className"),
@@ -483,7 +484,8 @@
"root_ex_stacktrace", asMap(
"$resolver", "exceptionRootCause",
"field", "stackTrace",
- "stringified", true)));
+ "stackTrace", asMap(
+ "stringified", true))));
// Create the layout.
final JsonTemplateLayout layout = JsonTemplateLayout
@@ -547,7 +549,8 @@
"root_ex_stacktrace", asMap(
"$resolver", "exceptionRootCause",
"field", "stackTrace",
- "stringified", true)));
+ "stackTrace", asMap(
+ "stringified", true))));
// Create the layout.
final JsonTemplateLayout layout = JsonTemplateLayout
@@ -1604,14 +1607,16 @@
"exStackTraceString", asMap(
"$resolver", "exception",
"field", "stackTrace",
- "stringified", true),
+ "stackTrace", asMap(
+ "stringified", true)),
"exRootCauseStackTrace", asMap(
"$resolver", "exceptionRootCause",
"field", "stackTrace"),
"exRootCauseStackTraceString", asMap(
"$resolver", "exceptionRootCause",
"field", "stackTrace",
- "stringified", true),
+ "stackTrace", asMap(
+ "stringified", true)),
"requiredFieldTriggeringError", true));
// Create the layout.
@@ -1634,7 +1639,7 @@
}
@Test
- void test_StackTraceTextResolver_with_maxStringLength() {
+ void test_stringified_exception_resolver_with_maxStringLength() {
// Create the event template.
final String eventTemplate = writeJson(asMap(
@@ -1672,6 +1677,123 @@
}
@Test
+ void test_stack_trace_truncation() {
+
+ // Create the exception to be logged.
+ final Exception childError =
+ new Exception("unique child exception message");
+ final Exception parentError =
+ new Exception("unique parent exception message", childError);
+
+ // Create the event template.
+ final String truncationSuffix = "~";
+ final String eventTemplate = writeJson(asMap(
+ // Raw exception.
+ "ex", asMap(
+ "$resolver", "exception",
+ "field", "stackTrace",
+ "stackTrace", asMap(
+ "stringified", true)),
+ // Exception matcher using strings.
+ "stringMatchedEx", asMap(
+ "$resolver", "exception",
+ "field", "stackTrace",
+ "stackTrace", asMap(
+ "stringified", asMap(
+ "truncation", asMap(
+ "suffix", truncationSuffix,
+ "pointMatcherStrings", Arrays.asList(
+ "this string shouldn't match with anything",
+ parentError.getMessage()))))),
+ // Exception matcher using regexes.
+ "regexMatchedEx", asMap(
+ "$resolver", "exception",
+ "field", "stackTrace",
+ "stackTrace", asMap(
+ "stringified", asMap(
+ "truncation", asMap(
+ "suffix", truncationSuffix,
+ "pointMatcherRegexes", Arrays.asList(
+ "this string shouldn't match with anything",
+ parentError
+ .getMessage()
+ .replace("unique", "[xu]n.que")))))),
+ // Raw exception root cause.
+ "rootEx", asMap(
+ "$resolver", "exceptionRootCause",
+ "field", "stackTrace",
+ "stackTrace", asMap(
+ "stringified", true)),
+ // Exception root cause matcher using strings.
+ "stringMatchedRootEx", asMap(
+ "$resolver", "exceptionRootCause",
+ "field", "stackTrace",
+ "stackTrace", asMap(
+ "stringified", asMap(
+ "truncation", asMap(
+ "suffix", truncationSuffix,
+ "pointMatcherStrings", Arrays.asList(
+ "this string shouldn't match with anything",
+ childError.getMessage()))))),
+ // Exception root cause matcher using regexes.
+ "regexMatchedRootEx", asMap(
+ "$resolver", "exceptionRootCause",
+ "field", "stackTrace",
+ "stackTrace", asMap(
+ "stringified", asMap(
+ "truncation", asMap(
+ "suffix", truncationSuffix,
+ "pointMatcherRegexes", Arrays.asList(
+ "this string shouldn't match with anything",
+ childError
+ .getMessage()
+ .replace("unique", "[xu]n.que"))))))));
+
+ // Create the layout.
+ final JsonTemplateLayout layout = JsonTemplateLayout
+ .newBuilder()
+ .setConfiguration(CONFIGURATION)
+ .setEventTemplate(eventTemplate)
+ .setStackTraceEnabled(true)
+ .build();
+
+ // Create the log event.
+ final LogEvent logEvent = Log4jLogEvent
+ .newBuilder()
+ .setLoggerName(LOGGER_NAME)
+ .setThrown(parentError)
+ .build();
+
+ // Check the serialized event.
+ final String expectedMatchedExEnd =
+ parentError.getMessage() + truncationSuffix;
+ final String expectedMatchedRootExEnd =
+ childError.getMessage() + truncationSuffix;
+ usingSerializedLogEventAccessor(layout, logEvent, accessor -> {
+
+ // Check the serialized exception.
+ assertThat(accessor.getString("ex"))
+ .doesNotEndWith(expectedMatchedExEnd)
+ .doesNotEndWith(expectedMatchedRootExEnd);
+ assertThat(accessor.getString("stringMatchedEx"))
+ .endsWith(expectedMatchedExEnd);
+ assertThat(accessor.getString("regexMatchedEx"))
+ .endsWith(expectedMatchedExEnd);
+
+ // Check the serialized exception root cause.
+ assertThat(accessor.getString("rootEx"))
+ .doesNotEndWith(expectedMatchedExEnd)
+ .doesNotEndWith(expectedMatchedRootExEnd);
+ assertThat(accessor.getString("stringMatchedRootEx"))
+ .endsWith(expectedMatchedRootExEnd);
+ assertThat(accessor.getString("regexMatchedRootEx"))
+ .endsWith(expectedMatchedRootExEnd);
+
+ });
+
+ }
+
+ @Test
void test_null_eventDelimiter() {
// Create the event template.
diff --git a/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/util/TruncatingBufferedWriterTest.java b/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/util/TruncatingBufferedWriterTest.java
index a8b210c..b52d453 100644
--- a/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/util/TruncatingBufferedWriterTest.java
+++ b/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/util/TruncatingBufferedWriterTest.java
@@ -75,10 +75,10 @@
expectedBuffer[expectedPosition++] = 'u';
expectedBuffer[expectedPosition++] = 'l';
expectedBuffer[expectedPosition++] = 'l';
- Assertions.assertThat(writer.getBuffer()).isEqualTo(expectedBuffer);
- Assertions.assertThat(writer.getPosition()).isEqualTo(expectedPosition);
- Assertions.assertThat(writer.getCapacity()).isEqualTo(capacity);
- Assertions.assertThat(writer.isTruncated()).isFalse();
+ Assertions.assertThat(writer.buffer()).isEqualTo(expectedBuffer);
+ Assertions.assertThat(writer.position()).isEqualTo(expectedPosition);
+ Assertions.assertThat(writer.capacity()).isEqualTo(capacity);
+ Assertions.assertThat(writer.truncated()).isFalse();
verifyClose(writer);
}
@@ -228,17 +228,17 @@
private void verifyTruncation(
final TruncatingBufferedWriter writer,
final char c) {
- Assertions.assertThat(writer.getBuffer()).isEqualTo(new char[]{c});
- Assertions.assertThat(writer.getPosition()).isEqualTo(1);
- Assertions.assertThat(writer.getCapacity()).isEqualTo(1);
- Assertions.assertThat(writer.isTruncated()).isTrue();
+ Assertions.assertThat(writer.buffer()).isEqualTo(new char[]{c});
+ Assertions.assertThat(writer.position()).isEqualTo(1);
+ Assertions.assertThat(writer.capacity()).isEqualTo(1);
+ Assertions.assertThat(writer.truncated()).isTrue();
verifyClose(writer);
}
private void verifyClose(final TruncatingBufferedWriter writer) {
writer.close();
- Assertions.assertThat(writer.getPosition()).isEqualTo(0);
- Assertions.assertThat(writer.isTruncated()).isFalse();
+ Assertions.assertThat(writer.position()).isEqualTo(0);
+ Assertions.assertThat(writer.truncated()).isFalse();
}
}
diff --git a/log4j-layout-template-json/src/test/resources/testJsonTemplateLayout.json b/log4j-layout-template-json/src/test/resources/testJsonTemplateLayout.json
index daf455e..e8e1063 100644
--- a/log4j-layout-template-json/src/test/resources/testJsonTemplateLayout.json
+++ b/log4j-layout-template-json/src/test/resources/testJsonTemplateLayout.json
@@ -10,7 +10,9 @@
"stacktrace": {
"$resolver": "exception",
"field": "stackTrace",
- "stringified": true
+ "stackTrace": {
+ "stringified": true
+ }
},
"line_number": {
"$resolver": "source",
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index b78a7c3..ad0b9d5 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -31,6 +31,9 @@
- "remove" - Removed
-->
<release version="3.0.0" date="2021-MM-DD" description="GA Release 3.0.0">
+ <action issue="LOG4J2-2993" dev="vy" type="add">
+ Support stack trace truncation in JsonTemplateLayout.
+ </action>
<action issue="LOG4J2-2998" dev="vy" type="fix">
Fix truncation of excessive strings ending with a high surrogate in JsonWriter.
</action>
diff --git a/src/site/asciidoc/manual/json-template-layout.adoc.vm b/src/site/asciidoc/manual/json-template-layout.adoc.vm
index 6003ffe..ff57244 100644
--- a/src/site/asciidoc/manual/json-template-layout.adoc.vm
+++ b/src/site/asciidoc/manual/json-template-layout.adoc.vm
@@ -59,13 +59,14 @@
},
"exception_message": {
"$resolver": "exception",
- "field": "message",
- "stringified": true
+ "field": "message"
},
"stacktrace": {
"$resolver": "exception",
"field": "stackTrace",
- "stringified": true
+ "stackTrace": {
+ "stringified": true
+ }
}
},
"line_number": {
@@ -376,7 +377,9 @@
"error.stack_trace": {
"$resolver": "exception",
"field": "stackTrace",
- "stringified": true
+ "stackTrace": {
+ "stringified": true
+ }
}
}
@@ -434,20 +437,43 @@
a|
[source]
----
-config = field , [ stringified ]
-field = "field" -> (
- "className" \|
- "message" \|
- "stackTrace" )
-stringified = "stringified" -> boolean
+config = field , [ stringified ] , [ stackTrace ]
+field = "field" -> ( "className" \| "message" \| "stackTrace" )
+
+stackTrace = "stackTrace" -> stringified
+stringified = "stringified" -> ( boolean \| truncation )
+truncation = "truncation" -> (
+ [ suffix ]
+ , [ pointMatcherStrings ]
+ , [ pointMatcherRegexes ]
+ )
+suffix = "suffix" -> string
+pointMatcherStrings = "pointMatcherStrings" -> string[]
+pointMatcherRegexes = "pointMatcherRegexes" -> string[]
----
a|
Resolves fields of the `Throwable` returned by `logEvent.getThrown()`.
+`stringified` is set to `false` by default. `stringified` at the root level is
+*deprecated* in favor of `stackTrace.stringified`, which has precedence if both
+are provided.
+
+`pointMatcherStrings` and `pointMatcherRegexes` enable the truncation of
+stringified stack traces after the given matching point. If both parameters are
+provided, `pointMatcherStrings` will be checked first.
+
+If a stringified stack trace truncation takes place, it will be indicated with
+`suffix`, which by default is set to the configured `truncatedStringSuffix` in
+the layout, unless explicitly provided.
+
Note that this resolver is toggled by
`log4j.layout.jsonTemplate.stackTraceEnabled` property.
-| Since `Throwable#getStackTrace()` clones the original `StackTraceElement[]`,
- access to (and hence rendering of) stack traces are not garbage-free.
+a|
+Since `Throwable#getStackTrace()` clones the original `StackTraceElement[]`,
+access to (and hence rendering of) stack traces are not garbage-free.
+
+Each `pointMatcherRegexes` item triggers a `Pattern#matcher()` call, which is
+not garbage-free.
a|
Resolve `logEvent.getThrown().getClass().getCanonicalName()`:
@@ -476,20 +502,38 @@
{
"$resolver": "exception",
"field": "stackTrace",
- "stringified": true
+ "stackTrace": {
+ "stringified": true
+ }
+}
+----
+
+Resolve the stack trace into a string field such that the content will be
+truncated by the given point matcher:
+
+[source,json]
+----
+{
+ "$resolver": "exception",
+ "field": "stackTrace",
+ "stackTrace": {
+ "stringified": {
+ "truncation": {
+ "suffix": ">",
+ "pointMatcherStrings": ["at javax.servlet.http.HttpServlet.service"]
+ }
+ }
+ }
}
----
| exceptionRootCause
| identical to `exception` resolver
-a|
-Resolves the fields of the innermost `Throwable` returned by
-`logEvent.getThrown()`.
-
-Note that this resolver is toggled by
-`log4j.layout.jsonTemplate.stackTraceEnabled` property.
+| identical to `exception` resolver with the exception that the innermost
+ `Throwable` in the causal-chain of `logEvent.getThrown()` is resolved
| identical to `exception` resolver
-| identical to `exception` resolver
+| identical to `exception` resolver with the exception that `${dollar}resolver`
+ field needs to be set to `exceptionRootCause`
| level
a|
@@ -621,8 +665,6 @@
[source]
----
config = [ stringified ] , [ fallbackKey ]
-pattern = "pattern" -> string
-includeStackTrace = "includeStacktrae" -> boolean
stringified = "stringified" -> boolean
fallbackKey = "fallbackKey" -> string
----
@@ -640,17 +682,6 @@
}
----
-Resolve the message into a string using a pattern:
-
-[source,json]
-----
-{
- "$resolver": "message",
- "pattern": ""[%t] %-5p %X{requestId, sessionId, loginId, userId, ipAddress, corpAcctNumber} %C{1.}.%M:%L - %m"",
- "stringified": true
-}
-----
-
Resolve the message such that if it is an `ObjectMessage` or a
`MultiformatMessage` with JSON support, its type (string, list, object, etc.)
will be retained: