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
99
100
101
102
103 public interface Serializer2 {
104 StringBuilder toSerializable(final LogEvent event, final StringBuilder builder);
105 }
106
107
108
109
110 protected static final int DEFAULT_STRING_BUILDER_SIZE = 1024;
111
112 protected static final int MAX_STRING_BUILDER_SIZE = Math.max(DEFAULT_STRING_BUILDER_SIZE,
113 size("log4j.layoutStringBuilder.maxSize", 2 * 1024));
114
115 private static final ThreadLocal<StringBuilder> threadLocal = new ThreadLocal<>();
116
117
118
119
120
121
122 protected static StringBuilder getStringBuilder() {
123 if (AbstractLogger.getRecursionDepth() > 1) {
124
125 return new StringBuilder(DEFAULT_STRING_BUILDER_SIZE);
126 }
127 StringBuilder result = threadLocal.get();
128 if (result == null) {
129 result = new StringBuilder(DEFAULT_STRING_BUILDER_SIZE);
130 threadLocal.set(result);
131 }
132 trimToMaxSize(result);
133 result.setLength(0);
134 return result;
135 }
136
137
138 private static boolean isPreJava8() {
139 return org.apache.logging.log4j.util.Constants.JAVA_MAJOR_VERSION < 8;
140 }
141
142 private static int size(final String property, final int defaultValue) {
143 return PropertiesUtil.getProperties().getIntegerProperty(property, defaultValue);
144 }
145
146 protected static void trimToMaxSize(final StringBuilder stringBuilder) {
147 StringBuilders.trimToMaxSize(stringBuilder, MAX_STRING_BUILDER_SIZE);
148 }
149
150 private Encoder<StringBuilder> textEncoder;
151
152
153
154
155 private transient Charset charset;
156
157 private final String charsetName;
158
159 private final Serializer footerSerializer;
160
161 private final Serializer headerSerializer;
162
163 private final boolean useCustomEncoding;
164
165 protected AbstractStringLayout(final Charset charset) {
166 this(charset, (byte[]) null, (byte[]) null);
167 }
168
169
170
171
172
173
174
175
176 protected AbstractStringLayout(final Charset aCharset, final byte[] header, final byte[] footer) {
177 super(null, header, footer);
178 this.headerSerializer = null;
179 this.footerSerializer = null;
180 this.charset = aCharset == null ? StandardCharsets.UTF_8 : aCharset;
181 this.charsetName = this.charset.name();
182 useCustomEncoding = isPreJava8()
183 && (StandardCharsets.ISO_8859_1.equals(aCharset) || StandardCharsets.US_ASCII.equals(aCharset));
184 textEncoder = Constants.ENABLE_DIRECT_ENCODERS ? new StringBuilderEncoder(charset) : null;
185 }
186
187
188
189
190
191
192
193
194
195 protected AbstractStringLayout(final Configuration config, final Charset aCharset,
196 final Serializer headerSerializer, final Serializer footerSerializer) {
197 super(config, null, null);
198 this.headerSerializer = headerSerializer;
199 this.footerSerializer = footerSerializer;
200 this.charset = aCharset == null ? StandardCharsets.UTF_8 : aCharset;
201 this.charsetName = this.charset.name();
202 useCustomEncoding = isPreJava8()
203 && (StandardCharsets.ISO_8859_1.equals(aCharset) || StandardCharsets.US_ASCII.equals(aCharset));
204 textEncoder = Constants.ENABLE_DIRECT_ENCODERS ? new StringBuilderEncoder(charset) : null;
205 }
206
207 protected byte[] getBytes(final String s) {
208 if (useCustomEncoding) {
209 return StringEncoder.encodeSingleByteChars(s);
210 }
211 try {
212 return s.getBytes(charsetName);
213 } catch (final UnsupportedEncodingException e) {
214 return s.getBytes(charset);
215 }
216 }
217
218 @Override
219 public Charset getCharset() {
220 return charset;
221 }
222
223
224
225
226 @Override
227 public String getContentType() {
228 return "text/plain";
229 }
230
231
232
233
234
235
236 @Override
237 public byte[] getFooter() {
238 return serializeToBytes(footerSerializer, super.getFooter());
239 }
240
241 public Serializer getFooterSerializer() {
242 return footerSerializer;
243 }
244
245
246
247
248
249
250 @Override
251 public byte[] getHeader() {
252 return serializeToBytes(headerSerializer, super.getHeader());
253 }
254
255 public Serializer getHeaderSerializer() {
256 return headerSerializer;
257 }
258
259 private DefaultLogEventFactory getLogEventFactory() {
260 return DefaultLogEventFactory.getInstance();
261 }
262
263
264
265
266
267
268 protected Encoder<StringBuilder> getStringBuilderEncoder() {
269 if (textEncoder == null) {
270 textEncoder = new StringBuilderEncoder(getCharset());
271 }
272 return textEncoder;
273 }
274
275 protected byte[] serializeToBytes(final Serializer serializer, final byte[] defaultValue) {
276 final String serializable = serializeToString(serializer);
277 if (serializer == null) {
278 return defaultValue;
279 }
280 return StringEncoder.toBytes(serializable, getCharset());
281 }
282
283 protected String serializeToString(final Serializer serializer) {
284 if (serializer == null) {
285 return null;
286 }
287 final LoggerConfig rootLogger = getConfiguration().getRootLogger();
288
289 final LogEvent logEvent = getLogEventFactory().createEvent(rootLogger.getName(), null, Strings.EMPTY,
290 rootLogger.getLevel(), null, null, null);
291 return serializer.toSerializable(logEvent);
292 }
293
294
295
296
297
298
299
300 @Override
301 public byte[] toByteArray(final LogEvent event) {
302 return getBytes(toSerializable(event));
303 }
304
305 }