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