001/** 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved. 004 * 005 * This program and the accompanying materials are dual-licensed under 006 * either the terms of the Eclipse Public License v1.0 as published by 007 * the Eclipse Foundation 008 * 009 * or (per the licensee's choosing) 010 * 011 * under the terms of the GNU Lesser General Public License version 2.1 012 * as published by the Free Software Foundation. 013 */ 014package ch.qos.logback.core; 015 016import java.io.OutputStream; 017import java.io.PrintStream; 018import java.lang.reflect.Method; 019import java.lang.reflect.Modifier; 020import java.util.Arrays; 021import java.util.NoSuchElementException; 022import java.util.Optional; 023 024import ch.qos.logback.core.joran.spi.ConsoleTarget; 025import ch.qos.logback.core.status.Status; 026import ch.qos.logback.core.status.WarnStatus; 027import ch.qos.logback.core.util.Loader; 028 029/** 030 * ConsoleAppender appends log events to <code>System.out</code> or 031 * <code>System.err</code> using a layout specified by the user. The default 032 * target is <code>System.out</code>. 033 * <p> 034 * 035 * </p> 036 * For more information about this appender, please refer to the online manual 037 * at http://logback.qos.ch/manual/appenders.html#ConsoleAppender 038 * 039 * @author Ceki Gülcü 040 * @author Tom SH Liu 041 * @author Ruediger Dohna 042 */ 043 044public class ConsoleAppender<E> extends OutputStreamAppender<E> { 045 046 protected ConsoleTarget target = ConsoleTarget.SystemOut; 047 protected boolean withJansi = false; 048 049 private final static String AnsiConsole_CLASS_NAME = "org.fusesource.jansi.AnsiConsole"; 050 private final static String JANSI2_OUT_METHOD_NAME = "out"; 051 private final static String JANSI2_ERR_METHOD_NAME = "err"; 052 private final static String WRAP_SYSTEM_OUT_METHOD_NAME = "wrapSystemOut"; 053 private final static String WRAP_SYSTEM_ERR_METHOD_NAME = "wrapSystemErr"; 054 private final static String SYSTEM_INSTALL_METHOD_NAME = "systemInstall"; 055 private final static Class<?>[] ARGUMENT_TYPES = { PrintStream.class }; 056 057 private final static String CONSOLE_APPENDER_WARNING_URL = CoreConstants.CODES_URL+"#slowConsole"; 058 059 /** 060 * Sets the value of the <b>Target</b> option. Recognized values are 061 * "System.out" and "System.err". Any other value will be ignored. 062 */ 063 public void setTarget(String value) { 064 ConsoleTarget t = ConsoleTarget.findByName(value.trim()); 065 if (t == null) { 066 targetWarn(value); 067 } else { 068 target = t; 069 } 070 } 071 072 /** 073 * Returns the current value of the <b>target</b> property. The default value of 074 * the option is "System.out". 075 * <p> 076 * See also {@link #setTarget}. 077 */ 078 public String getTarget() { 079 return target.getName(); 080 } 081 082 private void targetWarn(String val) { 083 Status status = new WarnStatus("[" + val + "] should be one of " + Arrays.toString(ConsoleTarget.values()), 084 this); 085 status.add(new WarnStatus("Using previously set target, System.out by default.", this)); 086 addStatus(status); 087 } 088 089 @Override 090 public void start() { 091 addInfo("NOTE: Writing to the console can be slow. Try to avoid logging to the "); 092 addInfo("console in production environments, especially in high volume systems."); 093 addInfo("See also "+CONSOLE_APPENDER_WARNING_URL); 094 OutputStream targetStream = target.getStream(); 095 // enable jansi only if withJansi set to true 096 if (withJansi) { 097 targetStream = wrapWithJansi(targetStream); 098 } 099 setOutputStream(targetStream); 100 super.start(); 101 } 102 103 private OutputStream wrapWithJansi(OutputStream targetStream) { 104 try { 105 addInfo("Enabling JANSI AnsiPrintStream for the console."); 106 ClassLoader classLoader = Loader.getClassLoaderOfObject(context); 107 Class<?> classObj = classLoader.loadClass(AnsiConsole_CLASS_NAME); 108 109 Method systemInstallMethod = classObj.getMethod(SYSTEM_INSTALL_METHOD_NAME); 110 if(systemInstallMethod != null) { 111 systemInstallMethod.invoke(null); 112 } 113 114// final Optional<Method> optSystemInstallMethod = Arrays.stream(classObj.getMethods()) 115// .filter(m -> m.getName().equals(SYSTEM_INSTALL_METHOD_NAME)) 116// .filter(m -> m.getParameters().length == 0) 117// .filter(m -> Modifier.isStatic(m.getModifiers())) 118// .findAny(); 119// 120// if (optSystemInstallMethod.isPresent()) { 121// final Method systemInstallMethod = optSystemInstallMethod.orElseThrow(() -> new NoSuchElementException("No systemInstall method present")); 122// systemInstallMethod.invoke(null); 123// } 124 125 // check for JAnsi 2 126 String methodNameJansi2 = target == ConsoleTarget.SystemOut ? JANSI2_OUT_METHOD_NAME 127 : JANSI2_ERR_METHOD_NAME; 128 final Optional<Method> optOutMethod = Arrays.stream(classObj.getMethods()) 129 .filter(m -> m.getName().equals(methodNameJansi2)) 130 .filter(m -> m.getParameters().length == 0) 131 .filter(m -> Modifier.isStatic(m.getModifiers())) 132 .filter(m -> PrintStream.class.isAssignableFrom(m.getReturnType())) 133 .findAny(); 134 if (optOutMethod.isPresent()) { 135 final Method outMethod = optOutMethod.orElseThrow(() -> new NoSuchElementException("No out/err method present")); 136 return (PrintStream) outMethod.invoke(null); 137 } 138 139 // JAnsi 1 140 String methodName = target == ConsoleTarget.SystemOut ? WRAP_SYSTEM_OUT_METHOD_NAME 141 : WRAP_SYSTEM_ERR_METHOD_NAME; 142 Method method = classObj.getMethod(methodName, ARGUMENT_TYPES); 143 return (OutputStream) method.invoke(null, new PrintStream(targetStream)); 144 } catch (Exception e) { 145 addWarn("Failed to create AnsiPrintStream. Falling back on the default stream.", e); 146 } 147 return targetStream; 148 } 149 150 /** 151 * @return whether to use JANSI or not. 152 */ 153 public boolean isWithJansi() { 154 return withJansi; 155 } 156 157 /** 158 * If true, this appender will output to a stream provided by the JANSI library. 159 * 160 * @param withJansi whether to use JANSI or not. 161 * @since 1.0.5 162 */ 163 public void setWithJansi(boolean withJansi) { 164 this.withJansi = withJansi; 165 } 166 167}