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.nio.charset.Charset;
20  import java.nio.charset.StandardCharsets;
21  import java.text.SimpleDateFormat;
22  import java.util.Date;
23  import java.util.HashMap;
24  import java.util.Locale;
25  import java.util.Map;
26  import java.util.regex.Matcher;
27  import java.util.regex.Pattern;
28  
29  import org.apache.logging.log4j.core.Layout;
30  import org.apache.logging.log4j.core.LogEvent;
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.net.Facility;
36  import org.apache.logging.log4j.core.net.Priority;
37  import org.apache.logging.log4j.core.util.NetUtils;
38  import org.apache.logging.log4j.util.Chars;
39  
40  /**
41   * Formats a log event as a BSD Log record.
42   */
43  @Plugin(name = "SyslogLayout", category = Node.CATEGORY, elementType = Layout.ELEMENT_TYPE, printObject = true)
44  public final class SyslogLayout extends AbstractStringLayout {
45  
46      /**
47       * Builds a SyslogLayout.
48       * <p>The main arguments are</p>
49       * <ul>
50       * <li>facility: The Facility is used to try to classify the message.</li>
51       * <li>includeNewLine: If true a newline will be appended to the result.</li>
52       * <li>escapeNL: Pattern to use for replacing newlines.</li>
53       * <li>charset: The character set.</li>
54       * </ul>
55       * @param <B> the builder type
56       */
57      public static class Builder<B extends Builder<B>> extends AbstractStringLayout.Builder<B>
58              implements org.apache.logging.log4j.core.util.Builder<SyslogLayout> {
59  
60          public Builder() {
61              super();
62              setCharset(StandardCharsets.UTF_8);
63          }
64  
65          @PluginBuilderAttribute
66          private Facility facility = Facility.LOCAL0;
67  
68          @PluginBuilderAttribute("newLine")
69          private boolean includeNewLine;
70  
71          @PluginBuilderAttribute("newLineEscape")
72          private String escapeNL;
73  
74          @Override
75          public SyslogLayout build() {
76              return new SyslogLayout(facility, includeNewLine, escapeNL, getCharset());
77          }
78  
79          public Facility getFacility() {
80              return facility;
81          }
82  
83          public boolean isIncludeNewLine() {
84              return includeNewLine;
85          }
86  
87          public String getEscapeNL() {
88              return escapeNL;
89          }
90  
91          public B setFacility(final Facility facility) {
92              this.facility = facility;
93              return asBuilder();
94          }
95  
96          public B setIncludeNewLine(final boolean includeNewLine) {
97              this.includeNewLine = includeNewLine;
98              return asBuilder();
99          }
100 
101         public B setEscapeNL(final String escapeNL) {
102             this.escapeNL = escapeNL;
103             return asBuilder();
104         }
105 
106     }
107 
108     @PluginBuilderFactory
109     public static <B extends Builder<B>> B newBuilder() {
110         return new Builder<B>().asBuilder();
111     }
112 
113     /**
114      * Match newlines in a platform-independent manner.
115      */
116     public static final Pattern NEWLINE_PATTERN = Pattern.compile("\\r?\\n");
117 
118     private final Facility facility;
119     private final boolean includeNewLine;
120     private final String escapeNewLine;
121 
122     /**
123      * Date format used if header = true.
124      */
125     private final SimpleDateFormat dateFormat = new SimpleDateFormat("MMM dd HH:mm:ss", Locale.ENGLISH);
126 
127     /**
128      * Host name used to identify messages from this appender.
129      */
130     private final String localHostname = NetUtils.getLocalHostname();
131 
132     protected SyslogLayout(final Facility facility, final boolean includeNL, final String escapeNL, final Charset charset) {
133         super(charset);
134         this.facility = facility;
135         this.includeNewLine = includeNL;
136         this.escapeNewLine = escapeNL == null ? null : Matcher.quoteReplacement(escapeNL);
137     }
138 
139     /**
140      * Formats a {@link org.apache.logging.log4j.core.LogEvent} in conformance with the BSD Log record format.
141      *
142      * @param event The LogEvent
143      * @return the event formatted as a String.
144      */
145     @Override
146     public String toSerializable(final LogEvent event) {
147         final StringBuilder buf = getStringBuilder();
148 
149         buf.append('<');
150         buf.append(Priority.getPriority(facility, event.getLevel()));
151         buf.append('>');
152         addDate(event.getTimeMillis(), buf);
153         buf.append(Chars.SPACE);
154         buf.append(localHostname);
155         buf.append(Chars.SPACE);
156 
157         String message = event.getMessage().getFormattedMessage();
158         if (null != escapeNewLine) {
159             message = NEWLINE_PATTERN.matcher(message).replaceAll(escapeNewLine);
160         }
161         buf.append(message);
162 
163         if (includeNewLine) {
164             buf.append('\n');
165         }
166         return buf.toString();
167     }
168 
169     private synchronized void addDate(final long timestamp, final StringBuilder buf) {
170         final int index = buf.length() + 4;
171         buf.append(dateFormat.format(new Date(timestamp)));
172         //  RFC 3164 says leading space, not leading zero on days 1-9
173         if (buf.charAt(index) == '0') {
174             buf.setCharAt(index, Chars.SPACE);
175         }
176     }
177 
178     /**
179      * Gets this SyslogLayout's content format. Specified by:
180      * <ul>
181      * <li>Key: "structured" Value: "false"</li>
182      * <li>Key: "dateFormat" Value: "MMM dd HH:mm:ss"</li>
183      * <li>Key: "format" Value: "&lt;LEVEL&gt;TIMESTAMP PROP(HOSTNAME) MESSAGE"</li>
184      * <li>Key: "formatType" Value: "logfilepatternreceiver" (format uses the keywords supported by
185      * LogFilePatternReceiver)</li>
186      * </ul>
187      *
188      * @return Map of content format keys supporting SyslogLayout
189      */
190     @Override
191     public Map<String, String> getContentFormat() {
192         final Map<String, String> result = new HashMap<>();
193         result.put("structured", "false");
194         result.put("formatType", "logfilepatternreceiver");
195         result.put("dateFormat", dateFormat.toPattern());
196         result.put("format", "<LEVEL>TIMESTAMP PROP(HOSTNAME) MESSAGE");
197         return result;
198     }
199 
200     /**
201      * Creates a SyslogLayout.
202      *
203      * @param facility The Facility is used to try to classify the message.
204      * @param includeNewLine If true a newline will be appended to the result.
205      * @param escapeNL Pattern to use for replacing newlines.
206      * @param charset The character set.
207      * @return A SyslogLayout.
208      * @deprecated Use {@link #newBuilder()}.
209      */
210     @Deprecated
211     public static SyslogLayout createLayout(final Facility facility, final boolean includeNewLine,
212                                             final String escapeNL, final Charset charset) {
213         return new SyslogLayout(facility, includeNewLine, escapeNL, charset);
214     }
215 
216     /**
217      * Gets the facility.
218      *
219      * @return the facility
220      */
221     public Facility getFacility() {
222         return facility;
223     }
224 }