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 */ 017 package org.apache.logging.log4j.core.appender; 018 019 import org.apache.logging.log4j.core.Filter; 020 import org.apache.logging.log4j.core.Layout; 021 import org.apache.logging.log4j.core.config.plugins.Plugin; 022 import org.apache.logging.log4j.core.config.plugins.PluginAttr; 023 import org.apache.logging.log4j.core.config.plugins.PluginElement; 024 import org.apache.logging.log4j.core.config.plugins.PluginFactory; 025 import org.apache.logging.log4j.core.helpers.Loader; 026 import org.apache.logging.log4j.core.layout.PatternLayout; 027 import org.apache.logging.log4j.util.PropertiesUtil; 028 029 import java.io.IOException; 030 import java.io.OutputStream; 031 import java.io.PrintStream; 032 import java.lang.reflect.Constructor; 033 034 /** 035 * ConsoleAppender appends log events to <code>System.out</code> or 036 * <code>System.err</code> using a layout specified by the user. The 037 * default target is <code>System.out</code>. 038 * @doubt accessing System.out or .err as a byte stream instead of a writer 039 * bypasses the JVM's knowledge of the proper encoding. (RG) Encoding 040 * is handled within the Layout. Typically, a Layout will generate a String 041 * and then call getBytes which may use a configured encoding or the system 042 * default. OTOH, a Writer cannot print byte streams. 043 */ 044 @Plugin(name = "Console", type = "Core", elementType = "appender", printObject = true) 045 public final class ConsoleAppender extends AbstractOutputStreamAppender { 046 047 private static ConsoleManagerFactory factory = new ConsoleManagerFactory(); 048 049 /** 050 * Enumeration of console destinations. 051 */ 052 public enum Target { 053 /** Standard output. */ 054 SYSTEM_OUT, 055 /** Standard error output. */ 056 SYSTEM_ERR 057 } 058 059 private ConsoleAppender(final String name, final Layout layout, final Filter filter, 060 final OutputStreamManager manager, 061 final boolean handleExceptions) { 062 super(name, layout, filter, handleExceptions, true, manager); 063 } 064 065 /** 066 * Create a Console Appender. 067 * @param layout The layout to use (required). 068 * @param filter The Filter or null. 069 * @param t The target ("SYSTEM_OUT" or "SYSTEM_ERR"). The default is "SYSTEM_OUT". 070 * @param follow If true will follow changes to the underlying output stream. 071 * @param name The name of the Appender (required). 072 * @param suppress "true" if exceptions should be hidden from the application, "false" otherwise. 073 * The default is "true". 074 * @return The ConsoleAppender. 075 */ 076 @PluginFactory 077 public static ConsoleAppender createAppender(@PluginElement("layout") Layout layout, 078 @PluginElement("filters") final Filter filter, 079 @PluginAttr("target") final String t, 080 @PluginAttr("name") final String name, 081 @PluginAttr("follow") final String follow, 082 @PluginAttr("suppressExceptions") final String suppress) { 083 if (name == null) { 084 LOGGER.error("No name provided for ConsoleAppender"); 085 return null; 086 } 087 if (layout == null) { 088 layout = PatternLayout.createLayout(null, null, null, null); 089 } 090 final boolean isFollow = follow == null ? false : Boolean.valueOf(follow); 091 final boolean handleExceptions = suppress == null ? true : Boolean.valueOf(suppress); 092 final Target target = t == null ? Target.SYSTEM_OUT : Target.valueOf(t); 093 return new ConsoleAppender(name, layout, filter, getManager(isFollow, target), handleExceptions); 094 } 095 096 private static OutputStreamManager getManager(final boolean follow, final Target target) { 097 final String type = target.name(); 098 final OutputStream os = getOutputStream(follow, target); 099 return OutputStreamManager.getManager(target.name() + "." + follow, new FactoryData(os, type), factory); 100 } 101 102 private static OutputStream getOutputStream(final boolean follow, final Target target) { 103 final PrintStream printStream = target == Target.SYSTEM_OUT ? 104 follow ? new PrintStream(new SystemOutStream()) : System.out : 105 follow ? new PrintStream(new SystemErrStream()) : System.err; 106 PropertiesUtil propsUtil = PropertiesUtil.getProperties(); 107 if (!propsUtil.getStringProperty("os.name").startsWith("Windows") || 108 propsUtil.getBooleanProperty("log4j.skipJansi")) { 109 return printStream; 110 } else { 111 try { 112 final ClassLoader loader = Loader.getClassLoader(); 113 // We type the parameter as a wildcard to avoid a hard reference to Jansi. 114 final Class<?> clazz = loader.loadClass("org.fusesource.jansi.WindowsAnsiOutputStream"); 115 final Constructor<?> constructor = clazz.getConstructor(OutputStream.class); 116 return (OutputStream) constructor.newInstance(printStream); 117 } catch (final ClassNotFoundException cnfe) { 118 LOGGER.debug("Jansi is not installed"); 119 } catch (final NoSuchMethodException nsme) { 120 LOGGER.warn("WindowsAnsiOutputStream is missing the proper constructor"); 121 } catch (final Exception ex) { 122 LOGGER.warn("Unable to instantiate WindowsAnsiOutputStream"); 123 } 124 return printStream; 125 } 126 } 127 128 /** 129 * An implementation of OutputStream that redirects to the 130 * current System.err. 131 * 132 */ 133 private static class SystemErrStream extends OutputStream { 134 public SystemErrStream() { 135 } 136 137 @Override 138 public void close() { 139 } 140 141 @Override 142 public void flush() { 143 System.err.flush(); 144 } 145 146 @Override 147 public void write(final byte[] b) throws IOException { 148 System.err.write(b); 149 } 150 151 @Override 152 public void write(final byte[] b, final int off, final int len) 153 throws IOException { 154 System.err.write(b, off, len); 155 } 156 157 @Override 158 public void write(final int b) { 159 System.err.write(b); 160 } 161 } 162 163 /** 164 * An implementation of OutputStream that redirects to the 165 * current System.out. 166 * 167 */ 168 private static class SystemOutStream extends OutputStream { 169 public SystemOutStream() { 170 } 171 172 @Override 173 public void close() { 174 } 175 176 @Override 177 public void flush() { 178 System.out.flush(); 179 } 180 181 @Override 182 public void write(final byte[] b) throws IOException { 183 System.out.write(b); 184 } 185 186 @Override 187 public void write(final byte[] b, final int off, final int len) 188 throws IOException { 189 System.out.write(b, off, len); 190 } 191 192 @Override 193 public void write(final int b) throws IOException { 194 System.out.write(b); 195 } 196 } 197 198 /** 199 * Data to pass to factory method. 200 */ 201 private static class FactoryData { 202 private final OutputStream os; 203 private final String type; 204 205 /** 206 * Constructor. 207 * @param os The OutputStream. 208 * @param type The name of the target. 209 */ 210 public FactoryData(final OutputStream os, final String type) { 211 this.os = os; 212 this.type = type; 213 } 214 } 215 216 /** 217 * Factory to create the Appender. 218 */ 219 private static class ConsoleManagerFactory implements ManagerFactory<OutputStreamManager, FactoryData> { 220 221 /** 222 * Create an OutputStreamManager. 223 * @param name The name of the entity to manage. 224 * @param data The data required to create the entity. 225 * @return The OutputStreamManager 226 */ 227 public OutputStreamManager createManager(final String name, final FactoryData data) { 228 return new OutputStreamManager(data.os, data.type); 229 } 230 } 231 232 }