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 wrapSystemOut_METHOD_NAME = "wrapSystemOut"; 053 private final static String wrapSystemErr_METHOD_NAME = "wrapSystemErr"; 054 private final static Class<?>[] ARGUMENT_TYPES = { PrintStream.class }; 055 056 private final static String CONSOLE_APPENDER_WARNING_URL = CoreConstants.CODES_URL+"#slowConsole"; 057 058 /** 059 * Sets the value of the <b>Target</b> option. Recognized values are 060 * "System.out" and "System.err". Any other value will be ignored. 061 */ 062 public void setTarget(String value) { 063 ConsoleTarget t = ConsoleTarget.findByName(value.trim()); 064 if (t == null) { 065 targetWarn(value); 066 } else { 067 target = t; 068 } 069 } 070 071 /** 072 * Returns the current value of the <b>target</b> property. The default value of 073 * the option is "System.out". 074 * <p> 075 * See also {@link #setTarget}. 076 */ 077 public String getTarget() { 078 return target.getName(); 079 } 080 081 private void targetWarn(String val) { 082 Status status = new WarnStatus("[" + val + "] should be one of " + Arrays.toString(ConsoleTarget.values()), 083 this); 084 status.add(new WarnStatus("Using previously set target, System.out by default.", this)); 085 addStatus(status); 086 } 087 088 @Override 089 public void start() { 090 addInfo("BEWARE: Writing to the console can be very slow. Avoid logging to the "); 091 addInfo("console in production environments, especially in high volume systems."); 092 addInfo("See also "+CONSOLE_APPENDER_WARNING_URL); 093 OutputStream targetStream = target.getStream(); 094 // enable jansi only if withJansi set to true 095 if (withJansi) { 096 targetStream = wrapWithJansi(targetStream); 097 } 098 setOutputStream(targetStream); 099 super.start(); 100 } 101 102 private OutputStream wrapWithJansi(OutputStream targetStream) { 103 try { 104 addInfo("Enabling JANSI AnsiPrintStream for the console."); 105 ClassLoader classLoader = Loader.getClassLoaderOfObject(context); 106 Class<?> classObj = classLoader.loadClass(AnsiConsole_CLASS_NAME); 107 108 // check for JAnsi 2 109 String methodNameJansi2 = target == ConsoleTarget.SystemOut ? JANSI2_OUT_METHOD_NAME 110 : JANSI2_ERR_METHOD_NAME; 111 final Optional<Method> optOutMethod = Arrays.stream(classObj.getMethods()) 112 .filter(m -> m.getName().equals(methodNameJansi2)) 113 .filter(m -> m.getParameters().length == 0) 114 .filter(m -> Modifier.isStatic(m.getModifiers())) 115 .filter(m -> PrintStream.class.isAssignableFrom(m.getReturnType())) 116 .findAny(); 117 if (optOutMethod.isPresent()) { 118 final Method outMethod = optOutMethod.orElseThrow(() -> new NoSuchElementException("No value present")); 119 return (PrintStream) outMethod.invoke(null); 120 } 121 122 // JAnsi 1 123 String methodName = target == ConsoleTarget.SystemOut ? wrapSystemOut_METHOD_NAME 124 : wrapSystemErr_METHOD_NAME; 125 Method method = classObj.getMethod(methodName, ARGUMENT_TYPES); 126 return (OutputStream) method.invoke(null, new PrintStream(targetStream)); 127 } catch (Exception e) { 128 addWarn("Failed to create AnsiPrintStream. Falling back on the default stream.", e); 129 } 130 return targetStream; 131 } 132 133 /** 134 * @return whether to use JANSI or not. 135 */ 136 public boolean isWithJansi() { 137 return withJansi; 138 } 139 140 /** 141 * If true, this appender will output to a stream provided by the JANSI library. 142 * 143 * @param withJansi whether to use JANSI or not. 144 * @since 1.0.5 145 */ 146 public void setWithJansi(boolean withJansi) { 147 this.withJansi = withJansi; 148 } 149 150}