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.Arrays; 020import java.util.List; 021 022import org.apache.logging.log4j.core.LogEvent; 023import org.apache.logging.log4j.core.config.Configuration; 024import org.apache.logging.log4j.core.config.plugins.Plugin; 025import org.apache.logging.log4j.core.layout.PatternLayout; 026import org.apache.logging.log4j.core.util.Patterns; 027import org.apache.logging.log4j.util.PerformanceSensitive; 028 029/** 030 * Style pattern converter. Adds ANSI color styling to the result of the enclosed pattern. 031 * 032 * <p> 033 * To disable ANSI output unconditionally, specify an additional option <code>disableAnsi=true</code>, or to 034 * disable ANSI output if no console is detected, specify option <code>noConsoleNoAnsi=true</code>. 035 * </p> 036 */ 037@Plugin(name = "style", category = PatternConverter.CATEGORY) 038@ConverterKeys({ "style" }) 039@PerformanceSensitive("allocation") 040public final class StyleConverter extends LogEventPatternConverter implements AnsiConverter { 041 042 private final List<PatternFormatter> patternFormatters; 043 044 private final boolean noAnsi; 045 046 private final String style; 047 048 private final String defaultStyle; 049 050 /** 051 * Constructs the converter. 052 * 053 * @param patternFormatters 054 * The PatternFormatters to generate the text to manipulate. 055 * @param style 056 * The style that should encapsulate the pattern. 057 * @param noAnsi 058 * If true, do not output ANSI escape codes. 059 */ 060 private StyleConverter(final List<PatternFormatter> patternFormatters, final String style, final boolean noAnsi) { 061 super("style", "style"); 062 this.patternFormatters = patternFormatters; 063 this.style = style; 064 this.defaultStyle = AnsiEscape.getDefaultStyle(); 065 this.noAnsi = noAnsi; 066 } 067 068 /** 069 * Gets an instance of the class. 070 * 071 * @param config 072 * The current Configuration. 073 * @param options 074 * pattern options, may be null. If first element is "short", only the first line of the throwable will 075 * be formatted. 076 * @return instance of class. 077 */ 078 public static StyleConverter newInstance(final Configuration config, final String[] options) { 079 if (options == null) { 080 return null; 081 } 082 if (options.length < 2) { 083 LOGGER.error("Incorrect number of options on style. Expected at least 1, received " + options.length); 084 return null; 085 } 086 if (options[0] == null) { 087 LOGGER.error("No pattern supplied for style converter"); 088 return null; 089 } 090 if (options[1] == null) { 091 LOGGER.error("No style attributes supplied for style converter"); 092 return null; 093 } 094 final PatternParser parser = PatternLayout.createPatternParser(config); 095 final List<PatternFormatter> formatters = parser.parse(options[0]); 096 final String style = AnsiEscape.createSequence(options[1].split(Patterns.COMMA_SEPARATOR)); 097 final boolean disableAnsi = Arrays.toString(options).contains(PatternParser.DISABLE_ANSI + "=true"); 098 final boolean noConsoleNoAnsi = Arrays.toString(options).contains(PatternParser.NO_CONSOLE_NO_ANSI + "=true"); 099 final boolean hideAnsi = disableAnsi || (noConsoleNoAnsi && System.console() == null); 100 return new StyleConverter(formatters, style, hideAnsi); 101 } 102 103 /** 104 * {@inheritDoc} 105 */ 106 @Override 107 public void format(final LogEvent event, final StringBuilder toAppendTo) { 108 int start = 0; 109 int end = 0; 110 if (!noAnsi) { // use ANSI: set prefix 111 start = toAppendTo.length(); 112 toAppendTo.append(style); 113 end = toAppendTo.length(); 114 } 115 116 //noinspection ForLoopReplaceableByForEach 117 for (int i = 0, size = patternFormatters.size(); i < size; i++) { 118 patternFormatters.get(i).format(event, toAppendTo); 119 } 120 121 // if we use ANSI we need to add the postfix or erase the unnecessary prefix 122 if (!noAnsi) { 123 if (toAppendTo.length() == end) { 124 toAppendTo.setLength(start); // erase prefix 125 } else { 126 toAppendTo.append(defaultStyle); // add postfix 127 } 128 } 129 } 130 131 @Override 132 public boolean handlesThrowable() { 133 for (final PatternFormatter formatter : patternFormatters) { 134 if (formatter.handlesThrowable()) { 135 return true; 136 } 137 } 138 return false; 139 } 140 141 /** 142 * Returns a String suitable for debugging. 143 * 144 * @return a String suitable for debugging. 145 */ 146 @Override 147 public String toString() { 148 final StringBuilder sb = new StringBuilder(); 149 sb.append(super.toString()); 150 sb.append("[style="); 151 sb.append(style); 152 sb.append(", defaultStyle="); 153 sb.append(defaultStyle); 154 sb.append(", patternFormatters="); 155 sb.append(patternFormatters); 156 sb.append(", noAnsi="); 157 sb.append(noAnsi); 158 sb.append(']'); 159 return sb.toString(); 160 } 161 162}