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    }