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.ArrayList; 020import java.util.List; 021import java.util.Locale; 022 023import org.apache.logging.log4j.Logger; 024import org.apache.logging.log4j.core.LogEvent; 025import org.apache.logging.log4j.core.config.Configuration; 026import org.apache.logging.log4j.core.config.plugins.Plugin; 027import org.apache.logging.log4j.core.util.Loader; 028import org.apache.logging.log4j.message.Message; 029import org.apache.logging.log4j.message.MultiformatMessage; 030import org.apache.logging.log4j.status.StatusLogger; 031import org.apache.logging.log4j.util.MultiFormatStringBuilderFormattable; 032import org.apache.logging.log4j.util.PerformanceSensitive; 033import org.apache.logging.log4j.util.StringBuilderFormattable; 034 035/** 036 * Returns the event's rendered message in a StringBuilder. 037 */ 038@Plugin(name = "MessagePatternConverter", category = PatternConverter.CATEGORY) 039@ConverterKeys({ "m", "msg", "message" }) 040@PerformanceSensitive("allocation") 041public class MessagePatternConverter extends LogEventPatternConverter { 042 043 private static final String LOOKUPS = "lookups"; 044 private static final String NOLOOKUPS = "nolookups"; 045 046 private MessagePatternConverter() { 047 super("Message", "message"); 048 } 049 050 private static TextRenderer loadMessageRenderer(final String[] options) { 051 if (options != null) { 052 for (final String option : options) { 053 switch (option.toUpperCase(Locale.ROOT)) { 054 case "ANSI": 055 if (Loader.isJansiAvailable()) { 056 return new JAnsiTextRenderer(options, JAnsiTextRenderer.DefaultMessageStyleMap); 057 } 058 StatusLogger.getLogger() 059 .warn("You requested ANSI message rendering but JANSI is not on the classpath."); 060 return null; 061 case "HTML": 062 return new HtmlTextRenderer(options); 063 } 064 } 065 } 066 return null; 067 } 068 069 /** 070 * Obtains an instance of pattern converter. 071 * 072 * @param config 073 * The Configuration. 074 * @param options 075 * options, may be null. 076 * @return instance of pattern converter. 077 */ 078 public static MessagePatternConverter newInstance(final Configuration config, final String[] options) { 079 String[] formats = withoutLookupOptions(options); 080 TextRenderer textRenderer = loadMessageRenderer(formats); 081 MessagePatternConverter result = formats == null || formats.length == 0 082 ? SimpleMessagePatternConverter.INSTANCE 083 : new FormattedMessagePatternConverter(formats); 084 if (textRenderer != null) { 085 result = new RenderingPatternConverter(result, textRenderer); 086 } 087 return result; 088 } 089 090 private static String[] withoutLookupOptions(final String[] options) { 091 if (options == null || options.length == 0) { 092 return options; 093 } 094 List<String> results = new ArrayList<>(options.length); 095 for (String option : options) { 096 if (LOOKUPS.equalsIgnoreCase(option) || NOLOOKUPS.equalsIgnoreCase(option)) { 097 LOGGER.info("The {} option will be ignored. Message Lookups are no longer supported.", option); 098 } else { 099 results.add(option); 100 } 101 } 102 return results.toArray(new String[0]); 103 } 104 105 @Override 106 public void format(final LogEvent event, final StringBuilder toAppendTo) { 107 throw new UnsupportedOperationException(); 108 } 109 110 private static final class SimpleMessagePatternConverter extends MessagePatternConverter { 111 private static final MessagePatternConverter INSTANCE = new SimpleMessagePatternConverter(); 112 113 /** 114 * {@inheritDoc} 115 */ 116 @Override 117 public void format(final LogEvent event, final StringBuilder toAppendTo) { 118 Message msg = event.getMessage(); 119 if (msg instanceof StringBuilderFormattable) { 120 ((StringBuilderFormattable) msg).formatTo(toAppendTo); 121 } else if (msg != null) { 122 toAppendTo.append(msg.getFormattedMessage()); 123 } 124 } 125 } 126 127 private static final class FormattedMessagePatternConverter extends MessagePatternConverter { 128 129 private final String[] formats; 130 131 FormattedMessagePatternConverter(final String[] formats) { 132 this.formats = formats; 133 } 134 135 /** 136 * {@inheritDoc} 137 */ 138 @Override 139 public void format(final LogEvent event, final StringBuilder toAppendTo) { 140 Message msg = event.getMessage(); 141 if (msg instanceof StringBuilderFormattable) { 142 if (msg instanceof MultiFormatStringBuilderFormattable) { 143 ((MultiFormatStringBuilderFormattable) msg).formatTo(formats, toAppendTo); 144 } else { 145 ((StringBuilderFormattable) msg).formatTo(toAppendTo); 146 } 147 } else if (msg != null) { 148 toAppendTo.append(msg instanceof MultiformatMessage 149 ? ((MultiformatMessage) msg).getFormattedMessage(formats) 150 : msg.getFormattedMessage()); 151 } 152 } 153 } 154 155 private static final class RenderingPatternConverter extends MessagePatternConverter { 156 157 private final MessagePatternConverter delegate; 158 private final TextRenderer textRenderer; 159 160 RenderingPatternConverter(final MessagePatternConverter delegate, final TextRenderer textRenderer) { 161 this.delegate = delegate; 162 this.textRenderer = textRenderer; 163 } 164 165 /** 166 * {@inheritDoc} 167 */ 168 @Override 169 public void format(final LogEvent event, final StringBuilder toAppendTo) { 170 StringBuilder workingBuilder = new StringBuilder(80); 171 delegate.format(event, workingBuilder); 172 textRenderer.render(workingBuilder, toAppendTo); 173 } 174 175 } 176}