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 028 import java.io.OutputStream; 029 import java.io.PrintStream; 030 import java.lang.reflect.Constructor; 031 032 /** 033 * ConsoleAppender appends log events to <code>System.out</code> or 034 * <code>System.err</code> using a layout specified by the user. The 035 * default target is <code>System.out</code>. 036 * @doubt accessing System.out or .err as a byte stream instead of a writer 037 * bypasses the JVM's knowledge of the proper encoding. (RG) Encoding 038 * is handled within the Layout. Typically, a Layout will generate a String 039 * and then call getBytes which may use a configured encoding or the system 040 * default. OTOH, a Writer cannot print byte streams. 041 */ 042 @Plugin(name = "Console", type = "Core", elementType = "appender", printObject = true) 043 public final class ConsoleAppender extends AbstractOutputStreamAppender { 044 045 private static ConsoleManagerFactory factory = new ConsoleManagerFactory(); 046 047 /** 048 * Enumeration of console destinations. 049 */ 050 public enum Target { 051 /** Standard output. */ 052 SYSTEM_OUT, 053 /** Standard error output. */ 054 SYSTEM_ERR 055 } 056 057 private ConsoleAppender(String name, Layout layout, Filter filter, OutputStreamManager manager, 058 boolean handleExceptions) { 059 super(name, layout, filter, handleExceptions, true, manager); 060 } 061 062 /** 063 * Create a Console Appender. 064 * @param layout The layout to use (required). 065 * @param filter The Filter or null. 066 * @param t The target ("SYSTEM_OUT" or "SYSTEM_ERR"). The default is "SYSTEM_OUT". 067 * @param name The name of the Appender (required). 068 * @param suppress "true" if exceptions should be hidden from the application, "false" otherwise. 069 * The default is "true". 070 * @return The ConsoleAppender. 071 */ 072 @PluginFactory 073 public static ConsoleAppender createAppender(@PluginElement("layout") Layout layout, 074 @PluginElement("filters") Filter filter, 075 @PluginAttr("target") String t, 076 @PluginAttr("name") String name, 077 @PluginAttr("suppressExceptions") String suppress) { 078 if (name == null) { 079 LOGGER.error("No name provided for ConsoleAppender"); 080 return null; 081 } 082 if (layout == null) { 083 layout = PatternLayout.createLayout(null, null, null, null); 084 } 085 boolean handleExceptions = suppress == null ? true : Boolean.valueOf(suppress); 086 Target target = t == null ? Target.SYSTEM_OUT : Target.valueOf(t); 087 return new ConsoleAppender(name, layout, filter, getManager(target), handleExceptions); 088 } 089 090 private static OutputStreamManager getManager(Target target) { 091 String type = target.name(); 092 OutputStream os = getOutputStream(target); 093 return OutputStreamManager.getManager(target.name(), new FactoryData(os, type), factory); 094 } 095 096 private static OutputStream getOutputStream(Target target) { 097 final PrintStream printStream = target == Target.SYSTEM_OUT ? System.out : System.err; 098 if (!System.getProperty("os.name").startsWith("Windows")) { 099 return printStream; 100 } else { 101 try { 102 ClassLoader loader = Loader.getClassLoader(); 103 // We type the parameter as a wildcard to avoid a hard reference to Jansi. 104 Class<?> clazz = loader.loadClass("org.fusesource.jansi.WindowsAnsiOutputStream"); 105 Constructor<?> constructor = clazz.getConstructor(OutputStream.class); 106 return (OutputStream) constructor.newInstance(printStream); 107 } catch (ClassNotFoundException cnfe) { 108 LOGGER.debug("Jansi is not installed"); 109 } catch (NoSuchMethodException nsme) { 110 LOGGER.warn("WindowsAnsiOutputStream is missing the proper constructor"); 111 } catch (Exception ex) { 112 LOGGER.warn("Unable to instantiate WindowsAnsiOutputStream"); 113 } 114 return printStream; 115 } 116 } 117 118 /** 119 * Data to pass to factory method. 120 */ 121 private static class FactoryData { 122 private OutputStream os; 123 private String type; 124 125 /** 126 * Constructor. 127 * @param os The OutputStream. 128 * @param type The name of the target. 129 */ 130 public FactoryData(OutputStream os, String type) { 131 this.os = os; 132 this.type = type; 133 } 134 } 135 136 /** 137 * Factory to create the Appender. 138 */ 139 private static class ConsoleManagerFactory implements ManagerFactory<OutputStreamManager, FactoryData> { 140 141 /** 142 * Create an OutputStreamManager. 143 * @param name The name of the entity to manage. 144 * @param data The data required to create the entity. 145 * @return The OutputStreamManager 146 */ 147 public OutputStreamManager createManager(String name, FactoryData data) { 148 return new OutputStreamManager(data.os, data.type); 149 } 150 } 151 152 }