All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.refcodes.logger.alt.console.impls.ConsoleLoggerImpl Maven / Gradle / Ivy

There is a newer version: 1.0.5
Show newest version
// /////////////////////////////////////////////////////////////////////////////
// REFCODES.ORG
// =============================================================================
// This code is copyright (c) by Siegfried Steiner, Munich, Germany and licensed
// under the following (see "http://en.wikipedia.org/wiki/Multi-licensing")
// licenses:
// =============================================================================
// GNU General Public License, v3.0 ("http://www.gnu.org/licenses/gpl-3.0.html")
// together with the GPL linking exception applied; as being applied by the GNU
// Classpath ("http://www.gnu.org/software/classpath/license.html")
// =============================================================================
// Apache License, v2.0 ("http://www.apache.org/licenses/LICENSE-2.0")
// =============================================================================
// Please contact the copyright holding author(s) of the software artifacts in
// question for licensing issues not being covered by the above listed licenses,
// also regarding commercial licensing models or regarding the compatibility
// with other open source licenses.
// /////////////////////////////////////////////////////////////////////////////

package org.refcodes.logger.alt.console.impls;

import static org.fusesource.jansi.Ansi.*;

import java.io.PrintStream;

import org.fusesource.jansi.Ansi.Color;
import org.fusesource.jansi.AnsiConsole;
import org.refcodes.component.Destroyable;
import org.refcodes.data.RuntimeConsts;
import org.refcodes.logger.IllegalRecordRuntimeException;
import org.refcodes.logger.LogPriority;
import org.refcodes.logger.Logger;
import org.refcodes.logger.LoggerField;
import org.refcodes.logger.RuntimeLogger;
import org.refcodes.logger.UnexpectedLogRuntimeException;
import org.refcodes.logger.alt.console.ConsoleLogger;
import org.refcodes.logger.impls.RuntimeLoggerHeaderImpl;
import org.refcodes.runtime.CommandLineInterpreter;
import org.refcodes.runtime.OperatingSystem;
import org.refcodes.runtime.SystemUtility;
import org.refcodes.tabular.ColumnMismatchException;
import org.refcodes.tabular.Header;
import org.refcodes.tabular.HeaderMismatchException;
import org.refcodes.tabular.PrintStackTrace;
import org.refcodes.tabular.Record;
import org.refcodes.tabular.Row;
import org.refcodes.textual.SplitTextMode;
import org.refcodes.textual.MoreTextMode;
import org.refcodes.textual.TableBuilder;
import org.refcodes.textual.TableStyle;
import org.refcodes.textual.HorizAlignTextMode;
import org.refcodes.textual.ColumnWidthMetrics;
import org.refcodes.textual.ColumnWidthType;
import org.refcodes.textual.impls.TableBuilderImpl;
import org.refcodes.textual.impls.HorizAlignTextBuilderImpl;
import org.refcodes.textual.impls.ColumnWidthMetricsImpl;

