custom console colorful logger
authorFrantišek Kučera <franta-hg@frantovo.cz>
Mon, 17 Nov 2014 18:12:27 +0100
changeset 84819598eb623
parent 7 8e9983260624
child 9 00d15f596bce
custom console colorful logger
java/copy-image-resizer/src/cz/frantovo/copyImageResizer/logging/ColorfulConsoleFormatter.java
java/copy-image-resizer/src/cz/frantovo/copyImageResizer/logging/ColorfulPrintWriter.java
java/copy-image-resizer/src/cz/frantovo/copyImageResizer/logging/ConsoleLoggerInitializer.java
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/java/copy-image-resizer/src/cz/frantovo/copyImageResizer/logging/ColorfulConsoleFormatter.java	Mon Nov 17 18:12:27 2014 +0100
     1.3 @@ -0,0 +1,100 @@
     1.4 +/**
     1.5 + * copy-image-resizer
     1.6 + * Copyright © 2013 František Kučera (frantovo.cz)
     1.7 + *
     1.8 + * This program is free software: you can redistribute it and/or modify
     1.9 + * it under the terms of the GNU General Public License as published by
    1.10 + * the Free Software Foundation, either version 3 of the License, or
    1.11 + * (at your option) any later version.
    1.12 + *
    1.13 + * This program is distributed in the hope that it will be useful,
    1.14 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    1.15 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    1.16 + * GNU General Public License for more details.
    1.17 + *
    1.18 + * You should have received a copy of the GNU General Public License
    1.19 + * along with this program. If not, see <http://www.gnu.org/licenses/>.
    1.20 + */
    1.21 +package cz.frantovo.copyImageResizer.logging;
    1.22 +
    1.23 +import cz.frantovo.copyImageResizer.logging.ColorfulPrintWriter.TerminalColor;
    1.24 +import cz.frantovo.copyImageResizer.logging.ColorfulPrintWriter.TerminalStyle;
    1.25 +import java.io.StringWriter;
    1.26 +import java.util.logging.Formatter;
    1.27 +import java.util.logging.Level;
    1.28 +import java.util.logging.LogRecord;
    1.29 +
    1.30 +/**
    1.31 + * For console/terminal log output. Log messages are printed in brief and colorful form.
    1.32 + *
    1.33 + * @author Ing. František Kučera (frantovo.cz)
    1.34 + */
    1.35 +public class ColorfulConsoleFormatter extends Formatter {
    1.36 +
    1.37 +	private boolean printStacktrace = false;
    1.38 +
    1.39 +	@Override
    1.40 +	public String format(LogRecord r) {
    1.41 +		StringWriter sw = new StringWriter();
    1.42 +		try (ColorfulPrintWriter out = new ColorfulPrintWriter(sw)) {
    1.43 +			printLevel(out, r.getLevel());
    1.44 +			printMessage(out, r);
    1.45 +			printThrowable(out, r);
    1.46 +			out.println();
    1.47 +		}
    1.48 +		return sw.toString();
    1.49 +	}
    1.50 +
    1.51 +	private void printLevel(ColorfulPrintWriter out, Level l) {
    1.52 +		TerminalColor color = TerminalColor.Magenta;
    1.53 +
    1.54 +		if (l == Level.SEVERE) {
    1.55 +			color = TerminalColor.Red;
    1.56 +		} else if (l == Level.WARNING) {
    1.57 +			color = TerminalColor.Yellow;
    1.58 +		}
    1.59 +
    1.60 +		out.print(color, rpad(l.getLocalizedName() + ": ", 10));
    1.61 +	}
    1.62 +
    1.63 +	private void printMessage(ColorfulPrintWriter out, LogRecord r) {
    1.64 +		out.print(formatMessage(r));
    1.65 +	}
    1.66 +
    1.67 +	private void printThrowable(ColorfulPrintWriter out, LogRecord r) {
    1.68 +		Throwable t = r.getThrown();
    1.69 +		if (t != null) {
    1.70 +			out.print(": ");
    1.71 +			out.print(TerminalColor.Red, t.getClass().getSimpleName());
    1.72 +			String message = t.getLocalizedMessage();
    1.73 +			if (message != null) {
    1.74 +				out.print(": ");
    1.75 +				out.print(message);
    1.76 +			}
    1.77 +			if (printStacktrace) {
    1.78 +				out.println();
    1.79 +				out.setForegroundColor(TerminalColor.Yellow);
    1.80 +				out.setStyle(TerminalStyle.Dim);
    1.81 +				t.printStackTrace(out);
    1.82 +				out.resetAll();
    1.83 +			}
    1.84 +		}
    1.85 +	}
    1.86 +
    1.87 +	public boolean isPrintStacktrace() {
    1.88 +		return printStacktrace;
    1.89 +	}
    1.90 +
    1.91 +	public void setPrintStacktrace(boolean printStacktrace) {
    1.92 +		this.printStacktrace = printStacktrace;
    1.93 +	}
    1.94 +
    1.95 +	private static String rpad(String s, int n) {
    1.96 +		if (n > 0) {
    1.97 +			return String.format("%1$-" + n + "s", s);
    1.98 +		} else {
    1.99 +			return s;
   1.100 +		}
   1.101 +	}
   1.102 +
   1.103 +}
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/java/copy-image-resizer/src/cz/frantovo/copyImageResizer/logging/ColorfulPrintWriter.java	Mon Nov 17 18:12:27 2014 +0100
     2.3 @@ -0,0 +1,358 @@
     2.4 +/**
     2.5 + * copy-image-resizer
     2.6 + * Copyright © 2013 František Kučera (frantovo.cz)
     2.7 + *
     2.8 + * This program is free software: you can redistribute it and/or modify
     2.9 + * it under the terms of the GNU General Public License as published by
    2.10 + * the Free Software Foundation, either version 3 of the License, or
    2.11 + * (at your option) any later version.
    2.12 + *
    2.13 + * This program is distributed in the hope that it will be useful,
    2.14 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    2.15 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    2.16 + * GNU General Public License for more details.
    2.17 + *
    2.18 + * You should have received a copy of the GNU General Public License
    2.19 + * along with this program. If not, see <http://www.gnu.org/licenses/>.
    2.20 + */
    2.21 +package cz.frantovo.copyImageResizer.logging;
    2.22 +
    2.23 +import java.io.File;
    2.24 +import java.io.FileNotFoundException;
    2.25 +import java.io.OutputStream;
    2.26 +import java.io.PrintWriter;
    2.27 +import java.io.UnsupportedEncodingException;
    2.28 +import java.io.Writer;
    2.29 +import java.util.EnumSet;
    2.30 +
    2.31 +/**
    2.32 + * PrintWriter with convenience methods for printing color and formatted text.
    2.33 + *
    2.34 + * Uses ANSI Escape Sequences.
    2.35 + * See: http://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x329.html
    2.36 + *
    2.37 + * @author Ing. František Kučera (frantovo.cz)
    2.38 + */
    2.39 +public class ColorfulPrintWriter extends PrintWriter {
    2.40 +
    2.41 +	public enum TerminalColor {
    2.42 +
    2.43 +		Black(30, 40),
    2.44 +		Red(31, 41),
    2.45 +		Green(32, 42),
    2.46 +		Yellow(33, 43),
    2.47 +		Blue(34, 44),
    2.48 +		Magenta(35, 45),
    2.49 +		Cyan(36, 46),
    2.50 +		White(37, 47);
    2.51 +		private final int foregroundCode;
    2.52 +		private final int backgroundCode;
    2.53 +
    2.54 +		private TerminalColor(int foregroundCode, int backgroundCode) {
    2.55 +			this.foregroundCode = foregroundCode;
    2.56 +			this.backgroundCode = backgroundCode;
    2.57 +		}
    2.58 +
    2.59 +		public int getForegroundCode() {
    2.60 +			return foregroundCode;
    2.61 +		}
    2.62 +
    2.63 +		public int getBackgroundCode() {
    2.64 +			return backgroundCode;
    2.65 +		}
    2.66 +	}
    2.67 +
    2.68 +	public enum TerminalStyle {
    2.69 +
    2.70 +		Reset(0),
    2.71 +		Bright(1),
    2.72 +		Dim(2),
    2.73 +		Underscore(4),
    2.74 +		Blink(5),
    2.75 +		Reverse(7),
    2.76 +		Hidden(8);
    2.77 +		private int code;
    2.78 +
    2.79 +		private TerminalStyle(int code) {
    2.80 +			this.code = code;
    2.81 +		}
    2.82 +
    2.83 +		public int getCode() {
    2.84 +			return code;
    2.85 +		}
    2.86 +	}
    2.87 +	private final boolean COLOR_ENABLED;
    2.88 +	private boolean colorful = true;
    2.89 +
    2.90 +	public void setStyle(TerminalStyle style) {
    2.91 +		setStyle(EnumSet.of(style));
    2.92 +	}
    2.93 +
    2.94 +	public void setStyle(EnumSet<TerminalStyle> styles) {
    2.95 +		printCodes(getStyleCodes(styles));
    2.96 +	}
    2.97 +
    2.98 +	private static int[] getStyleCodes(EnumSet<TerminalStyle> styles) {
    2.99 +		int[] array = new int[styles.size()];
   2.100 +		int i = 0;
   2.101 +		for (TerminalStyle s : styles) {
   2.102 +			array[i++] = s.getCode();
   2.103 +		}
   2.104 +		return array;
   2.105 +	}
   2.106 +
   2.107 +	/**
   2.108 +	 * Print (usually audible) bell code (\007, \a, ^G)
   2.109 +	 */
   2.110 +	public void bell() {
   2.111 +		print("\007");
   2.112 +	}
   2.113 +
   2.114 +	/**
   2.115 +	 * Eat the last character
   2.116 +	 */
   2.117 +	public void backspace() {
   2.118 +		print("\b");
   2.119 +	}
   2.120 +
   2.121 +	/**
   2.122 +	 * Eat n last characters
   2.123 +	 *
   2.124 +	 * @param count n
   2.125 +	 */
   2.126 +	public void backspace(int count) {
   2.127 +		for (int i = 0; i < count; i++) {
   2.128 +			backspace();
   2.129 +		}
   2.130 +	}
   2.131 +
   2.132 +	/**
   2.133 +	 * With 100 ms delay and all colors.
   2.134 +	 *
   2.135 +	 * @see #printRainbow(java.lang.String, int,
   2.136 +	 * info.globalcode.sql.dk.ColorfulPrintWriter.TerminalColor[])
   2.137 +	 */
   2.138 +	public void printRainbow(String string) {
   2.139 +		printRainbow(string, 100);
   2.140 +	}
   2.141 +
   2.142 +	/**
   2.143 +	 * With all colors.
   2.144 +	 *
   2.145 +	 * @see #printRainbow(java.lang.String, int,
   2.146 +	 * info.globalcode.sql.dk.ColorfulPrintWriter.TerminalColor[])
   2.147 +	 */
   2.148 +	public void printRainbow(String string, int delay) {
   2.149 +		printRainbow(string, delay, TerminalColor.values());
   2.150 +	}
   2.151 +
   2.152 +	/**
   2.153 +	 * Prints rainbow text – (re)writes same text subsequently in given colors and then in default
   2.154 +	 * color.
   2.155 +	 *
   2.156 +	 * @param string text to be printed, should not contain \n new line (then rainbow does not work
   2.157 +	 * – use println() after printRainbow() instead)
   2.158 +	 * @param delay delay between rewrites
   2.159 +	 * @param colors list of colors to be used
   2.160 +	 */
   2.161 +	public void printRainbow(String string, int delay, TerminalColor... colors) {
   2.162 +		for (TerminalColor c : colors) {
   2.163 +			print(c, string);
   2.164 +			try {
   2.165 +				Thread.sleep(delay);
   2.166 +			} catch (InterruptedException e) {
   2.167 +				// no time to sleep
   2.168 +				break;
   2.169 +			}
   2.170 +			backspace(string.length());
   2.171 +			flush();
   2.172 +		}
   2.173 +		print(string);
   2.174 +	}
   2.175 +
   2.176 +	public void setForegroundColor(TerminalColor color) {
   2.177 +		printCodes(color.getForegroundCode());
   2.178 +	}
   2.179 +
   2.180 +	public void setBackgroundColor(TerminalColor color) {
   2.181 +		printCodes(color.getBackgroundCode());
   2.182 +	}
   2.183 +
   2.184 +	public void print(TerminalColor foregroundColor, String string) {
   2.185 +		setForegroundColor(foregroundColor);
   2.186 +		print(string);
   2.187 +		resetAll();
   2.188 +	}
   2.189 +
   2.190 +	public void println(TerminalColor foregroundColor, String string) {
   2.191 +		print(foregroundColor, string);
   2.192 +		println();
   2.193 +	}
   2.194 +
   2.195 +	public void print(TerminalColor foregroundColor, TerminalColor backgroundColor, String string) {
   2.196 +		setForegroundColor(foregroundColor);
   2.197 +		setBackgroundColor(backgroundColor);
   2.198 +		print(string);
   2.199 +		resetAll();
   2.200 +	}
   2.201 +
   2.202 +	public void println(TerminalColor foregroundColor, TerminalColor backgroundColor, String string) {
   2.203 +		print(foregroundColor, backgroundColor, string);
   2.204 +		println();
   2.205 +	}
   2.206 +
   2.207 +	public void print(TerminalColor foregroundColor, TerminalColor backgroundColor, EnumSet<TerminalStyle> styles, String string) {
   2.208 +		setForegroundColor(foregroundColor);
   2.209 +		setBackgroundColor(backgroundColor);
   2.210 +		setStyle(styles);
   2.211 +		print(string);
   2.212 +		resetAll();
   2.213 +	}
   2.214 +
   2.215 +	public void println(TerminalColor foregroundColor, TerminalColor backgroundColor, EnumSet<TerminalStyle> styles, String string) {
   2.216 +		print(foregroundColor, backgroundColor, styles, string);
   2.217 +		println();
   2.218 +	}
   2.219 +
   2.220 +	public void print(TerminalColor foregroundColor, TerminalColor backgroundColor, TerminalStyle style, String string) {
   2.221 +		print(foregroundColor, backgroundColor, EnumSet.of(style), string);
   2.222 +	}
   2.223 +
   2.224 +	public void println(TerminalColor foregroundColor, TerminalColor backgroundColor, TerminalStyle style, String string) {
   2.225 +		print(foregroundColor, backgroundColor, style, string);
   2.226 +		println();
   2.227 +	}
   2.228 +
   2.229 +	public void print(TerminalColor foregroundColor, EnumSet<TerminalStyle> styles, String string) {
   2.230 +		setForegroundColor(foregroundColor);
   2.231 +		setStyle(styles);
   2.232 +		print(string);
   2.233 +		resetAll();
   2.234 +	}
   2.235 +
   2.236 +	public void println(TerminalColor foregroundColor, EnumSet<TerminalStyle> styles, String string) {
   2.237 +		print(foregroundColor, styles, string);
   2.238 +		println();
   2.239 +	}
   2.240 +
   2.241 +	public void print(TerminalColor foregroundColor, TerminalStyle style, String string) {
   2.242 +		print(foregroundColor, EnumSet.of(style), string);
   2.243 +	}
   2.244 +
   2.245 +	public void println(TerminalColor foregroundColor, TerminalStyle style, String string) {
   2.246 +		print(foregroundColor, style, string);
   2.247 +		println();
   2.248 +	}
   2.249 +
   2.250 +	public void print(EnumSet<TerminalStyle> styles, String string) {
   2.251 +		setStyle(styles);
   2.252 +		print(string);
   2.253 +		resetAll();
   2.254 +	}
   2.255 +
   2.256 +	public void println(EnumSet<TerminalStyle> styles, String string) {
   2.257 +		print(styles, string);
   2.258 +		println();
   2.259 +	}
   2.260 +
   2.261 +	public void print(TerminalStyle style, String string) {
   2.262 +		print(EnumSet.of(style), string);
   2.263 +	}
   2.264 +
   2.265 +	public void println(TerminalStyle style, String string) {
   2.266 +		print(style, string);
   2.267 +		println();
   2.268 +	}
   2.269 +
   2.270 +	public void resetAll() {
   2.271 +		printCodes(TerminalStyle.Reset.code);
   2.272 +	}
   2.273 +
   2.274 +	private void printCodes(int... codes) {
   2.275 +		if (COLOR_ENABLED && colorful) {
   2.276 +			print("\033[");
   2.277 +			for (int i = 0; i < codes.length; i++) {
   2.278 +				print(codes[i]);
   2.279 +				if (i < codes.length - 1 && codes.length > 1) {
   2.280 +					print(";");
   2.281 +				}
   2.282 +			}
   2.283 +			print("m");
   2.284 +		}
   2.285 +	}
   2.286 +
   2.287 +	/**
   2.288 +	 * Colors can be switched on/off during usage of this writer.
   2.289 +	 *
   2.290 +	 * @return whether colors are currently turned on
   2.291 +	 * @see #isColorEnabled()
   2.292 +	 */
   2.293 +	public boolean isColorful() {
   2.294 +		return colorful;
   2.295 +	}
   2.296 +
   2.297 +	/**
   2.298 +	 * Collors might be definitively disabled in constructor. If not, they can be turned on/off
   2.299 +	 * during usage of this writer by {@linkplain #setColorful(boolean)}
   2.300 +	 *
   2.301 +	 * @return whether colors are allowed for this instance of this class
   2.302 +	 * @see #isColorful()
   2.303 +	 */
   2.304 +	public boolean isColorEnabled() {
   2.305 +		return COLOR_ENABLED;
   2.306 +	}
   2.307 +
   2.308 +	/**
   2.309 +	 * @see #isColorful()
   2.310 +	 * @see #isColorEnabled()
   2.311 +	 */
   2.312 +	public void setColorful(boolean colorful) {
   2.313 +		this.colorful = colorful;
   2.314 +	}
   2.315 +
   2.316 +	public ColorfulPrintWriter(File file) throws FileNotFoundException {
   2.317 +		super(file);
   2.318 +		COLOR_ENABLED = true;
   2.319 +	}
   2.320 +
   2.321 +	public ColorfulPrintWriter(OutputStream out) {
   2.322 +		super(out);
   2.323 +		COLOR_ENABLED = true;
   2.324 +	}
   2.325 +
   2.326 +	public ColorfulPrintWriter(String fileName) throws FileNotFoundException {
   2.327 +		super(fileName);
   2.328 +		COLOR_ENABLED = true;
   2.329 +	}
   2.330 +
   2.331 +	public ColorfulPrintWriter(Writer out) {
   2.332 +		super(out);
   2.333 +		COLOR_ENABLED = true;
   2.334 +	}
   2.335 +
   2.336 +	public ColorfulPrintWriter(File file, String csn) throws FileNotFoundException, UnsupportedEncodingException {
   2.337 +		super(file, csn);
   2.338 +		COLOR_ENABLED = true;
   2.339 +	}
   2.340 +
   2.341 +	/**
   2.342 +	 * @param colorEnabled colors might be definitively disabled by this option – this might be more
   2.343 +	 * optimalizable than dynamic turning off colors by {@linkplain #setColorful(boolean)} which is
   2.344 +	 * not definitive (colors can be turned on during live of this instance). This might be useful
   2.345 +	 * if you need an instance of this class but don't need colors at all.
   2.346 +	 */
   2.347 +	public ColorfulPrintWriter(OutputStream out, boolean autoFlush, boolean colorEnabled) {
   2.348 +		super(out, autoFlush);
   2.349 +		COLOR_ENABLED = colorEnabled;
   2.350 +	}
   2.351 +
   2.352 +	public ColorfulPrintWriter(String fileName, String csn) throws FileNotFoundException, UnsupportedEncodingException {
   2.353 +		super(fileName, csn);
   2.354 +		COLOR_ENABLED = true;
   2.355 +	}
   2.356 +
   2.357 +	public ColorfulPrintWriter(Writer out, boolean autoFlush) {
   2.358 +		super(out, autoFlush);
   2.359 +		COLOR_ENABLED = true;
   2.360 +	}
   2.361 +}
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/java/copy-image-resizer/src/cz/frantovo/copyImageResizer/logging/ConsoleLoggerInitializer.java	Mon Nov 17 18:12:27 2014 +0100
     3.3 @@ -0,0 +1,78 @@
     3.4 +/**
     3.5 + * copy-image-resizer
     3.6 + * Copyright © 2013 František Kučera (frantovo.cz)
     3.7 + *
     3.8 + * This program is free software: you can redistribute it and/or modify
     3.9 + * it under the terms of the GNU General Public License as published by
    3.10 + * the Free Software Foundation, either version 3 of the License, or
    3.11 + * (at your option) any later version.
    3.12 + *
    3.13 + * This program is distributed in the hope that it will be useful,
    3.14 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    3.15 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    3.16 + * GNU General Public License for more details.
    3.17 + *
    3.18 + * You should have received a copy of the GNU General Public License
    3.19 + * along with this program. If not, see <http://www.gnu.org/licenses/>.
    3.20 + */
    3.21 +package cz.frantovo.copyImageResizer.logging;
    3.22 +
    3.23 +import cz.frantovo.copyImageResizer.Constants;
    3.24 +import java.util.logging.ConsoleHandler;
    3.25 +import java.util.logging.Handler;
    3.26 +import java.util.logging.Level;
    3.27 +import java.util.logging.Logger;
    3.28 +
    3.29 +/**
    3.30 + * Configures logging subsystem.
    3.31 + * Usage: java -Djava.util.logging.config.class=info.globalcode.sql.dk.logging.ConsoleLoggerInitializer …
    3.32 + *
    3.33 + * @author Ing. František Kučera (frantovo.cz)
    3.34 + */
    3.35 +public class ConsoleLoggerInitializer {
    3.36 +
    3.37 +	private static final Logger log = Logger.getLogger(ConsoleLoggerInitializer.class.getName());
    3.38 +	public static final String LEVEL_PROPERTY = ConsoleLoggerInitializer.class.getName() + ".level";
    3.39 +	private static final Level DEFAULT_LEVEL = Level.INFO;
    3.40 +
    3.41 +	public ConsoleLoggerInitializer() {
    3.42 +		Logger logger = Logger.getLogger(Constants.JAVA_PACKAGE);
    3.43 +		ConsoleHandler handler = new ConsoleHandler();
    3.44 +		ColorfulConsoleFormatter formatter = new ColorfulConsoleFormatter();
    3.45 +
    3.46 +		logger.addHandler(handler);
    3.47 +		handler.setFormatter(formatter);
    3.48 +
    3.49 +		setLevel(logger, handler, formatter);
    3.50 +
    3.51 +
    3.52 +		/**
    3.53 +		 * TODO: optional FileHandler – detailed logs in file in ~/sql-dk/log/…
    3.54 +		 */
    3.55 +	}
    3.56 +
    3.57 +	private void setLevel(Logger logger, Handler handler, ColorfulConsoleFormatter formatter) {
    3.58 +		boolean levelParseError = false;
    3.59 +		Level level;
    3.60 +		String cliLevel = System.getProperty(LEVEL_PROPERTY);
    3.61 +		if (cliLevel == null) {
    3.62 +			level = DEFAULT_LEVEL;
    3.63 +		} else {
    3.64 +			try {
    3.65 +				level = Level.parse(cliLevel);
    3.66 +			} catch (IllegalArgumentException e) {
    3.67 +				level = DEFAULT_LEVEL;
    3.68 +				levelParseError = true;
    3.69 +			}
    3.70 +		}
    3.71 +
    3.72 +		handler.setLevel(level);
    3.73 +		logger.setLevel(level);
    3.74 +
    3.75 +		if (levelParseError) {
    3.76 +			log.log(Level.WARNING, "Invalid logging level „{0}“ specified in „{1}“ → using default level „{2}“", new Object[]{cliLevel, LEVEL_PROPERTY, DEFAULT_LEVEL});
    3.77 +		}
    3.78 +
    3.79 +		formatter.setPrintStacktrace(level.intValue() < Level.INFO.intValue());
    3.80 +	}
    3.81 +}