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  import org.apache.logging.log4j.util.PropertiesUtil;
28  
29  import java.io.IOException;
30  import java.io.OutputStream;
31  import java.io.PrintStream;
32  import java.lang.reflect.Constructor;
33  
34  /**
35   * ConsoleAppender appends log events to <code>System.out</code> or
36   * <code>System.err</code> using a layout specified by the user. The
37   * default target is <code>System.out</code>.
38   * @doubt accessing System.out or .err as a byte stream instead of a writer
39   *    bypasses the JVM's knowledge of the proper encoding. (RG) Encoding
40   * is handled within the Layout. Typically, a Layout will generate a String
41   * and then call getBytes which may use a configured encoding or the system
42   * default. OTOH, a Writer cannot print byte streams.
43   */
44  @Plugin(name = "Console", type = "Core", elementType = "appender", printObject = true)
45  public final class ConsoleAppender extends AbstractOutputStreamAppender {
46  
47      private static ConsoleManagerFactory factory = new ConsoleManagerFactory();
48  
49      /**
50       * Enumeration of console destinations.
51       */
52      public enum Target {
53          /** Standard output. */
54          SYSTEM_OUT,
55          /** Standard error output. */
56          SYSTEM_ERR
57      }
58  
59      private ConsoleAppender(final String name, final Layout layout, final Filter filter,
60                              final OutputStreamManager manager,
61                              final boolean handleExceptions) {
62          super(name, layout, filter, handleExceptions, true, manager);
63      }
64  
65      /**
66       * Create a Console Appender.
67       * @param layout The layout to use (required).
68       * @param filter The Filter or null.
69       * @param t The target ("SYSTEM_OUT" or "SYSTEM_ERR"). The default is "SYSTEM_OUT".
70       * @param follow If true will follow changes to the underlying output stream.
71       * @param name The name of the Appender (required).
72       * @param suppress "true" if exceptions should be hidden from the application, "false" otherwise.
73       * The default is "true".
74       * @return The ConsoleAppender.
75       */
76      @PluginFactory
77      public static ConsoleAppender createAppender(@PluginElement("layout") Layout layout,
78                                                   @PluginElement("filters") final Filter filter,
79                                                   @PluginAttr("target") final String t,
80                                                   @PluginAttr("name") final String name,
81                                                   @PluginAttr("follow") final String follow,
82                                                   @PluginAttr("suppressExceptions") final String suppress) {
83          if (name == null) {
84              LOGGER.error("No name provided for ConsoleAppender");
85              return null;
86          }
87          if (layout == null) {
88              layout = PatternLayout.createLayout(null, null, null, null);
89          }
90          final boolean isFollow = follow == null ? false : Boolean.valueOf(follow);
91          final boolean handleExceptions = suppress == null ? true : Boolean.valueOf(suppress);
92          final Target target = t == null ? Target.SYSTEM_OUT : Target.valueOf(t);
93          return new ConsoleAppender(name, layout, filter, getManager(isFollow, target), handleExceptions);
94      }
95  
96      private static OutputStreamManager getManager(final boolean follow, final Target target) {
97          final String type = target.name();
98          final OutputStream os = getOutputStream(follow, target);
99          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 }