View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core.layout;
18  
19  import java.util.HashSet;
20  import java.util.Set;
21  
22  import javax.xml.stream.XMLStreamException;
23  
24  import com.fasterxml.jackson.databind.ser.FilterProvider;
25  import org.apache.logging.log4j.core.impl.Log4jLogEvent;
26  import org.apache.logging.log4j.core.jackson.JsonConstants;
27  import org.apache.logging.log4j.core.jackson.Log4jJsonObjectMapper;
28  import org.apache.logging.log4j.core.jackson.Log4jXmlObjectMapper;
29  import org.apache.logging.log4j.core.jackson.Log4jYamlObjectMapper;
30  import org.apache.logging.log4j.core.jackson.XmlConstants;
31  import org.codehaus.stax2.XMLStreamWriter2;
32  
33  import com.fasterxml.jackson.core.PrettyPrinter;
34  import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
35  import com.fasterxml.jackson.core.util.MinimalPrettyPrinter;
36  import com.fasterxml.jackson.databind.ObjectMapper;
37  import com.fasterxml.jackson.databind.ObjectWriter;
38  import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
39  import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
40  import com.fasterxml.jackson.dataformat.xml.util.DefaultXmlPrettyPrinter;
41  
42  abstract class JacksonFactory {
43  
44      static class JSON extends JacksonFactory {
45  
46          private final boolean encodeThreadContextAsList;
47          private final boolean includeStacktrace;
48          private final boolean stacktraceAsString;
49          private final boolean objectMessageAsJsonObject;
50  
51          public JSON(final boolean encodeThreadContextAsList, final boolean includeStacktrace, final boolean stacktraceAsString, final boolean objectMessageAsJsonObject) {
52              this.encodeThreadContextAsList = encodeThreadContextAsList;
53              this.includeStacktrace = includeStacktrace;
54              this.stacktraceAsString = stacktraceAsString;
55              this.objectMessageAsJsonObject = objectMessageAsJsonObject;
56          }
57  
58          @Override
59          protected String getPropertNameForContextMap() {
60              return JsonConstants.ELT_CONTEXT_MAP;
61          }
62  
63          @Override
64          protected String getPropertyNameForTimeMillis() {
65              return JsonConstants.ELT_TIME_MILLIS;
66          }
67  
68          @Override
69          protected String getPropertyNameForInstant() {
70              return JsonConstants.ELT_INSTANT;
71          }
72  
73          @Override
74          protected String getPropertNameForSource() {
75              return JsonConstants.ELT_SOURCE;
76          }
77  
78          @Override
79          protected String getPropertNameForNanoTime() {
80              return JsonConstants.ELT_NANO_TIME;
81          }
82  
83          @Override
84          protected PrettyPrinter newCompactPrinter() {
85              return new MinimalPrettyPrinter();
86          }
87  
88          @Override
89          protected ObjectMapper newObjectMapper() {
90              return new Log4jJsonObjectMapper(encodeThreadContextAsList, includeStacktrace, stacktraceAsString, objectMessageAsJsonObject);
91          }
92  
93          @Override
94          protected PrettyPrinter newPrettyPrinter() {
95              return new DefaultPrettyPrinter();
96          }
97  
98      }
99  
100     static class XML extends JacksonFactory {
101 
102         static final int DEFAULT_INDENT = 1;
103 
104         private final boolean includeStacktrace;
105         private final boolean stacktraceAsString;
106 
107 
108         public XML(final boolean includeStacktrace, final boolean stacktraceAsString) {
109             this.includeStacktrace = includeStacktrace;
110             this.stacktraceAsString = stacktraceAsString;
111         }
112 
113         @Override
114         protected String getPropertyNameForTimeMillis() {
115             return XmlConstants.ELT_TIME_MILLIS;
116         }
117 
118         @Override
119         protected String getPropertyNameForInstant() {
120             return XmlConstants.ELT_INSTANT;
121         }
122 
123         @Override
124         protected String getPropertNameForContextMap() {
125             return XmlConstants.ELT_CONTEXT_MAP;
126         }
127 
128         @Override
129         protected String getPropertNameForSource() {
130             return XmlConstants.ELT_SOURCE;
131         }
132 
133         @Override
134         protected String getPropertNameForNanoTime() {
135             return JsonConstants.ELT_NANO_TIME;
136         }
137 
138         @Override
139         protected PrettyPrinter newCompactPrinter() {
140             // Yes, null is the proper answer.
141             return null;
142         }
143 
144         @Override
145         protected ObjectMapper newObjectMapper() {
146             return new Log4jXmlObjectMapper(includeStacktrace, stacktraceAsString);
147         }
148 
149         @Override
150         protected PrettyPrinter newPrettyPrinter() {
151             return new Log4jXmlPrettyPrinter(DEFAULT_INDENT);
152         }
153     }
154 
155     static class YAML extends JacksonFactory {
156 
157         private final boolean includeStacktrace;
158         private final boolean stacktraceAsString;
159 
160 
161         public YAML(final boolean includeStacktrace, final boolean stacktraceAsString) {
162             this.includeStacktrace = includeStacktrace;
163             this.stacktraceAsString = stacktraceAsString;
164         }
165 
166         @Override
167         protected String getPropertyNameForTimeMillis() {
168             return JsonConstants.ELT_TIME_MILLIS;
169         }
170 
171         @Override
172         protected String getPropertyNameForInstant() {
173             return JsonConstants.ELT_INSTANT;
174         }
175 
176         @Override
177         protected String getPropertNameForContextMap() {
178             return JsonConstants.ELT_CONTEXT_MAP;
179         }
180 
181         @Override
182         protected String getPropertNameForSource() {
183             return JsonConstants.ELT_SOURCE;
184         }
185 
186         @Override
187         protected String getPropertNameForNanoTime() {
188             return JsonConstants.ELT_NANO_TIME;
189         }
190 
191         @Override
192         protected PrettyPrinter newCompactPrinter() {
193             return new MinimalPrettyPrinter();
194         }
195 
196         @Override
197         protected ObjectMapper newObjectMapper() {
198             return new Log4jYamlObjectMapper(false, includeStacktrace, stacktraceAsString);
199         }
200 
201         @Override
202         protected PrettyPrinter newPrettyPrinter() {
203             return new DefaultPrettyPrinter();
204         }
205     }
206 
207     /**
208      * When <Event>s are written into a XML file; the "Event" object is not the root element, but an element named
209      * <Events> created using {@link XmlLayout#getHeader()} and {@link XmlLayout#getFooter()} methods.
210      * <p>
211      * {@link com.fasterxml.jackson.dataformat.xml.util.DefaultXmlPrettyPrinter} is used to print the Event object into
212      * XML; hence it assumes &lt;Event&gt; tag as the root element, so it prints the &lt;Event&gt; tag without any
213      * indentation. To add an indentation to the &lt;Event&gt; tag; hence an additional indentation for any
214      * sub-elements, this class is written. As an additional task, to avoid the blank line printed after the ending
215      * &lt;/Event&gt; tag, {@link #writePrologLinefeed(XMLStreamWriter2)} method is also overridden.
216      * </p>
217      */
218     static class Log4jXmlPrettyPrinter extends DefaultXmlPrettyPrinter {
219 
220         private static final long serialVersionUID = 1L;
221 
222         Log4jXmlPrettyPrinter(final int nesting) {
223             _nesting = nesting;
224         }
225 
226         @Override
227         public void writePrologLinefeed(final XMLStreamWriter2 sw) throws XMLStreamException {
228             // nothing
229         }
230 
231         /**
232          * Sets the nesting level to 1 rather than 0, so the "Event" tag will get indentation of next level below root.
233          */
234         @Override
235         public DefaultXmlPrettyPrinter createInstance() {
236             return new Log4jXmlPrettyPrinter(XML.DEFAULT_INDENT);
237         }
238 
239     }
240 
241     abstract protected String getPropertyNameForTimeMillis();
242 
243     abstract protected String getPropertyNameForInstant();
244 
245     abstract protected String getPropertNameForContextMap();
246 
247     abstract protected String getPropertNameForSource();
248 
249     abstract protected String getPropertNameForNanoTime();
250 
251     abstract protected PrettyPrinter newCompactPrinter();
252 
253     abstract protected ObjectMapper newObjectMapper();
254 
255     abstract protected PrettyPrinter newPrettyPrinter();
256 
257     ObjectWriter newWriter(final boolean locationInfo, final boolean properties, final boolean compact) {
258         return newWriter(locationInfo, properties, compact, false);
259     }
260 
261     ObjectWriter newWriter(final boolean locationInfo, final boolean properties, final boolean compact,
262             final boolean includeMillis) {
263         final SimpleFilterProvider filters = new SimpleFilterProvider();
264         final Set<String> except = new HashSet<>(3);
265         if (!locationInfo) {
266             except.add(this.getPropertNameForSource());
267         }
268         if (!properties) {
269             except.add(this.getPropertNameForContextMap());
270         }
271         if (includeMillis) {
272             except.add(getPropertyNameForInstant());
273         } else {
274             except.add(getPropertyNameForTimeMillis());
275         }
276         except.add(this.getPropertNameForNanoTime());
277         filters.addFilter(Log4jLogEvent.class.getName(), SimpleBeanPropertyFilter.serializeAllExcept(except));
278         final ObjectWriter writer = this.newObjectMapper().writer(compact ? this.newCompactPrinter() : this.newPrettyPrinter());
279         return writer.with(filters);
280     }
281 
282 }