1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.core.layout;
18
19 import java.io.UnsupportedEncodingException;
20 import java.nio.charset.Charset;
21 import java.nio.charset.StandardCharsets;
22
23 import org.apache.logging.log4j.core.LogEvent;
24 import org.apache.logging.log4j.core.StringLayout;
25 import org.apache.logging.log4j.core.config.Configuration;
26 import org.apache.logging.log4j.core.config.LoggerConfig;
27 import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
28 import org.apache.logging.log4j.core.config.plugins.PluginElement;
29 import org.apache.logging.log4j.core.impl.DefaultLogEventFactory;
30 import org.apache.logging.log4j.core.impl.LocationAware;
31 import org.apache.logging.log4j.core.util.Constants;
32 import org.apache.logging.log4j.core.util.StringEncoder;
33 import org.apache.logging.log4j.spi.AbstractLogger;
34 import org.apache.logging.log4j.util.PropertiesUtil;
35 import org.apache.logging.log4j.util.StringBuilders;
36 import org.apache.logging.log4j.util.Strings;
37
38
39
40
41
42
43
44
45
46
47
48
49 public abstract class AbstractStringLayout extends AbstractLayout<String> implements StringLayout, LocationAware {
50
51 public abstract static class Builder<B extends Builder<B>> extends AbstractLayout.Builder<B> {
52
53 @PluginBuilderAttribute(value = "charset")
54 private Charset charset;
55
56 @PluginElement("footerSerializer")
57 private Serializer footerSerializer;
58
59 @PluginElement("headerSerializer")
60 private Serializer headerSerializer;
61
62 public Charset getCharset() {
63 return charset;
64 }
65
66 public Serializer getFooterSerializer() {
67 return footerSerializer;
68 }
69
70 public Serializer getHeaderSerializer() {
71 return headerSerializer;
72 }
73
74 public B setCharset(final Charset charset) {
75 this.charset = charset;
76 return asBuilder();
77 }
78
79 public B setFooterSerializer(final Serializer footerSerializer) {
80 this.footerSerializer = footerSerializer;
81 return asBuilder();
82 }
83
84 public B setHeaderSerializer(final Serializer headerSerializer) {
85 this.headerSerializer = headerSerializer;
86 return asBuilder();
87 }
88
89 }
90
91 public boolean requiresLocation() {
92 return false;
93 }
94
95 public interface Serializer {
96 String toSerializable(final LogEvent event);
97
98 default StringBuilder toSerializable(final LogEvent event, final StringBuilder builder) {
99 builder.append(toSerializable(event));
100 return builder;
101 }
102 }
103
104
105
106
107
108
109 public interface Serializer2 {
110 StringBuilder toSerializable(final LogEvent event, final StringBuilder builder);
111 }
112
113
114
115
116 protected static final int DEFAULT_STRING_BUILDER_SIZE = 1024;
117
118 protected static final int MAX_STRING_BUILDER_SIZE = Math.max(DEFAULT_STRING_BUILDER_SIZE,
119 size("log4j.layoutStringBuilder.maxSize", 2 * 1024));
120
121 private static final ThreadLocal<StringBuilder> threadLocal = new ThreadLocal<>();
122
123
124
125
126
127
128 protected static StringBuilder getStringBuilder() {
129 if (AbstractLogger.getRecursionDepth() > 1) {
130
131 return new StringBuilder(DEFAULT_STRING_BUILDER_SIZE);
132 }
133 StringBuilder result = threadLocal.get();
134 if (result == null) {
135 result = new StringBuilder(DEFAULT_STRING_BUILDER_SIZE);
136 threadLocal.set(result);
137 }
138 trimToMaxSize(result);
139 result.setLength(0);
140 return result;
141 }
142
143
144 private static boolean isPreJava8() {
145 return org.apache.logging.log4j.util.Constants.JAVA_MAJOR_VERSION < 8;
146 }
147
148 private static int size(final String property, final int defaultValue) {
149 return PropertiesUtil.getProperties().getIntegerProperty(property, defaultValue);
150 }
151
152 protected static void trimToMaxSize(final StringBuilder stringBuilder) {
153 StringBuilders.trimToMaxSize(stringBuilder, MAX_STRING_BUILDER_SIZE);
154 }
155
156 private Encoder<StringBuilder> textEncoder;
157
158
159
160
161 private transient Charset charset;
162
163 private final String charsetName;
164
165 private final Serializer footerSerializer;
166
167 private final Serializer headerSerializer;
168
169 private final boolean useCustomEncoding;
170
171 protected AbstractStringLayout(final Charset charset) {
172 this(charset, (byte[]) null, (byte[]) null);
173 }
174
175
176
177
178
179
180
181
182 protected AbstractStringLayout(final Charset aCharset, final byte[] header, final byte[] footer) {
183 super(null, header, footer);
184 this.headerSerializer = null;
185 this.footerSerializer = null;
186 this.charset = aCharset == null ? StandardCharsets.UTF_8 : aCharset;
187 this.charsetName = this.charset.name();
188 useCustomEncoding = isPreJava8()
189 && (StandardCharsets.ISO_8859_1.equals(aCharset) || StandardCharsets.US_ASCII.equals(aCharset));
190 textEncoder = Constants.ENABLE_DIRECT_ENCODERS ? new StringBuilderEncoder(charset) : null;
191 }
192
193
194
195
196
197
198
199
200
201 protected AbstractStringLayout(final Configuration config, final Charset aCharset,
202 final Serializer headerSerializer, final Serializer footerSerializer) {
203 super(config, null, null);
204 this.headerSerializer = headerSerializer;
205 this.footerSerializer = footerSerializer;
206 this.charset = aCharset == null ? StandardCharsets.UTF_8 : aCharset;
207 this.charsetName = this.charset.name();
208 useCustomEncoding = isPreJava8()
209 && (StandardCharsets.ISO_8859_1.equals(aCharset) || StandardCharsets.US_ASCII.equals(aCharset));
210 textEncoder = Constants.ENABLE_DIRECT_ENCODERS ? new StringBuilderEncoder(charset) : null;
211 }
212
213 protected byte[] getBytes(final String s) {
214 if (useCustomEncoding) {
215 return StringEncoder.encodeSingleByteChars(s);
216 }
217 try {
218 return s.getBytes(charsetName);
219 } catch (final UnsupportedEncodingException e) {
220 return s.getBytes(charset);
221 }
222 }
223
224 @Override
225 public Charset getCharset() {
226 return charset;
227 }
228
229
230
231
232 @Override
233 public String getContentType() {
234 return "text/plain";
235 }
236
237
238
239
240
241
242 @Override
243 public byte[] getFooter() {
244 return serializeToBytes(footerSerializer, super.getFooter());
245 }
246
247 public Serializer getFooterSerializer() {
248 return footerSerializer;
249 }
250
251
252
253
254
255
256 @Override
257 public byte[] getHeader() {
258 return serializeToBytes(headerSerializer, super.getHeader());
259 }
260
261 public Serializer getHeaderSerializer() {
262 return headerSerializer;
263 }
264
265 private DefaultLogEventFactory getLogEventFactory() {
266 return DefaultLogEventFactory.getInstance();
267 }
268
269
270
271
272
273
274 protected Encoder<StringBuilder> getStringBuilderEncoder() {
275 if (textEncoder == null) {
276 textEncoder = new StringBuilderEncoder(getCharset());
277 }
278 return textEncoder;
279 }
280
281 protected byte[] serializeToBytes(final Serializer serializer, final byte[] defaultValue) {
282 final String serializable = serializeToString(serializer);
283 if (serializable == null) {
284 return defaultValue;
285 }
286 return StringEncoder.toBytes(serializable, getCharset());
287 }
288
289 protected String serializeToString(final Serializer serializer) {
290 if (serializer == null) {
291 return null;
292 }
293 final LoggerConfig rootLogger = getConfiguration().getRootLogger();
294
295 final LogEvent logEvent = getLogEventFactory().createEvent(rootLogger.getName(), null, Strings.EMPTY,
296 rootLogger.getLevel(), null, null, null);
297 return serializer.toSerializable(logEvent);
298 }
299
300
301
302
303
304
305
306 @Override
307 public byte[] toByteArray(final LogEvent event) {
308 return getBytes(toSerializable(event));
309 }
310
311 }