blob: fbec0d3c5f92a9e05ba1d4714ce3b471967ace21 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010-present Sonatype, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Stuart McCulloch (Sonatype, Inc.) - initial API and implementation
*******************************************************************************/
package org.eclipse.sisu.inject;
import java.util.Map;
import java.util.logging.Level;
import com.google.inject.Binding;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.ProvisionException;
import com.google.inject.spi.Element;
import com.google.inject.spi.Elements;
/**
* Utility methods for dealing with container logging and recovery.
* <p>
* Set <b>-Dsisu.debug</b> to send detailed tracing to the console.
*/
public final class Logs
{
// ----------------------------------------------------------------------
// Static initialization
// ----------------------------------------------------------------------
static
{
String newLine;
boolean toConsole;
try
{
newLine = System.getProperty( "line.separator", "\n" );
final String debug = System.getProperty( "sisu.debug", "false" );
toConsole = "".equals( debug ) || "true".equalsIgnoreCase( debug );
}
catch ( final RuntimeException e )
{
newLine = "\n";
toConsole = false;
}
NEW_LINE = newLine;
Sink sink;
try
{
sink = toConsole ? new ConsoleSink() : new SLF4JSink();
}
catch ( final RuntimeException e )
{
sink = new JULSink();
}
catch ( final LinkageError e )
{
sink = new JULSink();
}
SINK = sink;
}
// ----------------------------------------------------------------------
// Constants
// ----------------------------------------------------------------------
public static final String NEW_LINE;
private static final String SISU = "Sisu";
private static final Sink SINK;
public static final boolean TRACE_ENABLED = SINK.isTraceEnabled();
// ----------------------------------------------------------------------
// Constructors
// ----------------------------------------------------------------------
private Logs()
{
// static utility class, not allowed to create instances
}
// ----------------------------------------------------------------------
// Utility methods
// ----------------------------------------------------------------------
/**
* Logs a trace message; uses "{}" format anchors. Pass {@link Throwable}s in last parameter for special handling.
*
* @param format The trace message format
* @param arg1 First object to format
* @param arg2 Second object to format
*/
public static void trace( final String format, final Object arg1, final Object arg2 )
{
if ( TRACE_ENABLED )
{
SINK.trace( format( format( format, arg1 ), arg2 ), arg2 instanceof Throwable ? (Throwable) arg2 : null );
}
}
/**
* Logs a warning message; uses "{}" format anchors. Pass {@link Throwable}s in last parameter for special handling.
*
* @param format The warning message format
* @param arg1 First object to format
* @param arg2 Second object to format
*/
public static void warn( final String format, final Object arg1, final Object arg2 )
{
SINK.warn( format( format( format, arg1 ), arg2 ), arg2 instanceof Throwable ? (Throwable) arg2 : null );
}
/**
* Helper method for catching {@link Throwable}s; severe errors such as {@link ThreadDeath} are always re-thrown.
*
* @param problem The problem
*/
public static void catchThrowable( final Throwable problem )
{
for ( Throwable cause = problem; cause != null; cause = cause.getCause() )
{
if ( cause instanceof ThreadDeath || cause instanceof VirtualMachineError )
{
throw (Error) cause; // must immediately re-throw severe errors
}
}
}
/**
* Helper method for throwing {@link Throwable}s; checked exceptions are wrapped as {@link ProvisionException}s.
*
* @param problem The problem
*/
public static void throwUnchecked( final Throwable problem )
{
if ( problem instanceof RuntimeException )
{
throw (RuntimeException) problem;
}
if ( problem instanceof Error )
{
throw (Error) problem;
}
// this cast lets us load the 'Logs' class and log messages even if Guice is not available
throw RuntimeException.class.cast( new ProvisionException( problem.toString(), problem ) );
}
/**
* Returns an identity string for the given object.
*
* @see System#identityHashCode(Object)
* @param object The object
* @return Identity string of the object.
*/
public static String identityToString( final Object object )
{
return null == object ? null : object.getClass().getName() + '@' //
+ Integer.toHexString( System.identityHashCode( object ) );
}
/**
* Returns a string representation of the given {@link Module}.
*
* @param module The module
* @return String representation of the module.
*/
public static String toString( final Module module )
{
final StringBuilder buf = new StringBuilder( identityToString( module ) );
buf.append( NEW_LINE ).append( NEW_LINE );
buf.append( "-----[elements]----------------------------------------------------------------" ).append( NEW_LINE );
int i = 0;
for ( final Element e : Elements.getElements( module ) )
{
buf.append( i++ ).append( ". " ).append( e ).append( NEW_LINE );
}
return buf.append( "-------------------------------------------------------------------------------" ).append( NEW_LINE ).toString();
}
/**
* Returns a string representation of the given {@link Injector}.
*
* @param injector The injector
* @return String representation of the injector.
*/
public static String toString( final Injector injector )
{
final StringBuilder buf = new StringBuilder( identityToString( injector ) );
if ( null != injector.getParent() )
{
buf.append( " parent: " ).append( identityToString( injector.getParent() ) );
}
buf.append( NEW_LINE ).append( NEW_LINE );
buf.append( "-----[explicit bindings]-------------------------------------------------------" ).append( NEW_LINE );
int i = 0;
final Map<Key<?>, Binding<?>> explicitBindings = injector.getBindings();
for ( final Binding<?> b : explicitBindings.values() )
{
buf.append( i++ ).append( ". " ).append( b ).append( NEW_LINE );
}
buf.append( "-----[implicit bindings]-------------------------------------------------------" ).append( NEW_LINE );
for ( final Binding<?> b : injector.getAllBindings().values() )
{
if ( !explicitBindings.containsKey( b.getKey() ) )
{
buf.append( i++ ).append( ". " ).append( b ).append( NEW_LINE );
}
}
return buf.append( "-------------------------------------------------------------------------------" ).append( NEW_LINE ).toString();
}
// ----------------------------------------------------------------------
// Implementation methods
// ----------------------------------------------------------------------
/**
* Replaces the first available formatting anchor with the given object.
*
* @param format The format string
* @param arg The object to format
*/
private static String format( final String format, final Object arg )
{
final int len = format.length();
boolean detailed = true;
int cursor = 0;
for ( char prevChar = ' ', currChar; cursor < len; prevChar = currChar, cursor++ )
{
currChar = format.charAt( cursor );
if ( prevChar == '{' && currChar == '}' )
{
break; // replace anchor with String.valueOf
}
if ( prevChar == '<' && currChar == '>' )
{
detailed = false;
break; // use Logs.identityToString instead
}
}
if ( cursor >= len )
{
return format;
}
final StringBuilder buf = new StringBuilder();
if ( --cursor > 0 )
{
buf.append( format.substring( 0, cursor ) );
}
try
{
buf.append( detailed ? arg : identityToString( arg ) );
}
catch ( final RuntimeException e )
{
buf.append( e );
}
cursor += 2;
if ( cursor < len )
{
buf.append( format.substring( cursor, len ) );
}
return buf.toString();
}
// ----------------------------------------------------------------------
// Implementation types
// ----------------------------------------------------------------------
/**
* Something that accepts formatted messages.
*/
private interface Sink
{
/**
* @return {@code true} if trace is enabled; otherwise {@code false}
*/
boolean isTraceEnabled();
/**
* Accepts a trace message and optional exception cause.
*
* @param message The trace message
* @param cause The exception cause
*/
void trace( String message, Throwable cause );
/**
* Accepts a warning message and optional exception cause.
*
* @param message The warning message
* @param cause The exception cause
*/
void warn( String message, Throwable cause );
}
/**
* {@link Sink}s messages to the system console.
*/
static final class ConsoleSink
implements Sink
{
// ----------------------------------------------------------------------
// Constants
// ----------------------------------------------------------------------
private static final String TRACE = "TRACE: " + SISU + " - ";
private static final String WARN = "WARN: " + SISU + " - ";
// ----------------------------------------------------------------------
// Public methods
// ----------------------------------------------------------------------
public boolean isTraceEnabled()
{
return true;
}
public void trace( final String message, final Throwable cause )
{
System.out.println( TRACE + message );
if ( null != cause )
{
cause.printStackTrace( System.out );
}
}
public void warn( final String message, final Throwable cause )
{
System.err.println( WARN + message );
if ( null != cause )
{
cause.printStackTrace( System.err );
}
}
}
/**
* {@link Sink}s messages to the JDK.
*/
static final class JULSink
implements Sink
{
// ----------------------------------------------------------------------
// Implementation fields
// ----------------------------------------------------------------------
private static final java.util.logging.Logger logger = java.util.logging.Logger.getLogger( SISU );
// ----------------------------------------------------------------------
// Public methods
// ----------------------------------------------------------------------
public boolean isTraceEnabled()
{
return logger.isLoggable( Level.FINER );
}
public void trace( final String message, final Throwable cause )
{
logger.log( Level.FINER, message, cause );
}
public void warn( final String message, final Throwable cause )
{
logger.log( Level.WARNING, message, cause );
}
}
/**
* {@link Sink}s messages via SLF4J.
*/
static final class SLF4JSink
implements Sink
{
// ----------------------------------------------------------------------
// Implementation fields
// ----------------------------------------------------------------------
private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger( SISU );
// ----------------------------------------------------------------------
// Public methods
// ----------------------------------------------------------------------
public boolean isTraceEnabled()
{
return logger.isTraceEnabled();
}
public void trace( final String message, final Throwable cause )
{
logger.trace( message, cause );
}
public void warn( final String message, final Throwable cause )
{
logger.warn( message, cause );
}
}
}