View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core.appender;
18  
19  import org.apache.logging.log4j.core.Filter;
20  import org.apache.logging.log4j.core.Layout;
21  import org.apache.logging.log4j.core.config.plugins.Plugin;
22  import org.apache.logging.log4j.core.config.plugins.PluginAttr;
23  import org.apache.logging.log4j.core.config.plugins.PluginElement;
24  import org.apache.logging.log4j.core.config.plugins.PluginFactory;
25  import org.apache.logging.log4j.core.helpers.Loader;
26  import org.apache.logging.log4j.core.layout.PatternLayout;
27  
28  import java.io.OutputStream;
29  import java.io.PrintStream;
30  import java.lang.reflect.Constructor;
31  
32  /**
33   * ConsoleAppender appends log events to <code>System.out</code> or
34   * <code>System.err</code> using a layout specified by the user. The
35   * default target is <code>System.out</code>.
36   * @doubt accessing System.out or .err as a byte stream instead of a writer
37   *    bypasses the JVM's knowledge of the proper encoding. (RG) Encoding
38   * is handled within the Layout. Typically, a Layout will generate a String
39   * and then call getBytes which may use a configured encoding or the system
40   * default. OTOH, a Writer cannot print byte streams.
41   */
42  @Plugin(name = "Console", type = "Core", elementType = "appender", printObject = true)
43  public final class ConsoleAppender extends AbstractOutputStreamAppender {
44  
45      private static ConsoleManagerFactory factory = new ConsoleManagerFactory();
46  
47      /**
48       * Enumeration of console destinations.
49       */
50      public enum Target {
51          /** Standard output. */
52          SYSTEM_OUT,
53          /** Standard error output. */
54          SYSTEM_ERR
55      }
56  
57      private ConsoleAppender(String name, Layout layout, Filter filter, OutputStreamManager manager,
58                             boolean handleExceptions) {
59          super(name, layout, filter, handleExceptions, true, manager);
60      }
61  
62      /**
63       * Create a Console Appender.
64       * @param layout The layout to use (required).
65       * @param filter The Filter or null.
66       * @param t The target ("SYSTEM_OUT" or "SYSTEM_ERR"). The default is "SYSTEM_OUT".
67       * @param name The name of the Appender (required).
68       * @param suppress "true" if exceptions should be hidden from the application, "false" otherwise.
69       * The default is "true".
70       * @return The ConsoleAppender.
71       */
72      @PluginFactory
73      public static ConsoleAppender createAppender(@PluginElement("layout") Layout layout,
74                                                   @PluginElement("filters") Filter filter,
75                                                   @PluginAttr("target") String t,
76                                                   @PluginAttr("name") String name,
77                                                   @PluginAttr("suppressExceptions") String suppress) {
78          if (name == null) {
79              LOGGER.error("No name provided for ConsoleAppender");
80              return null;
81          }
82          if (layout == null) {
83              layout = PatternLayout.createLayout(null, null, null, null);
84          }
85          boolean handleExceptions = suppress == null ? true : Boolean.valueOf(suppress);
86          Target target = t == null ? Target.SYSTEM_OUT : Target.valueOf(t);
87          return new ConsoleAppender(name, layout, filter, getManager(target), handleExceptions);
88      }
89  
90      private static OutputStreamManager getManager(Target target) {
91          String type = target.name();
92          OutputStream os = getOutputStream(target);
93          return OutputStreamManager.getManager(target.name(), new FactoryData(os, type), factory);
94      }
95  
96      private static OutputStream getOutputStream(Target target) {
97          final PrintStream printStream = target == Target.SYSTEM_OUT ? System.out : System.err;
98          if (!System.getProperty("os.name").startsWith("Windows")) {
99              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 }