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