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.IOException;
20 import java.io.Writer;
21 import java.nio.charset.Charset;
22 import java.nio.charset.StandardCharsets;
23 import java.util.HashMap;
24 import java.util.LinkedHashMap;
25 import java.util.Map;
26
27 import org.apache.logging.log4j.core.Layout;
28 import org.apache.logging.log4j.core.LogEvent;
29 import org.apache.logging.log4j.core.config.Configuration;
30 import org.apache.logging.log4j.core.config.DefaultConfiguration;
31 import org.apache.logging.log4j.core.config.Node;
32 import org.apache.logging.log4j.core.config.plugins.Plugin;
33 import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
34 import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
35 import org.apache.logging.log4j.core.config.plugins.PluginElement;
36 import org.apache.logging.log4j.core.util.KeyValuePair;
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69 @Plugin(name = "JsonLayout", category = Node.CATEGORY, elementType = Layout.ELEMENT_TYPE, printObject = true)
70 public final class JsonLayout extends AbstractJacksonLayout {
71
72 private static final String DEFAULT_FOOTER = "]";
73
74 private static final String DEFAULT_HEADER = "[";
75
76 static final String CONTENT_TYPE = "application/json";
77
78 public static class Builder<B extends Builder<B>> extends AbstractJacksonLayout.Builder<B>
79 implements org.apache.logging.log4j.core.util.Builder<JsonLayout> {
80
81 @PluginBuilderAttribute
82 private boolean propertiesAsList;
83
84 @PluginElement("AdditionalField")
85 private KeyValuePair[] additionalFields;
86
87 public Builder() {
88 super();
89 setCharset(StandardCharsets.UTF_8);
90 }
91
92 @Override
93 public JsonLayout build() {
94 final boolean encodeThreadContextAsList = isProperties() && propertiesAsList;
95 final String headerPattern = toStringOrNull(getHeader());
96 final String footerPattern = toStringOrNull(getFooter());
97 return new JsonLayout(getConfiguration(), isLocationInfo(), isProperties(), encodeThreadContextAsList,
98 isComplete(), isCompact(), getEventEol(), headerPattern, footerPattern, getCharset(),
99 isIncludeStacktrace(), isStacktraceAsString(), isIncludeNullDelimiter(),
100 getAdditionalFields());
101 }
102
103 public boolean isPropertiesAsList() {
104 return propertiesAsList;
105 }
106
107 public B setPropertiesAsList(final boolean propertiesAsList) {
108 this.propertiesAsList = propertiesAsList;
109 return asBuilder();
110 }
111
112 public KeyValuePair[] getAdditionalFields() {
113 return additionalFields;
114 }
115
116 public B setAdditionalFields(KeyValuePair[] additionalFields) {
117 this.additionalFields = additionalFields;
118 return asBuilder();
119 }
120 }
121
122
123
124
125 @Deprecated
126 protected JsonLayout(final Configuration config, final boolean locationInfo, final boolean properties,
127 final boolean encodeThreadContextAsList,
128 final boolean complete, final boolean compact, final boolean eventEol, final String headerPattern,
129 final String footerPattern, final Charset charset, final boolean includeStacktrace) {
130 super(config, new JacksonFactory.JSON(encodeThreadContextAsList, includeStacktrace, false).newWriter(
131 locationInfo, properties, compact),
132 charset, compact, complete, eventEol,
133 PatternLayout.newSerializerBuilder().setConfiguration(config).setPattern(headerPattern).setDefaultPattern(DEFAULT_HEADER).build(),
134 PatternLayout.newSerializerBuilder().setConfiguration(config).setPattern(footerPattern).setDefaultPattern(DEFAULT_FOOTER).build(),
135 false, null);
136 }
137
138 private JsonLayout(final Configuration config, final boolean locationInfo, final boolean properties,
139 final boolean encodeThreadContextAsList,
140 final boolean complete, final boolean compact, final boolean eventEol,
141 final String headerPattern, final String footerPattern, final Charset charset,
142 final boolean includeStacktrace, final boolean stacktraceAsString,
143 final boolean includeNullDelimiter,
144 final KeyValuePair[] additionalFields) {
145 super(config, new JacksonFactory.JSON(encodeThreadContextAsList, includeStacktrace, stacktraceAsString).newWriter(
146 locationInfo, properties, compact),
147 charset, compact, complete, eventEol,
148 PatternLayout.newSerializerBuilder().setConfiguration(config).setPattern(headerPattern).setDefaultPattern(DEFAULT_HEADER).build(),
149 PatternLayout.newSerializerBuilder().setConfiguration(config).setPattern(footerPattern).setDefaultPattern(DEFAULT_FOOTER).build(),
150 includeNullDelimiter,
151 additionalFields);
152 }
153
154
155
156
157
158
159 @Override
160 public byte[] getHeader() {
161 if (!this.complete) {
162 return null;
163 }
164 final StringBuilder buf = new StringBuilder();
165 final String str = serializeToString(getHeaderSerializer());
166 if (str != null) {
167 buf.append(str);
168 }
169 buf.append(this.eol);
170 return getBytes(buf.toString());
171 }
172
173
174
175
176
177
178 @Override
179 public byte[] getFooter() {
180 if (!this.complete) {
181 return null;
182 }
183 final StringBuilder buf = new StringBuilder();
184 buf.append(this.eol);
185 final String str = serializeToString(getFooterSerializer());
186 if (str != null) {
187 buf.append(str);
188 }
189 buf.append(this.eol);
190 return getBytes(buf.toString());
191 }
192
193 @Override
194 public Map<String, String> getContentFormat() {
195 final Map<String, String> result = new HashMap<>();
196 result.put("version", "2.0");
197 return result;
198 }
199
200
201
202
203 @Override
204 public String getContentType() {
205 return CONTENT_TYPE + "; charset=" + this.getCharset();
206 }
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240 @Deprecated
241 public static JsonLayout createLayout(
242 final Configuration config,
243 final boolean locationInfo,
244 final boolean properties,
245 final boolean propertiesAsList,
246 final boolean complete,
247 final boolean compact,
248 final boolean eventEol,
249 final String headerPattern,
250 final String footerPattern,
251 final Charset charset,
252 final boolean includeStacktrace) {
253 final boolean encodeThreadContextAsList = properties && propertiesAsList;
254 return new JsonLayout(config, locationInfo, properties, encodeThreadContextAsList, complete, compact, eventEol,
255 headerPattern, footerPattern, charset, includeStacktrace, false, false, null);
256 }
257
258 @PluginBuilderFactory
259 public static <B extends Builder<B>> B newBuilder() {
260 return new Builder<B>().asBuilder();
261 }
262
263
264
265
266
267
268 public static JsonLayout createDefaultLayout() {
269 return new JsonLayout(new DefaultConfiguration(), false, false, false, false, false, false,
270 DEFAULT_HEADER, DEFAULT_FOOTER, StandardCharsets.UTF_8, true, false, false, null);
271 }
272
273 @Override
274 public void toSerializable(final LogEvent event, final Writer writer) throws IOException {
275 if (complete && eventCount > 0) {
276 writer.append(", ");
277 }
278 super.toSerializable(event, writer);
279 }
280 }