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