/*! ****************************************************************************** * * Pentaho Data Integration * * Copyright (C) 2002-2016 by Pentaho : http://www.pentaho.com * ******************************************************************************* * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ******************************************************************************/ package org.pentaho.di.core.logging; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Enumeration; import org.apache.commons.vfs2.FileObject; import org.apache.log4j.Appender; import org.apache.log4j.ConsoleAppender; import org.apache.log4j.Layout; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.pentaho.di.core.Const; import org.pentaho.di.core.util.Utils; import org.pentaho.di.core.exception.KettleException; import org.pentaho.di.core.exception.KettleFileException; import org.pentaho.di.core.vfs.KettleVFS; /** * This class handles the logging. * * @author Matt * @since 25-04-2003 * * @deprecated As of release 7.0, no longer needed. This class is used only in * {@link org.pentaho.di.core.logging.CentralLogStore} which is never used in project */ @Deprecated public class LogWriter { // private static Class<?> PKG = LogWriter.class; // for i18n purposes, needed by Translator2!! private static LogWriter logWriter; public static final String STRING_PENTAHO_DI_LOGGER_NAME = "org.pentaho.di"; public static final String STRING_PENTAHO_DI_CONSOLE_APPENDER = "ConsoleAppender:" + STRING_PENTAHO_DI_LOGGER_NAME; public static final String STRING_PENTAHO_BASERVER_FILE_APPENDER = "PENTAHOFILE"; //$NON-NLS-1$ public static final String STRING_PENTAHO_BASERVER_CONSOLE_APPENDER = "PENTAHOCONSOLE"; //$NON-NLS-1$ // String... private int type; private String filter; // Log4j private Logger pentahoLogger; private Log4jFileAppender fileAppender; private File realFilename; private static Layout layout; // synchronizing logWriter singleton instance PDI-6840 public static final synchronized LogWriter getInstance() { if ( logWriter != null ) { return logWriter; } logWriter = new LogWriter(); return logWriter; } private LogWriter() { pentahoLogger = Logger.getLogger( STRING_PENTAHO_DI_LOGGER_NAME ); pentahoLogger.setAdditivity( false ); // ensure all messages get logged in this logger since we filtered it above // we do not set the level in the rootLogger so the rootLogger can decide by itself (e.g. in the platform) // pentahoLogger.setLevel( Level.ALL ); layout = new Log4jKettleLayout(); /* * The console output is provided for by Kettle5 itself // Add a console logger to see something on the console as well... // boolean consoleAppenderFound = false; Enumeration<?> appenders = pentahoLogger.getAllAppenders(); while (appenders.hasMoreElements()) { Appender appender = (Appender) appenders.nextElement(); if (appender instanceof ConsoleAppender) { consoleAppenderFound = true; break; } } // Play it safe, if another console appender exists for org.pentaho, don't add another one... // if (!consoleAppenderFound) { Layout patternLayout = new PatternLayout("%-5p %d{dd-MM HH:mm:ss,SSS} - %m%n"); ConsoleAppender consoleAppender = new ConsoleAppender(patternLayout); consoleAppender.setName(STRING_PENTAHO_DI_CONSOLE_APPENDER); pentahoLogger.addAppender(consoleAppender); } // BELOW : This is covered in log4j.xml // Doing it here is not really the right location. // // Get rid of the VFS info messages... // LogManager.getLogger("org.apache.commons.vfs").setLevel(Level.WARN); // Hide info messages from Jetty too... // // LogManager.getLogger("org.mortbay.log").setLevel(Level.WARN); // LogManager.getLogger("org.slf4j").setLevel(Level.WARN); */ } /** * Get a new log instance for the specified file if it is not open yet! * @param filename The log file to open * @param exact is this an exact filename (false: prefix of name in temp directory) * @return the LogWriter object */ // synchronizing logWriter singleton instance PDI-6840 public static final synchronized LogWriter getInstance( String filename, boolean exact ) throws KettleException { if ( logWriter != null ) { // OK, see if we have a file appender already for this // if ( logWriter.pentahoLogger.getAppender( LogWriter.createFileAppenderName( filename, exact ) ) == null ) { logWriter.fileAppender = createFileAppender( filename, exact ); logWriter.addAppender( logWriter.fileAppender ); } return logWriter; } logWriter = new LogWriter( filename, exact ); return logWriter; } /** * Closes the file appender opened by the getInstance(filename, exact, level) method */ public static final void closeAndRemoveFileAppender() { if ( logWriter.fileAppender != null ) { logWriter.fileAppender.close(); logWriter.pentahoLogger.removeAppender( logWriter.fileAppender ); } } private LogWriter( String filename, boolean exact ) { this(); try { fileAppender = createFileAppender( filename, exact ); addAppender( fileAppender ); } catch ( Exception e ) { System.out.println( "ERROR OPENING LOG FILE [" + filename + "] --> " + e.toString() ); } } /** * Create a file appender * @param filename The (VFS) filename (URL) to write to. * @param exact is this an exact filename of a filename to be stored in "java.io.tmp" * @return A new file appender * @throws KettleFileException In case there is a problem opening the file. */ public static final Log4jFileAppender createFileAppender( String filename, boolean exact ) throws KettleFileException { try { FileObject file; if ( !exact ) { file = KettleVFS.createTempFile( filename, ".log", System.getProperty( "java.io.tmpdir" ) ); } else { file = KettleVFS.getFileObject( filename ); } Log4jFileAppender appender = new Log4jFileAppender( file ); appender.setLayout( new Log4jKettleLayout( true ) ); appender.setName( LogWriter.createFileAppenderName( filename, exact ) ); return appender; } catch ( IOException e ) { throw new KettleFileException( "Unable to add Kettle file appender to Log4J", e ); } } /** * Create a file appender * @param filename The (VFS) filename (URL) to write to. * @param exact is this an exact filename of a filename to be stored in "java.io.tmp" * @param append * @return A new file appender * @throws KettleFileException In case there is a problem opening the file. */ public static final Log4jFileAppender createFileAppender( String filename, boolean exact, boolean append ) throws KettleFileException { try { FileObject file; if ( !exact ) { file = KettleVFS.createTempFile( filename, ".log", System.getProperty( "java.io.tmpdir" ) ); } else { file = KettleVFS.getFileObject( filename ); } Log4jFileAppender appender = new Log4jFileAppender( file, append ); appender.setLayout( new Log4jKettleLayout( true ) ); appender.setName( LogWriter.createFileAppenderName( filename, exact ) ); return appender; } catch ( IOException e ) { throw new KettleFileException( "Unable to add Kettle file appender to Log4J", e ); } } public static final String createFileAppenderName( String filename, boolean exact ) { if ( !exact ) { return "<temp file> : " + filename; } else { return filename; } } /** * * @return a new String appender object capable of capturing the log stream. * It starts to work the instant you add this appender to the Kettle logger. * * @deprecated Please use {@link CentralLogStore.getAppender()} instead. This uses a central logging buffer in stead of a distributed one. * It also supports incremental buffer gets, and much more. */ @Deprecated public static final Log4jStringAppender createStringAppender() { Log4jStringAppender appender = new Log4jStringAppender(); appender.setLayout( new Log4jKettleLayout( true ) ); return appender; } public static void setConsoleAppenderDebug() { Enumeration<?> appenders = Logger.getLogger( STRING_PENTAHO_DI_LOGGER_NAME ).getAllAppenders(); while ( appenders.hasMoreElements() ) { Object appender = appenders.nextElement(); if ( appender instanceof ConsoleAppender || appender instanceof Log4jConsoleAppender ) { if ( appender instanceof ConsoleAppender ) { ( (ConsoleAppender) appender ).setThreshold( Level.DEBUG ); } } } } public int getType() { return type; } public void setType( int type ) { this.type = type; } public boolean close() { boolean isNotEmbedded = true; try { // Close all appenders only if we are not embedded (ie. running a report in BA Server // that has a PDI data source is considered embedded) Logger logger = Logger.getLogger( STRING_PENTAHO_DI_LOGGER_NAME ); Enumeration<?> appenders = logger.getAllAppenders(); while ( appenders.hasMoreElements() ) { final Appender appender = (Appender) appenders.nextElement(); // Check to see if we have registered BA Server appenders if ( ( appender.getName().compareTo( STRING_PENTAHO_BASERVER_FILE_APPENDER ) == 0 ) || ( appender.getName().compareTo( STRING_PENTAHO_BASERVER_CONSOLE_APPENDER ) == 0 ) ) { isNotEmbedded = false; break; } } // If we are not embedded, we can safely close all appenders. if ( isNotEmbedded == true ) { pentahoLogger.removeAllAppenders(); LogWriter.unsetLogWriter(); } } catch ( Exception e ) { isNotEmbedded = false; } return isNotEmbedded; } // synchronizing logWriter singleton instance PDI-6840 private static synchronized void unsetLogWriter() { logWriter = null; } /** * * @param logMessage * @param channelLogLevel */ public void println( LogMessageInterface logMessage, LogLevel channelLogLevel ) { String subject = null; LogLevel logLevel = logMessage.getLevel(); if ( !logLevel.isVisible( channelLogLevel ) ) { return; // not for our eyes. } if ( subject == null ) { subject = "Kettle"; } // Are the message filtered? // if ( !logLevel.isError() && !Utils.isEmpty( filter ) ) { if ( subject.indexOf( filter ) < 0 && logMessage.toString().indexOf( filter ) < 0 ) { return; // "filter" not found in row: don't show! } } switch ( logLevel ) { case ERROR: pentahoLogger.error( logMessage ); break; case ROWLEVEL: case DEBUG: pentahoLogger.debug( logMessage ); break; default: pentahoLogger.info( logMessage ); break; } } public void println( LogMessageInterface message, Throwable e, LogLevel channelLogLevel ) { println( message, channelLogLevel ); String stackTrace = Const.getStackTracker( e ); LogMessage traceMessage = new LogMessage( stackTrace, message.getLogChannelId(), LogLevel.ERROR ); println( traceMessage, channelLogLevel ); } public void setFilter( String filter ) { this.filter = filter; } public String getFilter() { return filter; } /** * This is not thread safe: please try to get the file appender yourself using the static constructor and work from there */ public InputStream getFileInputStream() throws IOException { return KettleVFS.getInputStream( fileAppender.getFile() ); } /** * This is not thread safe: please try to get the file appender yourself using the static constructor and work from there */ public FileObject getFileAppenderFile() throws IOException { return fileAppender.getFile(); } /** * Get the file input stream for a certain appender. * The appender is looked up using the filename * @param filename The exact filename (with path: c:\temp\logfile.txt) or just a filename (spoon.log) * @param exact true if this is the exact filename or just the last part of the complete path. * @return The file input stream of the appender * @throws IOException in case the appender ocan't be found */ public FileInputStream getFileInputStream( String filename, boolean exact ) throws IOException { Appender appender = pentahoLogger.getAppender( createFileAppenderName( filename, exact ) ); if ( appender == null ) { throw new IOException( "Unable to find appender for file: " + filename + " (exact=" + exact + ")" ); } return new FileInputStream( ( (Log4jFileAppender) appender ).getFile().getName().getPathDecoded() ); } /** * @return Returns the realFilename. */ public File getRealFilename() { return realFilename; } /** * @param realFilename The realFilename to set. */ public void setRealFilename( File realFilename ) { this.realFilename = realFilename; } public void addAppender( Appender appender ) { pentahoLogger.addAppender( appender ); } public void removeAppender( Appender appender ) { pentahoLogger.removeAppender( appender ); } public static void setLayout( Layout layout ) { LogWriter.layout = layout; // save for later creation of new files... Enumeration<?> appenders = logWriter.pentahoLogger.getAllAppenders(); while ( appenders.hasMoreElements() ) { Appender appender = (Appender) appenders.nextElement(); if ( appender instanceof Log4jConsoleAppender || appender instanceof Log4jFileAppender ) { appender.setLayout( layout ); } } } public static Layout getLayout() { return layout; } public Logger getPentahoLogger() { return pentahoLogger; } }