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.pattern;
18  
19  import java.util.List;
20  
21  import org.apache.logging.log4j.core.LogEvent;
22  import org.apache.logging.log4j.core.config.Configuration;
23  import org.apache.logging.log4j.core.config.plugins.Plugin;
24  import org.apache.logging.log4j.core.layout.PatternLayout;
25  import org.apache.logging.log4j.util.EnglishEnums;
26  import org.apache.logging.log4j.util.PerformanceSensitive;
27  import org.apache.logging.log4j.util.StringBuilders;
28  
29  /**
30   * Converter that encodes the output from a pattern using a specified format. Supported formats include HTML
31   * (default) and JSON.
32   */
33  @Plugin(name = "encode", category = PatternConverter.CATEGORY)
34  @ConverterKeys({"enc", "encode"})
35  @PerformanceSensitive("allocation")
36  public final class EncodingPatternConverter extends LogEventPatternConverter {
37  
38      private final List<PatternFormatter> formatters;
39      private final EscapeFormat escapeFormat;
40  
41      /**
42       * Private constructor.
43       *
44       * @param formatters   the PatternFormatters to generate the text to manipulate.
45       * @param escapeFormat the escape format strategy to use for encoding output of formatters
46       */
47      private EncodingPatternConverter(final List<PatternFormatter> formatters,
48                                       final EscapeFormat escapeFormat) {
49          super("encode", "encode");
50          this.formatters = formatters;
51          this.escapeFormat = escapeFormat;
52      }
53  
54      /**
55       * Creates an EncodingPatternConverter using a pattern string and an optional escape format.
56       *
57       * @param config  the current Configuration
58       * @param options first option is the nested pattern format; second option is the escape format (optional)
59       * @return instance of pattern converter.
60       */
61      public static EncodingPatternConverter newInstance(final Configuration config, final String[] options) {
62          if (options.length > 2 || options.length == 0) {
63              LOGGER.error("Incorrect number of options on escape. Expected 1 or 2, but received {}",
64                  options.length);
65              return null;
66          }
67          if (options[0] == null) {
68              LOGGER.error("No pattern supplied on escape");
69              return null;
70          }
71          final EscapeFormat escapeFormat = options.length < 2 ? EscapeFormat.HTML
72              : EnglishEnums.valueOf(EscapeFormat.class, options[1], EscapeFormat.HTML);
73          final PatternParser parser = PatternLayout.createPatternParser(config);
74          final List<PatternFormatter> formatters = parser.parse(options[0]);
75          return new EncodingPatternConverter(formatters, escapeFormat);
76      }
77  
78      /**
79       * {@inheritDoc}
80       */
81      @Override
82      public void format(final LogEvent event, final StringBuilder toAppendTo) {
83          final int start = toAppendTo.length();
84          for (int i = 0; i < formatters.size(); i++) {
85              formatters.get(i).format(event, toAppendTo);
86          }
87          escapeFormat.escape(toAppendTo, start);
88      }
89  
90      private enum EscapeFormat {
91          HTML {
92              @Override
93              void escape(final StringBuilder toAppendTo, final int start) {
94                  for (int i = toAppendTo.length() - 1; i >= start; i--) { // backwards: length may change
95                      final char c = toAppendTo.charAt(i);
96                      switch (c) {
97                          case '\r':
98                              toAppendTo.setCharAt(i, '\\');
99                              toAppendTo.insert(i + 1, 'r');
100                             break;
101                         case '\n':
102                             toAppendTo.setCharAt(i, '\\');
103                             toAppendTo.insert(i + 1, 'n');
104                             break;
105                         case '&':
106                             toAppendTo.setCharAt(i, '&');
107                             toAppendTo.insert(i + 1, "amp;");
108                             break;
109                         case '<':
110                             toAppendTo.setCharAt(i, '&');
111                             toAppendTo.insert(i + 1, "lt;");
112                             break;
113                         case '>':
114                             toAppendTo.setCharAt(i, '&');
115                             toAppendTo.insert(i + 1, "gt;");
116                             break;
117                         case '"':
118                             toAppendTo.setCharAt(i, '&');
119                             toAppendTo.insert(i + 1, "quot;");
120                             break;
121                         case '\'':
122                             toAppendTo.setCharAt(i, '&');
123                             toAppendTo.insert(i + 1, "apos;");
124                             break;
125                         case '/':
126                             toAppendTo.setCharAt(i, '&');
127                             toAppendTo.insert(i + 1, "#x2F;");
128                             break;
129                     }
130                 }
131             }
132         },
133 
134         /**
135          * JSON string escaping as defined in RFC 4627.
136          *
137          * @see <a href="https://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>
138          */
139         JSON {
140             @Override
141             void escape(final StringBuilder toAppendTo, final int start) {
142                 StringBuilders.escapeJson(toAppendTo, start);
143             }
144         },
145 
146         CRLF {
147             @Override
148             void escape(final StringBuilder toAppendTo, final int start) {
149                 for (int i = toAppendTo.length() - 1; i >= start; i--) { // backwards: length may change
150                     final char c = toAppendTo.charAt(i);
151                     switch (c) {
152                         case '\r':
153                             toAppendTo.setCharAt(i, '\\');
154                             toAppendTo.insert(i + 1, 'r');
155                             break;
156                         case '\n':
157                             toAppendTo.setCharAt(i, '\\');
158                             toAppendTo.insert(i + 1, 'n');
159                             break;
160                     }
161                 }
162             }
163         },
164 
165         /**
166          * XML string escaping as defined in XML specification.
167          *
168          * @see <a href="https://www.w3.org/TR/xml/">XML specification</a>
169          */
170         XML {
171             @Override
172             void escape(final StringBuilder toAppendTo, final int start) {
173                 StringBuilders.escapeXml(toAppendTo, start);
174             }
175         };
176 
177         /**
178          * Escapes text using a standardized format from a given starting point to the end of the string.
179          *
180          * @param toAppendTo string buffer to escape
181          * @param start      where to start escaping from
182          */
183         abstract void escape(final StringBuilder toAppendTo, final int start);
184     }
185 }