/*************************************************************************
* Copyright 2009-2012 Eucalyptus Systems, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*
* Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta
* CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need
* additional information or have any questions.
*
* This file may incorporate work covered under the following copyright
* and permission notice:
*
* Software License Agreement (BSD License)
*
* Copyright (c) 2008, Regents of the University of California
* All rights reserved.
*
* Redistribution and use of this software in source and binary forms,
* with or without modification, are permitted provided that the
* following conditions are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE. USERS OF THIS SOFTWARE ACKNOWLEDGE
* THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE LICENSED MATERIAL,
* COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS SOFTWARE,
* AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING
* IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA,
* SANTA BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY,
* WHICH IN THE REGENTS' DISCRETION MAY INCLUDE, WITHOUT LIMITATION,
* REPLACEMENT OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO
* IDENTIFIED, OR WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT
* NEEDED TO COMPLY WITH ANY SUCH LICENSES OR RIGHTS.
************************************************************************/
package com.eucalyptus.records;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.ResourceBundle;
import java.util.concurrent.Callable;
import org.apache.log4j.Appender;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Hierarchy;
import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.Priority;
import org.apache.log4j.RollingFileAppender;
import org.apache.log4j.spi.Configurator;
import org.apache.log4j.spi.DefaultRepositorySelector;
import org.apache.log4j.spi.LoggerRepository;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.spi.RepositorySelector;
import org.apache.log4j.xml.DOMConfigurator;
import com.eucalyptus.bootstrap.SystemBootstrapper;
import com.eucalyptus.scripting.Groovyness;
import com.eucalyptus.system.BaseDirectory;
import com.eucalyptus.system.EucaLayout;
import com.eucalyptus.system.Threads;
import com.eucalyptus.system.log.EucaHierarchy;
import com.eucalyptus.system.log.EucaLoggingOutputStream;
import com.eucalyptus.system.log.EucaRootLogger;
import com.eucalyptus.system.log.NullEucaLogger;
import com.eucalyptus.util.LogUtil;
import com.google.common.base.Joiner;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
public class Logs {
static {
// Some things were done here to add %i (ThreadID) to the PatternLayout class in log4j.
// It was a bit complicated, requiring creating new LoggerFactory, Logger, and Hierarchy
// classes among other things. Also, default initialization of log4j (basically anywhere
// Logger.getLogger() is called will initialize everything with default values, with no
// easy way to change references to things already created.
// Hence, we force the simplest initialization of the normal log4j. (creating only the root logger)
BasicConfigurator.configure(new ConsoleAppender(new PatternLayout("%d{yyyy-MM-dd HH:mm:ss} %p %m%n")));
// Hack: If we pass "EXTREME" or "EXHAUST" to the dom configurator,
// by default it will use the DEBUG level when we want it to use the trace level
// So we save the old level, change it back when we are done.
String logLevelProp = "euca.log.level";
String oldLogLevel = System.getProperty(logLevelProp);
if ("EXHAUST".equals(oldLogLevel) || "EXTREME".equals(oldLogLevel)) {
System.setProperty(logLevelProp, "TRACE");
}
// Then we run the DOMConfigurator on a new LoggerRepository
URL url = Thread.currentThread().getContextClassLoader().getResource("log4j.xml");
Hierarchy eucaHierarchy = new EucaHierarchy(new EucaRootLogger(Level.DEBUG));
new DOMConfigurator().doConfigure(url, eucaHierarchy);
// Then we hook the new logger repository into the LogManager.
LogManager.setRepositorySelector(new DefaultRepositorySelector(eucaHierarchy), null);
// Now set it back
if ( oldLogLevel != null )
System.setProperty(logLevelProp, oldLogLevel);
}
private static Logger LOG = Logger.getLogger( Logs.class );
/**
* <pre>
* <appender name="cloud-cluster" class="org.apache.log4j.RollingFileAppender">
* <param name="File" value="${euca.log.dir}/cloud-cluster.log" />
* <param name="MaxFileSize" value="10MB" />
* <param name="MaxBackupIndex" value="25" />
* <param name="Threshold" value="${euca.log.level}" />
* <layout class="org.apache.log4j.PatternLayout">
* <param name="ConversionPattern" value="%d{EEE MMM d HH:mm:ss yyyy} %5p [%c{1}:%t] %m%n" />
* </layout>
* </appender>
* <appender name="cloud-exhaust" class="org.apache.log4j.RollingFileAppender">
* <param name="File" value="${euca.log.dir}/cloud-exhaust.log" />
* <param name="MaxFileSize" value="10MB" />
* <param name="MaxBackupIndex" value="25" />
* <param name="Threshold" value="${euca.exhaust.level}" />
* <layout class="org.apache.log4j.PatternLayout">
* <param name="ConversionPattern" value="${euca.log.exhaust.pattern}" />
* </layout>
* </appender>
* </pre>
*/
private static final Logger nullLogger = new NullEucaLogger();
public static Logger extreme( ) {
return LogLevel.EXTREME.logger( );
}
public static Logger exhaust( ) {
return LogLevel.EXHAUST.logger( );
}
public static Logger bootstrap( ) {
return Logger.getLogger( "BOOTSTRAP" );
}
public static void init( ) {
logLevel.get( );
try {
final Logger stdLogger = ( Logs.isExtrrreeeme( ) ? Logger.getLogger( SystemBootstrapper.class ) : Logs.extreme( ) );
final PrintStream oldOut = System.out;
final PrintStream oldErr = System.err;
if ( !System.getProperty( "euca.log.appender", "" ).equals( "console" ) ) {
System.setOut( new PrintStream( new EucaLoggingOutputStream( stdLogger, Level.INFO ), true ) );
System.setErr( new PrintStream( new EucaLoggingOutputStream( stdLogger, Level.ERROR ), true ) );
}
Logger.getRootLogger( ).info( LogUtil.subheader( "Starting system with debugging set as: " + Logs.logLevel.get( ) ) );
} catch ( final Exception t ) {
t.printStackTrace( );
System.exit( 1 );//GRZE: special case, can't open log files, hosed
}
}
public static void reInit( ) {
logLevel.recalculate( );
try {
final Logger stdLogger = ( Logs.isExtrrreeeme( ) ? Logger.getLogger( SystemBootstrapper.class ) : Logs.extreme( ) );
final PrintStream oldOut = System.out;
final PrintStream oldErr = System.err;
if ( !System.getProperty( "euca.log.appender", "" ).equals( "console" ) ) {
System.setOut( new PrintStream( new EucaLoggingOutputStream( stdLogger, Level.INFO ), true ) );
System.setErr( new PrintStream( new EucaLoggingOutputStream( stdLogger, Level.ERROR ), true ) );
}
Logger.getRootLogger( ).info( LogUtil.subheader( "Starting system with debugging set as: " + Logs.logLevel.get( ) ) );
} catch ( final Exception t ) {
t.printStackTrace( );
System.exit( 1 );//GRZE: special case, can't open log files, hosed
}
}
enum LogLevel implements Callable<Boolean> {
ALL,
EXHAUST {
@Override
String level( ) {
return TRACE.name( );
}
@Override
String exhaustLevel( ) {
return this.level( );
}
},
EXTREME {
@Override
String level( ) {
return TRACE.name( );
}
@Override
String exhaustLevel( ) {
return this.level( );
}
},
TRACE,
DEBUG,
INFO,
WARN,
ERROR,
FATAL,
OFF;
private static final String PROP_LOG_LEVEL = "euca.log.level";
private final Logger logger;
LogLevel( ) {
this.logger = Logger.getLogger( this.name( ) );
}
@Override
public Boolean call( ) {
return logLevel.get( ).ordinal( ) <= this.ordinal( );
}
public Logger logger( ) {
if ( this.call( ) ) {
return this.logger;
} else {
return nullLogger;
}
}
private LogLevel init( ) {
System.setProperty( PROP_LOG_LEVEL, this.level( ) );
System.setProperty( "euca.exhaust.level", this.exhaustLevel( ) );
System.setProperty( "euca.log.exhaustive", this.exhaustLevel( ) );
System.setProperty( "euca.log.exhaustive.cc", this.exhaustLevel( ) );
System.setProperty( "euca.log.exhaustive.user", this.exhaustLevel( ) );
System.setProperty( "euca.log.exhaustive.db", this.exhaustLevel( ) );
System.setProperty( "euca.log.exhaustive.external", this.exhaustLevel( ) );
System.setProperty( "euca.log.exhaustive.user", this.exhaustLevel( ) );
return this;
}
String level( ) {
return this.name( );
}
String exhaustLevel( ) {
return this.ordinal( ) <= TRACE.ordinal( ) ? TRACE.name( )
: FATAL.name( );
}
static LogLevel get( ) {
try {
return LogLevel.valueOf( System.getProperty( PROP_LOG_LEVEL ).toUpperCase( ) ).init( );
} catch ( final IllegalArgumentException ex ) {
if ( EXTREME.name( ).equals( System.getProperty( PROP_LOG_LEVEL ).toUpperCase( ) ) ) {
return EXTREME.init( );
} else {
throw ex;
}
} catch ( final NullPointerException ex ) {
return LogLevel.INFO.init( );
}
}
}
private static final class LogLevelResupplier {
private Supplier<LogLevel> realLogLevel;
LogLevelResupplier() {
recalculate();
}
public LogLevel get( ) {
return realLogLevel.get();
}
public void recalculate() {
realLogLevel = Suppliers.memoize( new Supplier<LogLevel>( ) {
@Override
public LogLevel get( ) {
return LogLevel.get( );
}
} );
}
}
private static final LogLevelResupplier logLevel = new LogLevelResupplier();
public static boolean isExtrrreeeme( ) {
return LogLevel.EXTREME.call( );
}
public static boolean isDebug( ) {
return LogLevel.DEBUG.call( );
}
public static boolean isTrace( ) {
return LogLevel.TRACE.call( );
}
@Deprecated
public static String dump( final Object o ) {
String ret = null;
if ( ( ret = groovyDump( o ) ) != null ) {
return ret;
} else if ( ( ret = groovyInspect( o ) ) != null ) {
return ret;
} else {
return ( o == null
? Threads.currentStackFrame( 1 ) + ": null"
: "" + o );
}
}
public static String groovyDump( final Object o ) {
final HashMap ctx = new HashMap( ) {
/**
*
*/
private static final long serialVersionUID = 1L;
{
this.put( "o", o );
}
};
try {
return ""
+ Groovyness.eval( "try {return o.dump()" +
".replaceAll(\"<\",\"[\")" +
".replaceAll(\">\",\"]\")" +
".replaceAll(\"[\\\\w\\\\.]+\\\\.(\\\\w+)@\\\\w*\", { Object[] it -> it[1] })" +
".replaceAll(\"class:class [\\\\w\\\\.]+\\\\.(\\\\w+),\", { Object[] it -> it[1] });" +
"} catch( Exception e ) {return \"\"+o;}", ctx );
} catch ( final Exception ex ) {
LOG.error( ex, ex );
return null;
}
}
public static String groovyInspect( final Object o ) {
final HashMap ctx = new HashMap( ) {
/**
*
*/
private static final long serialVersionUID = 1L;
{
this.put( "o", o );
}
};
try {
return "" + Groovyness.eval( "try{return o.inspect();}catch(Exception e){return \"\"+o;}", ctx );
} catch ( final Exception ex ) {
LOG.error( ex, ex );
return null;
}
}
}