001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017package org.apache.logging.log4j.core.pattern; 018 019import java.util.List; 020 021import org.apache.logging.log4j.core.LogEvent; 022import org.apache.logging.log4j.core.config.Configuration; 023import org.apache.logging.log4j.core.config.plugins.Plugin; 024import org.apache.logging.log4j.core.layout.PatternLayout; 025import org.apache.logging.log4j.util.EnglishEnums; 026import org.apache.logging.log4j.util.PerformanceSensitive; 027import org.apache.logging.log4j.util.StringBuilders; 028 029/** 030 * Converter that encodes the output from a pattern using a specified format. Supported formats include HTML 031 * (default) and JSON. 032 */ 033@Plugin(name = "encode", category = PatternConverter.CATEGORY) 034@ConverterKeys({"enc", "encode"}) 035@PerformanceSensitive("allocation") 036public final class EncodingPatternConverter extends LogEventPatternConverter { 037 038 private final List<PatternFormatter> formatters; 039 private final EscapeFormat escapeFormat; 040 041 /** 042 * Private constructor. 043 * 044 * @param formatters the PatternFormatters to generate the text to manipulate. 045 * @param escapeFormat the escape format strategy to use for encoding output of formatters 046 */ 047 private EncodingPatternConverter(final List<PatternFormatter> formatters, 048 final EscapeFormat escapeFormat) { 049 super("encode", "encode"); 050 this.formatters = formatters; 051 this.escapeFormat = escapeFormat; 052 } 053 054 /** 055 * Creates an EncodingPatternConverter using a pattern string and an optional escape format. 056 * 057 * @param config the current Configuration 058 * @param options first option is the nested pattern format; second option is the escape format (optional) 059 * @return instance of pattern converter. 060 */ 061 public static EncodingPatternConverter newInstance(final Configuration config, final String[] options) { 062 if (options.length > 2 || options.length == 0) { 063 LOGGER.error("Incorrect number of options on escape. Expected 1 or 2, but received {}", 064 options.length); 065 return null; 066 } 067 if (options[0] == null) { 068 LOGGER.error("No pattern supplied on escape"); 069 return null; 070 } 071 final EscapeFormat escapeFormat = options.length < 2 ? EscapeFormat.HTML 072 : EnglishEnums.valueOf(EscapeFormat.class, options[1], EscapeFormat.HTML); 073 final PatternParser parser = PatternLayout.createPatternParser(config); 074 final List<PatternFormatter> formatters = parser.parse(options[0]); 075 return new EncodingPatternConverter(formatters, escapeFormat); 076 } 077 078 /** 079 * {@inheritDoc} 080 */ 081 @Override 082 public void format(final LogEvent event, final StringBuilder toAppendTo) { 083 final int start = toAppendTo.length(); 084 for (int i = 0; i < formatters.size(); i++) { 085 formatters.get(i).format(event, toAppendTo); 086 } 087 escapeFormat.escape(toAppendTo, start); 088 } 089 090 private enum EscapeFormat { 091 HTML { 092 @Override 093 void escape(final StringBuilder toAppendTo, final int start) { 094 for (int i = toAppendTo.length() - 1; i >= start; i--) { // backwards: length may change 095 final char c = toAppendTo.charAt(i); 096 switch (c) { 097 case '\r': 098 toAppendTo.setCharAt(i, '\\'); 099 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}