/*! ****************************************************************************** * * 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.ui.spoon.trans; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; import org.pentaho.di.core.Const; import org.pentaho.di.core.util.EnvUtil; import org.pentaho.di.core.util.Utils; import org.pentaho.di.core.Props; import org.pentaho.di.core.logging.HasLogChannelInterface; import org.pentaho.di.core.logging.KettleLogLayout; import org.pentaho.di.core.logging.KettleLogStore; import org.pentaho.di.core.logging.KettleLoggingEvent; import org.pentaho.di.core.logging.LogChannelInterface; import org.pentaho.di.core.logging.LogLevel; import org.pentaho.di.core.logging.LogParentProvidedInterface; import org.pentaho.di.core.logging.LoggingRegistry; import org.pentaho.di.i18n.BaseMessages; import org.pentaho.di.ui.core.ConstUI; import org.pentaho.di.ui.core.gui.GUIResource; import org.pentaho.di.ui.spoon.Spoon; public class LogBrowser { private static Class<?> PKG = Spoon.class; // for i18n purposes, needed by Translator2!! private StyledText text; private LogParentProvidedInterface logProvider; private List<String> childIds = new ArrayList<String>(); private Date lastLogRegistryChange; private AtomicBoolean paused; public LogBrowser( final StyledText text, final LogParentProvidedInterface logProvider ) { this.text = text; this.logProvider = logProvider; this.paused = new AtomicBoolean( false ); } public void installLogSniffer() { // Create a new buffer appender to the log and capture that directly... // final AtomicInteger lastLogId = new AtomicInteger( -1 ); final AtomicBoolean busy = new AtomicBoolean( false ); final KettleLogLayout logLayout = new KettleLogLayout( true ); final StyleRange normalLogLineStyle = new StyleRange(); normalLogLineStyle.foreground = GUIResource.getInstance().getColorBlue(); final StyleRange errorLogLineStyle = new StyleRange(); errorLogLineStyle.foreground = GUIResource.getInstance().getColorRed(); // Refresh the log every second or so // final Timer logRefreshTimer = new Timer( "log sniffer Timer" ); TimerTask timerTask = new TimerTask() { public void run() { if ( text.isDisposed() ) { return; } text.getDisplay().asyncExec( new Runnable() { public void run() { HasLogChannelInterface provider = logProvider.getLogChannelProvider(); if ( provider != null && !text.isDisposed() && !busy.get() && !paused.get() && text.isVisible() ) { busy.set( true ); LogChannelInterface logChannel = provider.getLogChannel(); String parentLogChannelId = logChannel.getLogChannelId(); LoggingRegistry registry = LoggingRegistry.getInstance(); Date registryModDate = registry.getLastModificationTime(); if ( childIds == null || lastLogRegistryChange == null || registryModDate.compareTo( lastLogRegistryChange ) > 0 ) { lastLogRegistryChange = registry.getLastModificationTime(); childIds = LoggingRegistry.getInstance().getLogChannelChildren( parentLogChannelId ); } // See if we need to log any lines... // int lastNr = KettleLogStore.getLastBufferLineNr(); if ( lastNr > lastLogId.get() ) { List<KettleLoggingEvent> logLines = KettleLogStore.getLogBufferFromTo( childIds, true, lastLogId.get(), lastNr ); // The maximum size of the log buffer // int maxSize = Props.getInstance().getMaxNrLinesInLog() * 150; // int position = text.getSelection().x; // StringBuilder buffer = new StringBuilder(text.getText()); synchronized ( text ) { for ( int i = 0; i < logLines.size(); i++ ) { KettleLoggingEvent event = logLines.get( i ); String line = logLayout.format( event ).trim(); int start = text.getText().length(); int length = line.length(); if ( length > 0 ) { text.append( line ); text.append( Const.CR ); if ( event.getLevel() == LogLevel.ERROR ) { StyleRange styleRange = new StyleRange(); styleRange.foreground = GUIResource.getInstance().getColorRed(); styleRange.start = start; styleRange.length = length; text.setStyleRange( styleRange ); } else { StyleRange styleRange = new StyleRange(); styleRange.foreground = GUIResource.getInstance().getColorBlue(); styleRange.start = start; styleRange.length = Math.min( 20, length ); text.setStyleRange( styleRange ); } } } } // Erase it all in one go // This makes it a bit more efficient // int size = text.getText().length(); if ( maxSize > 0 && size > maxSize ) { int dropIndex = ( text.getText().indexOf( Const.CR, size - maxSize ) ) + Const.CR.length(); text.replaceTextRange( 0, dropIndex, "" ); } text.setSelection( text.getText().length() ); lastLogId.set( lastNr ); } busy.set( false ); } } } ); } }; // Refresh every often enough // logRefreshTimer .schedule( timerTask, Const.toInt( EnvUtil.getSystemProperty( Const.KETTLE_LOG_TAB_REFRESH_DELAY ), 1000 ), Const.toInt( EnvUtil.getSystemProperty( Const.KETTLE_LOG_TAB_REFRESH_PERIOD ), 1000 ) ); // Make sure the timer goes down when the widget is disposed // text.addDisposeListener( new DisposeListener() { public void widgetDisposed( DisposeEvent event ) { logRefreshTimer.cancel(); } } ); final Menu menu = new Menu( text ); MenuItem item = new MenuItem( menu, SWT.NONE ); item.setText( BaseMessages.getString( PKG, "LogBrowser.CopySelectionToClipboard.MenuItem" ) ); item.addSelectionListener( new SelectionAdapter() { public void widgetSelected( SelectionEvent event ) { String selection = text.getSelectionText(); if ( !Utils.isEmpty( selection ) ) { GUIResource.getInstance().toClipboard( selection ); } } } ); text.setMenu( menu ); text.addMouseListener( new MouseAdapter() { public void mouseDown( MouseEvent event ) { if ( event.button == 3 ) { ConstUI.displayMenu( menu, text ); } } } ); } /** * @return the text */ public StyledText getText() { return text; } public LogParentProvidedInterface getLogProvider() { return logProvider; } public boolean isPaused() { return paused.get(); } public void setPaused( boolean paused ) { this.paused.set( paused ); } }