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