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