/**
 * The {@link ConsoleLoggerImpl} implements the {@link Logger} interface for
 * providing logging functionality for extended pimped console output (via
 * {@link System#out} and {@link System#err} to be used by {@link RuntimeLogger}
 * implementations.
 * 

* ATTENTION: As the table layout for the generated log lines makes only sense * when all {@link ConsoleLoggerImpl} instances print out their output according * to the last state of a previous output (from another instance of the * {@link ConsoleLoggerImpl}), the "bean" attributes refer actually to static * member variables: Their values are valid for all instances of this class! *

* Still the setters and getters are of "instance" scope (not static) to enable * the builder-pattern to be applied. *

* The {@link ConsoleLoggerImpl} by default uses the * {@link SystemUtility#getConsoleWidth()} method, which determines the width in * characters of the system's console in use. In case you pass a * "-Dconsole.width=n" JVM argument, then your width is taken, else the actual * console's width is being tried to be determined. See * {@link SystemUtility#SYS_PROP_CONSOLE_WIDTH}. * * You can also use the {@link #setRowWidth(int)} or {@link #withRowWidth(int)} * method in order to programmatically set the console's row width. * * @param The type of the {@link Record} instances managed by the * {@link Logger}. */ public class ConsoleLoggerImpl implements Destroyable, ConsoleLogger { // ///////////////////////////////////////////////////////////////////////// // ENUM: // ///////////////////////////////////////////////////////////////////////// private enum TableLayout { NONE, MSG, EXC } // ///////////////////////////////////////////////////////////////////////// // CONSTANTS: // ///////////////////////////////////////////////////////////////////////// private static final int HIGH_RES = 1; private static final int LOW_RES = 0; private static final int INDEX_LINE_NUMBER = 0; private static final int INDEX_EXCEPTION = 7; // @formatter:off private static final ColumnWidthMetrics[] A_WIDTH_LINE_NUMBER = new ColumnWidthMetrics[] { new ColumnWidthMetricsImpl( 7, ColumnWidthType.ABSOLUTE), new ColumnWidthMetricsImpl( 10, ColumnWidthType.ABSOLUTE) }; private static final ColumnWidthMetrics[] B_WIDTH_DATE = new ColumnWidthMetrics[] { new ColumnWidthMetricsImpl( 19, ColumnWidthType.ABSOLUTE), new ColumnWidthMetricsImpl( 19, ColumnWidthType.ABSOLUTE) }; private static final ColumnWidthMetrics[] C_WIDTH_PRIORITY = new ColumnWidthMetrics[] { new ColumnWidthMetricsImpl( 7, ColumnWidthType.ABSOLUTE), new ColumnWidthMetricsImpl( 7, ColumnWidthType.ABSOLUTE) }; private static final ColumnWidthMetrics[] D_WIDTH_THREAD = new ColumnWidthMetrics[] { new ColumnWidthMetricsImpl( 2, ColumnWidthType.RELATIVE), new ColumnWidthMetricsImpl( 15, ColumnWidthType.ABSOLUTE) }; private static final ColumnWidthMetrics[] E_WIDTH_CLASS = new ColumnWidthMetrics[] { new ColumnWidthMetricsImpl( 4, ColumnWidthType.RELATIVE), new ColumnWidthMetricsImpl( 1, ColumnWidthType.RELATIVE) }; private static final ColumnWidthMetrics[] F_WIDTH_METHOD = new ColumnWidthMetrics[] { new ColumnWidthMetricsImpl( 3, ColumnWidthType.RELATIVE), new ColumnWidthMetricsImpl( 45, ColumnWidthType.ABSOLUTE) }; private static final ColumnWidthMetrics[] G_WIDTH_MESSAGE = new ColumnWidthMetrics[] { new ColumnWidthMetricsImpl( 7, ColumnWidthType.RELATIVE), new ColumnWidthMetricsImpl( 2, ColumnWidthType.RELATIVE) }; private static final ColumnWidthMetrics[] Z_WIDTH_EXCEPTION = new ColumnWidthMetrics[] { new ColumnWidthMetricsImpl( 1, ColumnWidthType.RELATIVE), new ColumnWidthMetricsImpl( 1, ColumnWidthType.RELATIVE) }; // @formatter:on private static final String ANSI_LINE_NUMBER = ansi().fg( Color.MAGENTA ).bg( Color.DEFAULT ).toString(); private static final String ANSI_DATE = ansi().fg( Color.WHITE ).toString(); private static final String ANSI_THREAD = ansi().fgBright( Color.WHITE ).bg( Color.BLACK ).toString(); private static final String ANSI_MESSAGE = ansi().fg( Color.DEFAULT ).bg( Color.DEFAULT ).toString(); private static final String ANSI_EXCEPTION = ansi().fgBright( Color.RED ).toString(); private static final String ANSI_CLASS = ansi().fg( Color.DEFAULT ).bg( Color.DEFAULT ).toString(); private static final String ANSI_METHOD = ansi().bold().fg( Color.BLUE ).toString(); private static final String ANSI_PRIORITY_WARN = ansi().bold().fg( Color.RED ).toString(); private static final String ANSI_PRIORITY_INFO = ansi().bold().fg( Color.GREEN ).toString(); private static final String ANSI_PRIORITY_PANIC = ansi().bold().fgBright( Color.WHITE ).bgBright( Color.RED ).toString(); private static final String ANSI_PRIORITY_ERROR = ansi().bold().fgBright( Color.RED ).toString(); private static final String ANSI_PRIORITY_DEBUG = ansi().bold().fgBright( Color.YELLOW ).bgBright( Color.BLUE ).toString(); private static final String ANSI_PRIORITY_TRACE = ansi().bold().fg( Color.WHITE ).toString(); // @formatter:off private static String[] ANSI_PRIORITIES = new String[] { ANSI_PRIORITY_TRACE, ANSI_PRIORITY_DEBUG, ANSI_PRIORITY_INFO, ANSI_PRIORITY_INFO, ANSI_PRIORITY_WARN, ANSI_PRIORITY_ERROR, ANSI_PRIORITY_ERROR, ANSI_PRIORITY_ERROR, ANSI_PRIORITY_PANIC }; // @formatter:on // ///////////////////////////////////////////////////////////////////////// // VARIABLES: // ///////////////////////////////////////////////////////////////////////// private Header _header = new RuntimeLoggerHeaderImpl( PrintStackTrace.EXPLODED ); private PrintStream _outStream = SystemUtility.getCommandLineInterpreter() == CommandLineInterpreter.WIN_CMD ? AnsiConsole.out : System.out; private PrintStream _errStream = SystemUtility.getCommandLineInterpreter() == CommandLineInterpreter.WIN_CMD ? AnsiConsole.err : System.err; private TableStyle _tableStyle = SystemUtility.getOperatingSystem() == OperatingSystem.WINDOWS ? TableStyle.ASCII : TableStyle.SINGLE; private boolean _hasLeftBorder = false; private boolean _hasRightBorder = false; private TableBuilder _tableMsgBuilder[] = new TableBuilder[9]; private TableBuilder _tableExcBuilder[] = new TableBuilder[9]; private int _rowWidth = SystemUtility.getConsoleWidth(); // ///////////////////////////////////////////////////////////////////////// // STATE: // ///////////////////////////////////////////////////////////////////////// private TableLayout _lastLayout = TableLayout.NONE; private LogPriority _lastPriority = LogPriority.NONE; private int _resIndex; // ///////////////////////////////////////////////////////////////////////// // CONSTRUCTORS: // ///////////////////////////////////////////////////////////////////////// /** * Initially enables or disables ANSI escape sequences as of detection of * terminal's ANSI support. You can overrule this setting by calling * {@link #setEscCodes(boolean)}. * * @see SystemUtility#isAnsiSupported() */ public ConsoleLoggerImpl() { init(); setEscCodes( SystemUtility.isAnsiSupported() ); } // ///////////////////////////////////////////////////////////////////////// // METHODS: // ///////////////////////////////////////////////////////////////////////// @Override public synchronized void log( Record aRecord ) throws IllegalRecordRuntimeException, UnexpectedLogRuntimeException { try { LogPriority thePriority = (LogPriority) LoggerField.LOG_PRIORITY.getColumn().get( aRecord ); if ( thePriority != LogPriority.DISCARD ) { Row theRow = _header.toPrintableRow( aRecord ); if ( theRow.size() > INDEX_LINE_NUMBER ) { String theLineNumber = theRow.remove( INDEX_LINE_NUMBER ); theRow.add( INDEX_LINE_NUMBER, new HorizAlignTextBuilderImpl().withHorizAlignTextMode( HorizAlignTextMode.RIGHT ).withText( theLineNumber ).withColumnWidth( A_WIDTH_LINE_NUMBER[_resIndex].getColumnWidth() ).withFillChar( '0' ).toString() ); // theRow.add( INDEX_LINE_NUMBER, // AlignTextUtility.toAlignRight( theLineNumber, // A_WIDTH_LINE_NUMBER[_resIndex].getColumnWidth(), '0' ) ); } String theException = null; if ( theRow.size() > INDEX_EXCEPTION ) { theException = theRow.remove( INDEX_EXCEPTION ); } if ( _lastLayout == TableLayout.EXC ) { _tableMsgBuilder[thePriority.getPriority()].printRowEnd( _tableExcBuilder[thePriority.getPriority()] ); _tableMsgBuilder[thePriority.getPriority()].printRowContinue( theRow.toArray( new String[theRow.size()] ) ); } else { if ( _lastPriority == thePriority ) { _tableMsgBuilder[thePriority.getPriority()].printRowContinue( theRow.toArray( new String[theRow.size()] ) ); } else { _tableMsgBuilder[thePriority.getPriority()].printRow( theRow.toArray( new String[theRow.size()] ) ); } } if ( _lastLayout == TableLayout.NONE ) continueTablePrinters( _tableMsgBuilder[thePriority.getPriority()] ); _lastLayout = TableLayout.MSG; if ( theException != null ) { theException = theException.replaceAll( "\t", "--> " ); _tableExcBuilder[thePriority.getPriority()].printRowEnd( _tableMsgBuilder[thePriority.getPriority()] ); _tableExcBuilder[thePriority.getPriority()].printRowContinue( theException ); if ( _lastLayout == TableLayout.NONE ) continueTablePrinters( _tableExcBuilder[thePriority.getPriority()] ); _lastLayout = TableLayout.EXC; } _lastPriority = thePriority; } } catch ( ColumnMismatchException | ClassCastException | HeaderMismatchException e ) { throw new IllegalRecordRuntimeException( aRecord, e ); } } @Override public PrintStream getOutStream() { return _outStream; } @Override public void setOutStream( PrintStream aOutStream ) { _outStream = aOutStream; } @Override public PrintStream getErrStream() { return _errStream; } @Override public void setErrStream( PrintStream aErrStream ) { _errStream = aErrStream; } @Override public TableStyle getTableStyle() { return _tableStyle; } @Override public void setTableStyle( TableStyle aTableStyle ) { _tableStyle = aTableStyle; init(); } @Override public void setTableStyleName( String aTableStyleName ) { setTableStyle( TableStyle.valueOf( aTableStyleName ) ); } @Override public void setEscCodes( boolean isEscCodesEnabled ) { for ( TableBuilder eBuilder : _tableMsgBuilder ) { eBuilder.withEscCodes( isEscCodesEnabled ); } for ( TableBuilder eBuilder : _tableExcBuilder ) { eBuilder.withEscCodes( isEscCodesEnabled ); } } @Override public boolean hasLeftBorder() { return _hasLeftBorder; } @Override public void setLeftBorder( boolean hasLeftBorder ) { _hasLeftBorder = hasLeftBorder; init(); } @Override public boolean hasRightBorder() { return _hasRightBorder; } @Override public void setRightBorder( boolean hasRightBorder ) { _hasRightBorder = hasRightBorder; init(); } @Override public int getRowWidth() { return _rowWidth; } @Override public void setRowWidth( int aRowWidth ) { _rowWidth = aRowWidth; init(); } // @Override // public void setMemberType( MemberType aMemberType ) { // _memberType = aMemberType; // } // @Override // public void setMemberTypeName( String aMemberType ) { // _memberType = MemberType.valueOf( aMemberType ); // } // ///////////////////////////////////////////////////////////////////////// // BUILDER: // ///////////////////////////////////////////////////////////////////////// @Override public ConsoleLogger withOutStream( PrintStream aOutStream ) { _outStream = aOutStream; return this; } @Override public ConsoleLogger withErrStream( PrintStream aErrStream ) { _errStream = aErrStream; return this; } @Override public ConsoleLogger withTableStyle( TableStyle aTableStyle ) { _tableStyle = aTableStyle; init(); return this; } @Override public ConsoleLogger withEscCodes( boolean isEscCodesEnabled ) { setEscCodes( isEscCodesEnabled ); return this; } @Override public ConsoleLogger withLeftBorder( boolean hasLeftBorder ) { _hasLeftBorder = hasLeftBorder; return this; } @Override public ConsoleLogger withRightBorder( boolean hasRightBorder ) { _hasRightBorder = hasRightBorder; return this; } @Override public ConsoleLogger withRowWidth( int aRowWidth ) { setRowWidth( aRowWidth ); return this; } // ///////////////////////////////////////////////////////////////////////// // LIFECYCLE: // ///////////////////////////////////////////////////////////////////////// @Override public void destroy() { if ( SystemUtility.getCommandLineInterpreter() == CommandLineInterpreter.WIN_CMD ) AnsiConsole.systemUninstall(); if ( _lastLayout == TableLayout.MSG ) { _tableMsgBuilder[0].printTail(); } else if ( _lastLayout == TableLayout.EXC ) { _tableExcBuilder[0].printTail(); } } private void init() { if ( SystemUtility.getCommandLineInterpreter() == CommandLineInterpreter.WIN_CMD ) AnsiConsole.systemInstall(); boolean isLessThanWarn; MoreTextMode eMoreMode; if ( _rowWidth <= RuntimeConsts.MIN_CONSOLE_WIDTH ) { _rowWidth = RuntimeConsts.MIN_CONSOLE_WIDTH; } if ( _rowWidth < RuntimeConsts.MAX_CONSOLE_WIDTH ) { _resIndex = LOW_RES; } else { _resIndex = HIGH_RES; } boolean isLineBreak = true; if ( SystemUtility.getCommandLineInterpreter() == CommandLineInterpreter.WIN_CMD && _rowWidth == SystemUtility.getConsoleWidth() ) { isLineBreak = false; } for ( int i = 0; i < _tableMsgBuilder.length; i++ ) { isLessThanWarn = i < LogPriority.WARN.getPriority(); if ( isLessThanWarn ) eMoreMode = MoreTextMode.LEFT; else eMoreMode = MoreTextMode.NONE; _tableMsgBuilder[i] = new TableBuilderImpl( _rowWidth ).withTableStyle( _tableStyle ).withPrintStream( isLessThanWarn ? _outStream : _errStream ); _tableMsgBuilder[i].withLeftBorder( _hasLeftBorder ).withRightBorder( _hasRightBorder ); if ( !isLineBreak ) _tableMsgBuilder[i].withLineBreak( "" ); _tableMsgBuilder[i].addColumn().withColumnWidthMetrics( A_WIDTH_LINE_NUMBER[_resIndex] ).withColumnEscCode( ANSI_LINE_NUMBER ).withColumnHorizAlignTextMode( HorizAlignTextMode.CENTER ).withColumnMoreTextMode( eMoreMode ); _tableMsgBuilder[i].addColumn().withColumnWidthMetrics( B_WIDTH_DATE[_resIndex] ).withColumnEscCode( ANSI_DATE ).withColumnHorizAlignTextMode( HorizAlignTextMode.RIGHT ).withColumnMoreTextMode( eMoreMode ); _tableMsgBuilder[i].addColumn().withColumnWidthMetrics( C_WIDTH_PRIORITY[_resIndex] ).withColumnEscCode( ANSI_PRIORITIES[i] ).withColumnHorizAlignTextMode( HorizAlignTextMode.LEFT ).withColumnMoreTextMode( eMoreMode ); _tableMsgBuilder[i].addColumn().withColumnWidthMetrics( D_WIDTH_THREAD[_resIndex] ).withColumnEscCode( ANSI_THREAD ).withColumnHorizAlignTextMode( HorizAlignTextMode.LEFT ).withColumnMoreTextMode( eMoreMode ); _tableMsgBuilder[i].addColumn().withColumnWidthMetrics( E_WIDTH_CLASS[_resIndex] ).withColumnEscCode( ANSI_CLASS ).withColumnHorizAlignTextMode( HorizAlignTextMode.LEFT ).withColumnMoreTextMode( eMoreMode ); _tableMsgBuilder[i].addColumn().withColumnWidthMetrics( F_WIDTH_METHOD[_resIndex] ).withColumnEscCode( ANSI_METHOD ).withColumnHorizAlignTextMode( HorizAlignTextMode.LEFT ).withColumnMoreTextMode( eMoreMode ); _tableMsgBuilder[i].addColumn().withColumnWidthMetrics( G_WIDTH_MESSAGE[_resIndex] ).withColumnEscCode( ANSI_MESSAGE ).withColumnHorizAlignTextMode( HorizAlignTextMode.LEFT ).withColumnMoreTextMode( eMoreMode ); } for ( int i = 0; i < _tableExcBuilder.length; i++ ) { isLessThanWarn = i < LogPriority.WARN.getPriority(); _tableExcBuilder[i] = new TableBuilderImpl( _rowWidth ).withTableStyle( _tableStyle == TableStyle.SINGLE ? TableStyle.SINGLE_DASHED : _tableStyle ).withPrintStream( isLessThanWarn ? _outStream : _errStream ); _tableExcBuilder[i].withLeftBorder( _hasLeftBorder ).withRightBorder( _hasRightBorder ); if ( !isLineBreak ) _tableExcBuilder[i].withLineBreak( "" ); _tableExcBuilder[i].addColumn().withColumnWidthMetrics( Z_WIDTH_EXCEPTION[_resIndex] ).withColumnEscCode( ANSI_EXCEPTION ).withColumnHorizAlignTextMode( HorizAlignTextMode.LEFT ).withColumnSplitTextMode( SplitTextMode.AT_FIRST_END_OF_LINE ); } } // ///////////////////////////////////////////////////////////////////////// // HOOKS: // ///////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////// // HELPER: // ///////////////////////////////////////////////////////////////////////// private void continueTablePrinters( TableBuilder aTableBuilder ) { for ( int i = 0; i < _tableMsgBuilder.length; i++ ) { if ( aTableBuilder != _tableMsgBuilder[i] ) _tableMsgBuilder[i].setTableStatus( aTableBuilder.getTableStatus() ); if ( aTableBuilder != _tableExcBuilder[i] ) _tableExcBuilder[i].setTableStatus( aTableBuilder.getTableStatus() ); } } }