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 final String version = System.getProperty("java.version");
135 final String[] parts = version.split("\\.");
136 try {
137 final int major = Integer.parseInt(parts[1]);
138 return major < 8;
139 } catch (final Exception ex) {
140 return true;
141 }
142 }
143
144 private static int size(final String property, final int defaultValue) {
145 return PropertiesUtil.getProperties().getIntegerProperty(property, defaultValue);
146 }
147
148 protected static void trimToMaxSize(final StringBuilder stringBuilder) {
149 StringBuilders.trimToMaxSize(stringBuilder, MAX_STRING_BUILDER_SIZE);
150 }
151
152 private Encoder<StringBuilder> textEncoder;
153
154
155
156
157 private transient Charset charset;
158
159 private final String charsetName;
160
161 private final Serializer footerSerializer;
162
163 private final Serializer headerSerializer;
164
165 private final boolean useCustomEncoding;
166
167 protected AbstractStringLayout(final Charset charset) {
168 this(charset, (byte[]) null, (byte[]) null);
169 }
170
171
172
173
174
175
176
177
178 protected AbstractStringLayout(final Charset aCharset, final byte[] header, final byte[] footer) {
179 super(null, header, footer);
180 this.headerSerializer = null;
181 this.footerSerializer = null;
182 this.charset = aCharset == null ? StandardCharsets.UTF_8 : aCharset;
183 this.charsetName = this.charset.name();
184 useCustomEncoding = isPreJava8()
185 && (StandardCharsets.ISO_8859_1.equals(aCharset) || StandardCharsets.US_ASCII.equals(aCharset));
186 textEncoder = Constants.ENABLE_DIRECT_ENCODERS ? new StringBuilderEncoder(charset) : null;
187 }
188
189
190
191
192
193
194
195
196
197 protected AbstractStringLayout(final Configuration config, final Charset aCharset,
198 final Serializer headerSerializer, final Serializer footerSerializer) {
199 super(config, null, null);
200 this.headerSerializer = headerSerializer;
201 this.footerSerializer = footerSerializer;
202 this.charset = aCharset == null ? StandardCharsets.UTF_8 : aCharset;
203 this.charsetName = this.charset.name();
204 useCustomEncoding = isPreJava8()
205 && (StandardCharsets.ISO_8859_1.equals(aCharset) || StandardCharsets.US_ASCII.equals(aCharset));
206 textEncoder = Constants.ENABLE_DIRECT_ENCODERS ? new StringBuilderEncoder(charset) : null;
207 }
208
209 protected byte[] getBytes(final String s) {
210 if (useCustomEncoding) {
211 return StringEncoder.encodeSingleByteChars(s);
212 }
213 try {
214 return s.getBytes(charsetName);
215 } catch (final UnsupportedEncodingException e) {
216 return s.getBytes(charset);
217 }
218 }
219
220 @Override
221 public Charset getCharset() {
222 return charset;
223 }
224
225
226
227
228 @Override
229 public String getContentType() {
230 return "text/plain";
231 }
232
233
234
235
236
237
238 @Override
239 public byte[] getFooter() {
240 return serializeToBytes(footerSerializer, super.getFooter());
241 }
242
243 public Serializer getFooterSerializer() {
244 return footerSerializer;
245 }
246
247
248
249
250
251
252 @Override
253 public byte[] getHeader() {
254 return serializeToBytes(headerSerializer, super.getHeader());
255 }
256
257 public Serializer getHeaderSerializer() {
258 return headerSerializer;
259 }
260
261 private DefaultLogEventFactory getLogEventFactory() {
262 return DefaultLogEventFactory.getInstance();
263 }
264
265
266
267
268
269
270 protected Encoder<StringBuilder> getStringBuilderEncoder() {
271 if (textEncoder == null) {
272 textEncoder = new StringBuilderEncoder(getCharset());
273 }
274 return textEncoder;
275 }
276
277 protected byte[] serializeToBytes(final Serializer serializer, final byte[] defaultValue) {
278 final String serializable = serializeToString(serializer);
279 if (serializer == null) {
280 return defaultValue;
281 }
282 return StringEncoder.toBytes(serializable, getCharset());
283 }
284
285 protected String serializeToString(final Serializer serializer) {
286 if (serializer == null) {
287 return null;
288 }
289 final LoggerConfig rootLogger = getConfiguration().getRootLogger();
290
291 final LogEvent logEvent = getLogEventFactory().createEvent(rootLogger.getName(), null, Strings.EMPTY,
292 rootLogger.getLevel(), null, null, null);
293 return serializer.toSerializable(logEvent);
294 }
295
296
297
298
299
300
301
302 @Override
303 public byte[] toByteArray(final LogEvent event) {
304 return getBytes(toSerializable(event));
305 }
306
307 }