|
16 | 16 | */ |
17 | 17 | package org.apache.logging.log4j.core.pattern; |
18 | 18 |
|
| 19 | +import java.util.ArrayList; |
| 20 | +import java.util.List; |
19 | 21 | import java.util.Locale; |
20 | 22 |
|
21 | 23 | import org.apache.logging.log4j.core.LogEvent; |
|
37 | 39 | @Plugin(name = "MessagePatternConverter", category = PatternConverter.CATEGORY) |
38 | 40 | @ConverterKeys({ "m", "msg", "message" }) |
39 | 41 | @PerformanceSensitive("allocation") |
40 | | -public final class MessagePatternConverter extends LogEventPatternConverter { |
| 42 | +public class MessagePatternConverter extends LogEventPatternConverter { |
41 | 43 |
|
| 44 | + private static final String LOOKUPS = "lookups"; |
42 | 45 | private static final String NOLOOKUPS = "nolookups"; |
43 | 46 |
|
44 | | - private final String[] formats; |
45 | | - private final Configuration config; |
46 | | - private final TextRenderer textRenderer; |
47 | | - private final boolean noLookups; |
48 | | - |
49 | | - /** |
50 | | - * Private constructor. |
51 | | - * |
52 | | - * @param options |
53 | | - * options, may be null. |
54 | | - */ |
55 | | - private MessagePatternConverter(final Configuration config, final String[] options) { |
| 47 | + private MessagePatternConverter() { |
56 | 48 | super("Message", "message"); |
57 | | - this.formats = options; |
58 | | - this.config = config; |
59 | | - final int noLookupsIdx = loadNoLookups(options); |
60 | | - this.noLookups = Constants.FORMAT_MESSAGES_PATTERN_DISABLE_LOOKUPS || noLookupsIdx >= 0; |
61 | | - this.textRenderer = loadMessageRenderer(noLookupsIdx >= 0 ? ArrayUtils.remove(options, noLookupsIdx) : options); |
62 | 49 | } |
63 | 50 |
|
64 | | - private int loadNoLookups(final String[] options) { |
| 51 | + private static boolean loadLookups(final String[] options) { |
65 | 52 | if (options != null) { |
66 | | - for (int i = 0; i < options.length; i++) { |
67 | | - final String option = options[i]; |
68 | | - if (NOLOOKUPS.equalsIgnoreCase(option)) { |
69 | | - return i; |
| 53 | + for (final String option : options) { |
| 54 | + if (LOOKUPS.equalsIgnoreCase(option)) { |
| 55 | + return true; |
70 | 56 | } |
71 | 57 | } |
72 | 58 | } |
73 | | - return -1; |
| 59 | + return false; |
74 | 60 | } |
75 | 61 |
|
76 | | - private TextRenderer loadMessageRenderer(final String[] options) { |
| 62 | + private static TextRenderer loadMessageRenderer(final String[] options) { |
77 | 63 | if (options != null) { |
78 | 64 | for (final String option : options) { |
79 | 65 | switch (option.toUpperCase(Locale.ROOT)) { |
@@ -102,55 +88,129 @@ private TextRenderer loadMessageRenderer(final String[] options) { |
102 | 88 | * @return instance of pattern converter. |
103 | 89 | */ |
104 | 90 | public static MessagePatternConverter newInstance(final Configuration config, final String[] options) { |
105 | | - return new MessagePatternConverter(config, options); |
| 91 | + boolean lookups = loadLookups(options); |
| 92 | + String[] formats = withoutLookupOptions(options); |
| 93 | + TextRenderer textRenderer = loadMessageRenderer(formats); |
| 94 | + MessagePatternConverter result = formats == null || formats.length == 0 |
| 95 | + ? SimpleMessagePatternConverter.INSTANCE |
| 96 | + : new FormattedMessagePatternConverter(formats); |
| 97 | + if (lookups && config != null) { |
| 98 | + result = new LookupMessagePatternConverter(result, config); |
| 99 | + } |
| 100 | + if (textRenderer != null) { |
| 101 | + result = new RenderingPatternConverter(result, textRenderer); |
| 102 | + } |
| 103 | + return result; |
| 104 | + } |
| 105 | + |
| 106 | + private static String[] withoutLookupOptions(final String[] options) { |
| 107 | + if (options == null || options.length == 0) { |
| 108 | + return options; |
| 109 | + } |
| 110 | + List<String> results = new ArrayList<>(options.length); |
| 111 | + for (String option : options) { |
| 112 | + if (!LOOKUPS.equalsIgnoreCase(option) && !NOLOOKUPS.equalsIgnoreCase(option)) { |
| 113 | + results.add(option); |
| 114 | + } |
| 115 | + } |
| 116 | + return results.toArray(new String[0]); |
106 | 117 | } |
107 | 118 |
|
108 | 119 | /** |
109 | 120 | * {@inheritDoc} |
110 | 121 | */ |
111 | 122 | @Override |
112 | 123 | public void format(final LogEvent event, final StringBuilder toAppendTo) { |
113 | | - final Message msg = event.getMessage(); |
114 | | - if (msg instanceof StringBuilderFormattable) { |
| 124 | + throw new UnsupportedOperationException(); |
| 125 | + } |
115 | 126 |
|
116 | | - final boolean doRender = textRenderer != null; |
117 | | - final StringBuilder workingBuilder = doRender ? new StringBuilder(80) : toAppendTo; |
| 127 | + private static final class SimpleMessagePatternConverter extends MessagePatternConverter { |
| 128 | + private static final MessagePatternConverter INSTANCE = new SimpleMessagePatternConverter(); |
118 | 129 |
|
119 | | - final int offset = workingBuilder.length(); |
120 | | - if (msg instanceof MultiFormatStringBuilderFormattable) { |
121 | | - ((MultiFormatStringBuilderFormattable) msg).formatTo(formats, workingBuilder); |
122 | | - } else { |
123 | | - ((StringBuilderFormattable) msg).formatTo(workingBuilder); |
| 130 | + /** |
| 131 | + * {@inheritDoc} |
| 132 | + */ |
| 133 | + @Override |
| 134 | + public void format(final LogEvent event, final StringBuilder toAppendTo) { |
| 135 | + Message msg = event.getMessage(); |
| 136 | + if (msg instanceof StringBuilderFormattable) { |
| 137 | + ((StringBuilderFormattable) msg).formatTo(toAppendTo); |
| 138 | + } else if (msg != null) { |
| 139 | + toAppendTo.append(msg.getFormattedMessage()); |
124 | 140 | } |
| 141 | + } |
| 142 | + } |
125 | 143 |
|
126 | | - // TODO can we optimize this? |
127 | | - if (config != null && !noLookups) { |
128 | | - for (int i = offset; i < workingBuilder.length() - 1; i++) { |
129 | | - if (workingBuilder.charAt(i) == '$' && workingBuilder.charAt(i + 1) == '{') { |
130 | | - final String value = workingBuilder.substring(offset, workingBuilder.length()); |
131 | | - workingBuilder.setLength(offset); |
132 | | - workingBuilder.append(config.getStrSubstitutor().replace(event, value)); |
133 | | - } |
| 144 | + private static final class FormattedMessagePatternConverter extends MessagePatternConverter { |
| 145 | + |
| 146 | + private final String[] formats; |
| 147 | + |
| 148 | + FormattedMessagePatternConverter(final String[] formats) { |
| 149 | + this.formats = formats; |
| 150 | + } |
| 151 | + |
| 152 | + /** |
| 153 | + * {@inheritDoc} |
| 154 | + */ |
| 155 | + @Override |
| 156 | + public void format(final LogEvent event, final StringBuilder toAppendTo) { |
| 157 | + Message msg = event.getMessage(); |
| 158 | + if (msg instanceof StringBuilderFormattable) { |
| 159 | + if (msg instanceof MultiFormatStringBuilderFormattable) { |
| 160 | + ((MultiFormatStringBuilderFormattable) msg).formatTo(formats, toAppendTo); |
| 161 | + } else { |
| 162 | + ((StringBuilderFormattable) msg).formatTo(toAppendTo); |
134 | 163 | } |
| 164 | + } else if (msg != null) { |
| 165 | + toAppendTo.append(msg instanceof MultiformatMessage |
| 166 | + ? ((MultiformatMessage) msg).getFormattedMessage(formats) |
| 167 | + : msg.getFormattedMessage()); |
135 | 168 | } |
136 | | - if (doRender) { |
137 | | - textRenderer.render(workingBuilder, toAppendTo); |
138 | | - } |
139 | | - return; |
140 | 169 | } |
141 | | - if (msg != null) { |
142 | | - final String result; |
143 | | - if (msg instanceof MultiformatMessage) { |
144 | | - result = ((MultiformatMessage) msg).getFormattedMessage(formats); |
145 | | - } else { |
146 | | - result = msg.getFormattedMessage(); |
147 | | - } |
148 | | - if (result != null) { |
149 | | - toAppendTo.append(config != null && result.contains("${") |
150 | | - ? config.getStrSubstitutor().replace(event, result) : result); |
151 | | - } else { |
152 | | - toAppendTo.append("null"); |
| 170 | + } |
| 171 | + |
| 172 | + private static final class LookupMessagePatternConverter extends MessagePatternConverter { |
| 173 | + private final MessagePatternConverter delegate; |
| 174 | + private final Configuration config; |
| 175 | + |
| 176 | + LookupMessagePatternConverter(final MessagePatternConverter delegate, final Configuration config) { |
| 177 | + this.delegate = delegate; |
| 178 | + this.config = config; |
| 179 | + } |
| 180 | + |
| 181 | + /** |
| 182 | + * {@inheritDoc} |
| 183 | + */ |
| 184 | + @Override |
| 185 | + public void format(final LogEvent event, final StringBuilder toAppendTo) { |
| 186 | + int start = toAppendTo.length(); |
| 187 | + delegate.format(event, toAppendTo); |
| 188 | + int indexOfSubstitution = toAppendTo.indexOf("${", start); |
| 189 | + if (indexOfSubstitution >= 0) { |
| 190 | + config.getStrSubstitutor() |
| 191 | + .replaceIn(event, toAppendTo, indexOfSubstitution, toAppendTo.length() - indexOfSubstitution); |
153 | 192 | } |
154 | 193 | } |
155 | 194 | } |
| 195 | + |
| 196 | + private static final class RenderingPatternConverter extends MessagePatternConverter { |
| 197 | + |
| 198 | + private final MessagePatternConverter delegate; |
| 199 | + private final TextRenderer textRenderer; |
| 200 | + |
| 201 | + RenderingPatternConverter(final MessagePatternConverter delegate, final TextRenderer textRenderer) { |
| 202 | + this.delegate = delegate; |
| 203 | + this.textRenderer = textRenderer; |
| 204 | + } |
| 205 | + |
| 206 | + /** |
| 207 | + * {@inheritDoc} |
| 208 | + */ |
| 209 | + @Override |
| 210 | + public void format(final LogEvent event, final StringBuilder toAppendTo) { |
| 211 | + StringBuilder workingBuilder = new StringBuilder(80); |
| 212 | + delegate.format(event, workingBuilder); |
| 213 | + textRenderer.render(workingBuilder, toAppendTo); |
| 214 | + } |
| 215 | + } |
156 | 216 | } |
0 commit comments