├── README.md ├── .gitignore ├── src └── main │ └── java │ ├── org │ └── kettle │ │ ├── xp │ │ ├── LastZoomBase.java │ │ ├── SetDefaultJobZoomLevelExtensionPoint.java │ │ ├── SetDefaultTransZoomLevelExtensionPoint.java │ │ ├── LogJobExecutionTimeExtensionPoint.java │ │ ├── LogTransExecutionTimeExtensionPoint.java │ │ ├── DrawStepDebugLevelBeeExtensionPoint.java │ │ ├── util │ │ │ ├── SvgLoader.java │ │ │ └── ZoomLevel.java │ │ ├── DrawJobEntryDebugLevelBeeExtensionPoint.java │ │ ├── SetStepDebugLevelExtensionPoint.java │ │ └── ModifyJobEntryLogLevelExtensionPoint.java │ │ ├── xul │ │ ├── spoon_overlays.xul │ │ ├── job_graph_overlays.xul │ │ └── trans_graph_overlays.xul │ │ ├── util │ │ ├── Defaults.java │ │ └── DebugLevelUtil.java │ │ ├── PDIMessages.java │ │ ├── StepDebugLevel.java │ │ ├── DebugPluginsSpoonPlugin.java │ │ ├── JobEntryDebugLevel.java │ │ ├── DebugPluginsHelper.java │ │ └── dialog │ │ ├── StepDebugLevelDialog.java │ │ └── JobEntryDebugLevelDialog.java │ └── bee.svg ├── pom.xml ├── LICENSE └── kettle-debug-plugin.iml /README.md: -------------------------------------------------------------------------------- 1 | # kettle-debug-plugin 2 | 3 | Various useful plugins to increase esae of debugging transformations and jobs 4 | 5 | See: https://github.com/mattcasters/kettle-debug-plugin/wiki -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | 25 | .idea/ 26 | .idea/compiler.xml 27 | .idea/encodings.xml 28 | .idea/uiDesigner.xml 29 | .idea/vcs.xml 30 | 31 | .idea/misc.xml 32 | .idea/workspace.xml 33 | target/ 34 | kettle-debug-plugin.iml 35 | -------------------------------------------------------------------------------- /src/main/java/org/kettle/xp/LastZoomBase.java: -------------------------------------------------------------------------------- 1 | package org.kettle.xp; 2 | 3 | import org.kettle.xp.util.ZoomLevel; 4 | import org.pentaho.di.job.JobPainter; 5 | import org.pentaho.di.trans.TransPainter; 6 | 7 | 8 | public class LastZoomBase { 9 | 10 | private static int lastZoom; 11 | 12 | public void rememberTransZoomLevel(TransPainter transPainter) { 13 | 14 | ZoomLevel.getInstance().setLastTransMagnification( transPainter.getMagnification() ); 15 | } 16 | 17 | public void rememberJobZoomLevel(JobPainter jobPainter) { 18 | 19 | ZoomLevel.getInstance().setLastJobMagnification( jobPainter.getMagnification() ); 20 | } 21 | 22 | 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/org/kettle/xul/spoon_overlays.xul: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/main/java/org/kettle/util/Defaults.java: -------------------------------------------------------------------------------- 1 | package org.kettle.util; 2 | 3 | import java.util.Arrays; 4 | import java.util.HashSet; 5 | import java.util.List; 6 | import java.util.Set; 7 | 8 | public class Defaults { 9 | public static final String DEBUG_GROUP = "debug_plugin"; 10 | 11 | public static final String STEP_ATTR_LOGLEVEL = "log_level"; 12 | public static final String STEP_ATTR_START_ROW = "start_row"; 13 | public static final String STEP_ATTR_END_ROW = "end_row"; 14 | public static final String STEP_ATTR_CONDITION = "condition"; 15 | 16 | public static final String JOBENTRY_ATTR_LOGLEVEL = "entry_log_level"; 17 | public static final String JOBENTRY_ATTR_LOG_RESULT= "entry_log_result"; 18 | public static final String JOBENTRY_ATTR_LOG_VARIABLES= "entry_log_variables"; 19 | public static final String JOBENTRY_ATTR_LOG_RESULT_ROWS = "entry_log_result_rows"; 20 | public static final String JOBENTRY_ATTR_LOG_RESULT_FILES = "entry_log_result_files"; 21 | 22 | public static final String VARIABLE_KETTLE_DEBUG_DURATION = "KETTLE_DEBUG_DURATION"; 23 | 24 | public static final Set VARIABLES_TO_IGNORE = getVariablesToIgnore(); 25 | 26 | private static Set getVariablesToIgnore() { 27 | Set strings = new HashSet<>( ); 28 | 29 | List valuesList = Arrays.asList("vfs.hc.embeddedMetastoreKey"); 30 | strings.addAll( valuesList ); 31 | 32 | return strings; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/kettle/xp/SetDefaultJobZoomLevelExtensionPoint.java: -------------------------------------------------------------------------------- 1 | package org.kettle.xp; 2 | 3 | import org.eclipse.swt.SWT; 4 | import org.eclipse.swt.widgets.Combo; 5 | import org.eclipse.swt.widgets.Control; 6 | import org.eclipse.swt.widgets.Event; 7 | import org.eclipse.swt.widgets.ToolBar; 8 | import org.eclipse.swt.widgets.ToolItem; 9 | import org.kettle.xp.util.ZoomLevel; 10 | import org.pentaho.di.core.exception.KettleException; 11 | import org.pentaho.di.core.extension.ExtensionPoint; 12 | import org.pentaho.di.core.extension.ExtensionPointInterface; 13 | import org.pentaho.di.core.logging.LogChannelInterface; 14 | import org.pentaho.di.job.JobMeta; 15 | import org.pentaho.di.trans.TransMeta; 16 | import org.pentaho.di.ui.spoon.Spoon; 17 | import org.pentaho.di.ui.spoon.job.JobGraph; 18 | import org.pentaho.di.ui.spoon.trans.TransGraph; 19 | 20 | @ExtensionPoint( 21 | id = "SetDefaultJobZoomLevelExtensionPoint", 22 | description = "After a file is opened, set the appropriate zoom level", 23 | extensionPointId = "JobAfterOpen" 24 | ) 25 | /** 26 | * Every time we load a transformation in Spoon, set the zoom level to what we used last time... 27 | */ 28 | public class SetDefaultJobZoomLevelExtensionPoint implements ExtensionPointInterface { 29 | 30 | @Override public void callExtensionPoint( LogChannelInterface log, Object o ) throws KettleException { 31 | 32 | ZoomLevel.changeJobGraphZoomLevel(); 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/kettle/xp/SetDefaultTransZoomLevelExtensionPoint.java: -------------------------------------------------------------------------------- 1 | package org.kettle.xp; 2 | 3 | import org.eclipse.swt.SWT; 4 | import org.eclipse.swt.custom.CTabFolder; 5 | import org.eclipse.swt.custom.CTabItem; 6 | import org.eclipse.swt.graphics.GC; 7 | import org.eclipse.swt.graphics.Image; 8 | import org.eclipse.swt.graphics.Point; 9 | import org.eclipse.swt.widgets.Combo; 10 | import org.eclipse.swt.widgets.Composite; 11 | import org.eclipse.swt.widgets.Control; 12 | import org.eclipse.swt.widgets.Display; 13 | import org.eclipse.swt.widgets.Label; 14 | import org.eclipse.swt.widgets.ToolBar; 15 | import org.eclipse.swt.widgets.ToolItem; 16 | import org.kettle.xp.util.ZoomLevel; 17 | import org.pentaho.di.core.exception.KettleException; 18 | import org.pentaho.di.core.extension.ExtensionPoint; 19 | import org.pentaho.di.core.extension.ExtensionPointInterface; 20 | import org.pentaho.di.core.logging.LogChannelInterface; 21 | import org.pentaho.di.ui.spoon.Spoon; 22 | import org.pentaho.di.ui.spoon.trans.TransGraph; 23 | 24 | @ExtensionPoint( 25 | id = "SetDefaultTransZoomLevelExtensionPoint", 26 | description = "After a file is opened, set the appropriate zoom level", 27 | extensionPointId = "TransAfterOpen" 28 | ) 29 | /** 30 | * Every time we load a transformation in Spoon, set the zoom level to what we used last time... 31 | */ 32 | public class SetDefaultTransZoomLevelExtensionPoint implements ExtensionPointInterface { 33 | 34 | @Override public void callExtensionPoint( LogChannelInterface log, Object o ) throws KettleException { 35 | 36 | ZoomLevel.changeTransGraphZoomLevel(); 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/org/kettle/xul/job_graph_overlays.xul: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/main/java/org/kettle/xul/trans_graph_overlays.xul: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/main/java/org/kettle/PDIMessages.java: -------------------------------------------------------------------------------- 1 | /*! ****************************************************************************** 2 | * 3 | * Pentaho Data Integration 4 | * 5 | * Copyright (C) 2002-2017 by Pentaho : http://www.pentaho.com 6 | * 7 | ******************************************************************************* 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with 11 | * the License. You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | * 21 | ******************************************************************************/ 22 | 23 | package org.kettle; 24 | 25 | import java.util.Enumeration; 26 | import java.util.ResourceBundle; 27 | 28 | import org.pentaho.di.i18n.BaseMessages; 29 | 30 | public class PDIMessages extends ResourceBundle{ 31 | 32 | // private static ResourceBundle lafBundle; 33 | 34 | private Class clz = this.getClass(); 35 | 36 | public PDIMessages(){ 37 | } 38 | 39 | public PDIMessages(Class pkg){ 40 | this.clz = pkg; 41 | } 42 | 43 | @Override 44 | public Enumeration getKeys() { 45 | return null; 46 | } 47 | 48 | @Override 49 | protected Object handleGetObject(String key) { 50 | String result = BaseMessages.getString(clz, key); 51 | return result; 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /src/main/java/org/kettle/xp/LogJobExecutionTimeExtensionPoint.java: -------------------------------------------------------------------------------- 1 | package org.kettle.xp; 2 | 3 | import org.kettle.util.DebugLevelUtil; 4 | import org.kettle.util.Defaults; 5 | import org.pentaho.di.core.exception.KettleException; 6 | import org.pentaho.di.core.extension.ExtensionPoint; 7 | import org.pentaho.di.core.extension.ExtensionPointInterface; 8 | import org.pentaho.di.core.logging.LogChannelInterface; 9 | import org.pentaho.di.job.Job; 10 | import org.pentaho.di.job.JobAdapter; 11 | import org.pentaho.di.trans.Trans; 12 | import org.pentaho.di.trans.TransAdapter; 13 | 14 | import java.util.concurrent.TimeUnit; 15 | 16 | @ExtensionPoint( 17 | id = "LogJobExecutionTimeExtensionPoint", 18 | description = "Logs execution time of a job when it finishes", 19 | extensionPointId = "JobStart" 20 | ) 21 | /** 22 | * set the debug level right before the step starts to run 23 | */ 24 | public class LogJobExecutionTimeExtensionPoint implements ExtensionPointInterface { 25 | 26 | @Override public void callExtensionPoint( LogChannelInterface log, Object o ) throws KettleException { 27 | if (!(o instanceof Job )) { 28 | return; 29 | } 30 | 31 | Job job = (Job) o; 32 | 33 | // If the KETTLE_DEBUG_DURATION variable is set to N or FALSE, we don't log duration 34 | // 35 | String durationVariable = job.getVariable( Defaults.VARIABLE_KETTLE_DEBUG_DURATION, "Y" ); 36 | if ("N".equalsIgnoreCase( durationVariable ) || "FALSE".equalsIgnoreCase( durationVariable)) { 37 | // Nothing to do here 38 | return; 39 | } 40 | 41 | final long startTime = System.currentTimeMillis(); 42 | 43 | job.addJobListener( new JobAdapter() { 44 | @Override public void jobFinished( Job job ) throws KettleException { 45 | long endTime = System.currentTimeMillis(); 46 | double seconds = ((double)endTime - (double)startTime) / 1000; 47 | log.logBasic("Job duration : "+ seconds+" seconds [ "+ DebugLevelUtil.getDurationHMS( seconds ) +" ]"); 48 | 49 | } 50 | } ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/org/kettle/xp/LogTransExecutionTimeExtensionPoint.java: -------------------------------------------------------------------------------- 1 | package org.kettle.xp; 2 | 3 | import org.kettle.util.DebugLevelUtil; 4 | import org.kettle.util.Defaults; 5 | import org.pentaho.di.core.exception.KettleException; 6 | import org.pentaho.di.core.extension.ExtensionPoint; 7 | import org.pentaho.di.core.extension.ExtensionPointInterface; 8 | import org.pentaho.di.core.logging.LogChannelInterface; 9 | import org.pentaho.di.core.logging.LogLevel; 10 | import org.pentaho.di.trans.Trans; 11 | import org.pentaho.di.trans.TransAdapter; 12 | import org.pentaho.di.trans.step.BaseStep; 13 | import org.pentaho.di.trans.step.StepInterface; 14 | 15 | import java.util.List; 16 | import java.util.Map; 17 | import java.util.concurrent.TimeUnit; 18 | 19 | @ExtensionPoint( 20 | id = "LogTransExecutionTimeExtensionPoint", 21 | description = "Logs execution time of a transformation when it finishes", 22 | extensionPointId = "TransformationPrepareExecution" 23 | ) 24 | /** 25 | * set the debug level right before the step starts to run 26 | */ 27 | public class LogTransExecutionTimeExtensionPoint implements ExtensionPointInterface { 28 | 29 | @Override public void callExtensionPoint( LogChannelInterface log, Object o ) throws KettleException { 30 | if (!(o instanceof Trans )) { 31 | return; 32 | } 33 | 34 | Trans trans= (Trans) o; 35 | 36 | // If the KETTLE_DEBUG_DURATION variable is set to N or FALSE, we don't log duration 37 | // 38 | String durationVariable = trans.getVariable( Defaults.VARIABLE_KETTLE_DEBUG_DURATION, "Y" ); 39 | if ("N".equalsIgnoreCase( durationVariable ) || "FALSE".equalsIgnoreCase( durationVariable)) { 40 | // Nothing to do here 41 | return; 42 | } 43 | 44 | final long startTime = System.currentTimeMillis(); 45 | 46 | trans.addTransListener( new TransAdapter() { 47 | @Override public void transFinished( Trans trans ) throws KettleException { 48 | long endTime = System.currentTimeMillis(); 49 | double seconds = ((double)endTime - (double)startTime) / 1000; 50 | log.logBasic("Transformation duration : "+ seconds+" seconds [ "+ DebugLevelUtil.getDurationHMS( seconds ) +" ]"); 51 | } 52 | } ); 53 | 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/org/kettle/StepDebugLevel.java: -------------------------------------------------------------------------------- 1 | package org.kettle; 2 | 3 | import org.pentaho.di.core.Condition; 4 | import org.pentaho.di.core.logging.LogLevel; 5 | 6 | public class StepDebugLevel implements Cloneable { 7 | private LogLevel logLevel; 8 | private int startRow; 9 | private int endRow; 10 | private Condition condition; 11 | 12 | public StepDebugLevel() { 13 | condition = new Condition(); 14 | logLevel=LogLevel.DEBUG; 15 | startRow=-1; 16 | endRow=-1; 17 | } 18 | 19 | public StepDebugLevel( LogLevel logLevel ) { 20 | this(); 21 | this.logLevel = logLevel; 22 | } 23 | 24 | public StepDebugLevel( LogLevel logLevel, int startRow, int endRow, Condition condition ) { 25 | this(logLevel); 26 | this.startRow = startRow; 27 | this.endRow = endRow; 28 | this.condition = condition; 29 | } 30 | 31 | @Override public StepDebugLevel clone() { 32 | return new StepDebugLevel(logLevel, startRow, endRow, (Condition) condition.clone() ); 33 | } 34 | 35 | /** 36 | * Gets logLevel 37 | * 38 | * @return value of logLevel 39 | */ 40 | public LogLevel getLogLevel() { 41 | return logLevel; 42 | } 43 | 44 | /** 45 | * @param logLevel The logLevel to set 46 | */ 47 | public void setLogLevel( LogLevel logLevel ) { 48 | this.logLevel = logLevel; 49 | } 50 | 51 | /** 52 | * Gets startRow 53 | * 54 | * @return value of startRow 55 | */ 56 | public int getStartRow() { 57 | return startRow; 58 | } 59 | 60 | /** 61 | * @param startRow The startRow to set 62 | */ 63 | public void setStartRow( int startRow ) { 64 | this.startRow = startRow; 65 | } 66 | 67 | /** 68 | * Gets endRow 69 | * 70 | * @return value of endRow 71 | */ 72 | public int getEndRow() { 73 | return endRow; 74 | } 75 | 76 | /** 77 | * @param endRow The endRow to set 78 | */ 79 | public void setEndRow( int endRow ) { 80 | this.endRow = endRow; 81 | } 82 | 83 | /** 84 | * Gets condition 85 | * 86 | * @return value of condition 87 | */ 88 | public Condition getCondition() { 89 | return condition; 90 | } 91 | 92 | /** 93 | * @param condition The condition to set 94 | */ 95 | public void setCondition( Condition condition ) { 96 | this.condition = condition; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/org/kettle/xp/DrawStepDebugLevelBeeExtensionPoint.java: -------------------------------------------------------------------------------- 1 | package org.kettle.xp; 2 | 3 | import org.kettle.StepDebugLevel; 4 | import org.kettle.util.DebugLevelUtil; 5 | import org.kettle.util.Defaults; 6 | import org.kettle.xp.util.SvgLoader; 7 | import org.pentaho.di.core.exception.KettleException; 8 | import org.pentaho.di.core.extension.ExtensionPoint; 9 | import org.pentaho.di.core.extension.ExtensionPointInterface; 10 | import org.pentaho.di.core.logging.LogChannelInterface; 11 | import org.pentaho.di.trans.TransPainterExtension; 12 | import org.pentaho.di.ui.spoon.Spoon; 13 | 14 | import java.awt.image.BufferedImage; 15 | import java.util.Map; 16 | 17 | @ExtensionPoint( 18 | id = "DrawStepDebugLevelBeeExtensionPoint", 19 | description = "Draw a bee over a step which has debug level information stored", 20 | extensionPointId = "TransPainterStep" 21 | ) 22 | /** 23 | * Paint steps that have a debug level set... 24 | */ 25 | public class DrawStepDebugLevelBeeExtensionPoint implements ExtensionPointInterface { 26 | 27 | private static BufferedImage beeImage; 28 | 29 | @Override public void callExtensionPoint( LogChannelInterface logChannelInterface, Object o ) throws KettleException { 30 | if ( !( o instanceof TransPainterExtension ) ) { 31 | return; 32 | } 33 | 34 | try { 35 | // The next statement sometimes causes an exception in WebSpoon 36 | // Keep it in the try/catch block 37 | // 38 | Spoon spoon = Spoon.getInstance(); 39 | TransPainterExtension tpe = (TransPainterExtension) o; 40 | 41 | Map stepLevelMap = tpe.transMeta.getAttributesMap().get( Defaults.DEBUG_GROUP ); 42 | 43 | if ( stepLevelMap != null ) { 44 | 45 | String stepname = tpe.stepMeta.getName(); 46 | 47 | final StepDebugLevel debugLevel = DebugLevelUtil.getStepDebugLevel( stepLevelMap, stepname ); 48 | if ( debugLevel != null ) { 49 | 50 | // Paint the bee... 51 | // 52 | // SwtUniversalImage beeImage = SwtSvgImageUtil.getUniversalImage( spoon.getDisplay(), this.getClass().getClassLoader(), "bee.svg" ); 53 | 54 | if ( beeImage == null ) { 55 | beeImage = SvgLoader.transcodeSVGDocument( this.getClass().getClassLoader(), "bee.svg", 30, 26 ); 56 | } 57 | 58 | tpe.gc.drawImage( beeImage, tpe.x1 + tpe.iconsize, tpe.y1 - tpe.iconsize / 2 ); 59 | 60 | } 61 | } 62 | } catch ( Exception e ) { 63 | // Ignore error, not that important 64 | // logChannelInterface.logError( "Unable to handle specific debug level", e ); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/org/kettle/DebugPluginsSpoonPlugin.java: -------------------------------------------------------------------------------- 1 | /*! ****************************************************************************** 2 | * 3 | * Pentaho Data Integration 4 | * 5 | * Copyright (C) 2002-2017 by Pentaho : http://www.pentaho.com 6 | * 7 | ******************************************************************************* 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with 11 | * the License. You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | * 21 | ******************************************************************************/ 22 | 23 | package org.kettle; 24 | 25 | import org.pentaho.di.ui.spoon.SpoonLifecycleListener; 26 | import org.pentaho.di.ui.spoon.SpoonPerspective; 27 | import org.pentaho.di.ui.spoon.SpoonPlugin; 28 | import org.pentaho.di.ui.spoon.SpoonPluginCategories; 29 | import org.pentaho.di.ui.spoon.SpoonPluginInterface; 30 | import org.pentaho.ui.xul.XulDomContainer; 31 | import org.pentaho.ui.xul.XulException; 32 | 33 | @SpoonPlugin( id = "Debug", image = "" ) 34 | @SpoonPluginCategories( { "spoon", "trans-graph", "job-graph" } ) 35 | public class DebugPluginsSpoonPlugin implements SpoonPluginInterface { 36 | 37 | public DebugPluginsSpoonPlugin() { 38 | } 39 | 40 | public void applyToContainer( String category, XulDomContainer container ) throws XulException { 41 | container.registerClassLoader( getClass().getClassLoader() ); 42 | 43 | if ( category.equals( "spoon" ) ) { 44 | container.loadOverlay( "org/kettle/xul/spoon_overlays.xul" ); 45 | container.addEventHandler( DebugPluginsHelper.getInstance() ); 46 | } 47 | if ( category.equals( "trans-graph" ) ) { 48 | container.loadOverlay( "org/kettle/xul/trans_graph_overlays.xul" ); 49 | container.addEventHandler( DebugPluginsHelper.getInstance() ); 50 | } 51 | if ( category.equals( "job-graph" ) ) { 52 | container.loadOverlay( "org/kettle/xul/job_graph_overlays.xul" ); 53 | container.addEventHandler( DebugPluginsHelper.getInstance() ); 54 | } 55 | } 56 | 57 | public SpoonLifecycleListener getLifecycleListener() { 58 | return null; 59 | } 60 | 61 | public SpoonPerspective getPerspective() { 62 | return null; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/org/kettle/xp/util/SvgLoader.java: -------------------------------------------------------------------------------- 1 | package org.kettle.xp.util; 2 | 3 | import org.apache.batik.transcoder.Transcoder; 4 | import org.apache.batik.transcoder.TranscoderInput; 5 | import org.apache.batik.transcoder.TranscoderOutput; 6 | import org.apache.batik.transcoder.image.PNGTranscoder; 7 | import org.pentaho.di.core.exception.KettleException; 8 | 9 | import javax.swing.*; 10 | import java.awt.*; 11 | import java.awt.image.BufferedImage; 12 | import java.io.ByteArrayOutputStream; 13 | import java.io.InputStream; 14 | 15 | public class SvgLoader { 16 | 17 | public static BufferedImage transcodeSVGDocument( ClassLoader classLoader, String filename, int width, int height ) throws KettleException { 18 | // Create a PNG transcoder. 19 | Transcoder t = new PNGTranscoder(); 20 | 21 | // Set the transcoding hints. 22 | t.addTranscodingHint( PNGTranscoder.KEY_WIDTH, new Float(width) ); 23 | t.addTranscodingHint( PNGTranscoder.KEY_HEIGHT, new Float(height) ); 24 | 25 | // Create the transcoder input. 26 | // 27 | InputStream inputStream = classLoader.getResourceAsStream( filename ); 28 | TranscoderInput input = new TranscoderInput( inputStream ); 29 | 30 | ByteArrayOutputStream ostream = null; 31 | try { 32 | // Create the transcoder output. 33 | ostream = new ByteArrayOutputStream(); 34 | TranscoderOutput output = new TranscoderOutput( ostream ); 35 | 36 | // Save the image. 37 | t.transcode( input, output ); 38 | 39 | // Flush and close the stream. 40 | ostream.flush(); 41 | ostream.close(); 42 | } catch( Exception ex ){ 43 | throw new KettleException( "Error loading svg file "+filename, ex ); 44 | } 45 | 46 | // Convert the byte stream into an image. 47 | byte[] imgData = ostream.toByteArray(); 48 | Image img = Toolkit.getDefaultToolkit().createImage( imgData ); 49 | 50 | // Wait until the entire image is loaded. 51 | MediaTracker tracker = new MediaTracker( new JPanel() ); 52 | tracker.addImage( img, 0 ); 53 | try { 54 | tracker.waitForID( 0 ); 55 | } catch( InterruptedException ex ){ 56 | throw new KettleException( "Interrupted", ex ); 57 | } 58 | 59 | // Return the newly rendered image. 60 | // 61 | if (img instanceof BufferedImage) { 62 | return (BufferedImage)img; 63 | } 64 | 65 | // Convert to a buffered image for the Kettle GC API 66 | // 67 | BufferedImage bufferedImage = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_ARGB); 68 | Graphics2D bufferedGc = bufferedImage.createGraphics(); 69 | bufferedGc.drawImage(img, 0, 0, null); 70 | bufferedGc.dispose(); 71 | return bufferedImage; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/org/kettle/xp/DrawJobEntryDebugLevelBeeExtensionPoint.java: -------------------------------------------------------------------------------- 1 | package org.kettle.xp; 2 | 3 | import org.kettle.JobEntryDebugLevel; 4 | import org.kettle.util.DebugLevelUtil; 5 | import org.kettle.util.Defaults; 6 | import org.kettle.xp.util.SvgLoader; 7 | import org.pentaho.di.core.exception.KettleException; 8 | import org.pentaho.di.core.extension.ExtensionPoint; 9 | import org.pentaho.di.core.extension.ExtensionPointInterface; 10 | import org.pentaho.di.core.gui.GCInterface; 11 | import org.pentaho.di.core.logging.LogChannelInterface; 12 | import org.pentaho.di.job.JobPainterExtension; 13 | import org.pentaho.di.job.entry.JobEntryCopy; 14 | import org.pentaho.di.ui.core.PropsUI; 15 | import org.pentaho.di.ui.spoon.Spoon; 16 | 17 | import java.awt.image.BufferedImage; 18 | import java.io.UnsupportedEncodingException; 19 | import java.util.Map; 20 | 21 | @ExtensionPoint( 22 | id = "DrawJobEntryDebugLevelBeeExtensionPoint", 23 | description = "Draw a bee over a job entry which has debug level information stored", 24 | extensionPointId = "JobPainterArrow" 25 | ) 26 | /** 27 | * We need to use the hop drawing logic because the job entry XP is not available 28 | */ 29 | public class DrawJobEntryDebugLevelBeeExtensionPoint implements ExtensionPointInterface { 30 | 31 | private static BufferedImage beeImage; 32 | 33 | @Override public void callExtensionPoint( LogChannelInterface log, Object o ) throws KettleException { 34 | if ( !( o instanceof JobPainterExtension ) ) { 35 | return; 36 | } 37 | 38 | try { 39 | // The next statement sometimes causes an exception in WebSpoon 40 | // Keep it in the try/catch block 41 | // 42 | Spoon spoon = Spoon.getInstance(); 43 | JobPainterExtension jpe = (JobPainterExtension) o; 44 | int iconsize = PropsUI.getInstance().getIconSize(); 45 | 46 | Map entryLevelMap = jpe.jobMeta.getAttributesMap().get( Defaults.DEBUG_GROUP ); 47 | 48 | if ( entryLevelMap != null ) { 49 | 50 | drawBee( jpe.gc, entryLevelMap, jpe.jobHop.getFromEntry(), jpe.x1, jpe.y1, iconsize ); 51 | drawBee( jpe.gc, entryLevelMap, jpe.jobHop.getToEntry(), jpe.x2, jpe.y2, iconsize ); 52 | } 53 | } catch ( Exception e ) { 54 | // Ignore error, not that important 55 | // logChannelInterface.logError( "Unable to handle specific debug level", e ); 56 | } 57 | } 58 | 59 | private void drawBee( GCInterface gc, Map entryLevelMap, JobEntryCopy copy, int x, int y, int iconsize ) throws Exception { 60 | 61 | final JobEntryDebugLevel debugLevel = DebugLevelUtil.getJobEntryDebugLevel( entryLevelMap, copy.toString() ); 62 | if ( debugLevel == null ) { 63 | return; 64 | } 65 | 66 | if ( beeImage == null ) { 67 | beeImage = SvgLoader.transcodeSVGDocument( this.getClass().getClassLoader(), "bee.svg", 30, 26 ); 68 | } 69 | 70 | gc.drawImage( beeImage, x + iconsize / 2, y - iconsize ); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/org/kettle/JobEntryDebugLevel.java: -------------------------------------------------------------------------------- 1 | package org.kettle; 2 | 3 | import org.pentaho.di.core.Condition; 4 | import org.pentaho.di.core.logging.LogLevel; 5 | 6 | public class JobEntryDebugLevel implements Cloneable { 7 | private LogLevel logLevel; 8 | 9 | private boolean loggingResult; 10 | private boolean loggingVariables; 11 | private boolean loggingResultRows; 12 | private boolean loggingResultFiles; 13 | 14 | public JobEntryDebugLevel() { 15 | logLevel=LogLevel.DEBUG; 16 | loggingResult=false; 17 | loggingVariables=false; 18 | loggingResultRows=false; 19 | loggingResultFiles=false; 20 | } 21 | 22 | public JobEntryDebugLevel( LogLevel logLevel ) { 23 | this(); 24 | this.logLevel = logLevel; 25 | } 26 | 27 | public JobEntryDebugLevel( LogLevel logLevel, boolean loggingResult, boolean loggingVariables, boolean loggingResultRows, boolean loggingResultFiles) { 28 | this(logLevel); 29 | this.loggingResult = loggingResult; 30 | this.loggingVariables = loggingVariables; 31 | this.loggingResultRows = loggingResultRows; 32 | this.loggingResultFiles = loggingResultFiles; 33 | } 34 | 35 | @Override public JobEntryDebugLevel clone() { 36 | return new JobEntryDebugLevel(logLevel, loggingResult, loggingVariables, loggingResultRows, loggingResultFiles ); 37 | } 38 | 39 | 40 | /** 41 | * Gets logLevel 42 | * 43 | * @return value of logLevel 44 | */ 45 | public LogLevel getLogLevel() { 46 | return logLevel; 47 | } 48 | 49 | /** 50 | * @param logLevel The logLevel to set 51 | */ 52 | public void setLogLevel( LogLevel logLevel ) { 53 | this.logLevel = logLevel; 54 | } 55 | 56 | /** 57 | * Gets loggingResult 58 | * 59 | * @return value of loggingResult 60 | */ 61 | public boolean isLoggingResult() { 62 | return loggingResult; 63 | } 64 | 65 | /** 66 | * @param loggingResult The loggingResult to set 67 | */ 68 | public void setLoggingResult( boolean loggingResult ) { 69 | this.loggingResult = loggingResult; 70 | } 71 | 72 | /** 73 | * Gets loggingVariables 74 | * 75 | * @return value of loggingVariables 76 | */ 77 | public boolean isLoggingVariables() { 78 | return loggingVariables; 79 | } 80 | 81 | /** 82 | * @param loggingVariables The loggingVariables to set 83 | */ 84 | public void setLoggingVariables( boolean loggingVariables ) { 85 | this.loggingVariables = loggingVariables; 86 | } 87 | 88 | /** 89 | * Gets loggingResultRows 90 | * 91 | * @return value of loggingResultRows 92 | */ 93 | public boolean isLoggingResultRows() { 94 | return loggingResultRows; 95 | } 96 | 97 | /** 98 | * @param loggingResultRows The loggingResultRows to set 99 | */ 100 | public void setLoggingResultRows( boolean loggingResultRows ) { 101 | this.loggingResultRows = loggingResultRows; 102 | } 103 | 104 | /** 105 | * Gets loggingResultFiles 106 | * 107 | * @return value of loggingResultFiles 108 | */ 109 | public boolean isLoggingResultFiles() { 110 | return loggingResultFiles; 111 | } 112 | 113 | /** 114 | * @param loggingResultFiles The loggingResultFiles to set 115 | */ 116 | public void setLoggingResultFiles( boolean loggingResultFiles ) { 117 | this.loggingResultFiles = loggingResultFiles; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.pentaho 7 | pentaho-ce-jar-parent-pom 8 | 8.1-SNAPSHOT 9 | 10 | kettle-debug-plugin 11 | ${project.artifactId} 12 | Various useful plugins to increase esae of debugging transformations and jobs 13 | 14 | 15 | Apache License, version 2.0 16 | https://www.apache.org/licenses/LICENSE-2.0 17 | repo 18 | 19 | 20 | 21 | 22 | 8.1-SNAPSHOT 23 | 1.9.5 24 | 4.4 25 | 26 | 27 | 28 | 29 | eclipse-maven-repo 30 | Eclipse Maven 31 | https://mvnrepository.com 32 | 33 | 34 | 35 | 36 | 37 | 38 | pentaho-kettle 39 | kettle-ui-swt 40 | ${pentaho-kettle.version} 41 | 42 | 43 | pentaho-kettle 44 | kettle-engine 45 | ${pentaho-kettle.version} 46 | 47 | 48 | junit 49 | junit 50 | ${junit.version} 51 | test 52 | 53 | 54 | 55 | 56 | 57 | 58 | install 59 | 60 | 61 | 62 | src/main/java 63 | 64 | **/*.properties 65 | **/*.xul 66 | **/*.svg 67 | 68 | 69 | 70 | 71 | 72 | 73 | maven-dependency-plugin 74 | 75 | 76 | install 77 | 78 | copy-dependencies 79 | 80 | 81 | ${project.build.directory}/lib 82 | provided 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | pentaho-kettle 95 | kettle-ui-swt 96 | ${pentaho-kettle.version} 97 | 98 | 99 | pentaho-kettle 100 | kettle-engine 101 | ${pentaho-kettle.version} 102 | 103 | 104 | junit 105 | junit 106 | ${junit.version} 107 | test 108 | 109 | 110 | 111 | 112 | 113 | 114 | kettle-debug-plugin 115 | 1.6.0-SNAPSHOT 116 | 117 | -------------------------------------------------------------------------------- /src/main/java/org/kettle/xp/util/ZoomLevel.java: -------------------------------------------------------------------------------- 1 | package org.kettle.xp.util; 2 | 3 | import org.apache.commons.lang.StringUtils; 4 | import org.eclipse.swt.SWT; 5 | import org.eclipse.swt.widgets.Combo; 6 | import org.eclipse.swt.widgets.Control; 7 | import org.eclipse.swt.widgets.Event; 8 | import org.eclipse.swt.widgets.Listener; 9 | import org.eclipse.swt.widgets.ToolBar; 10 | import org.eclipse.swt.widgets.ToolItem; 11 | import org.pentaho.di.core.Const; 12 | import org.pentaho.di.ui.core.PropsUI; 13 | import org.pentaho.di.ui.spoon.AbstractGraph; 14 | import org.pentaho.di.ui.spoon.Spoon; 15 | import org.pentaho.di.ui.spoon.job.JobGraph; 16 | import org.pentaho.di.ui.spoon.trans.TransGraph; 17 | 18 | public class ZoomLevel { 19 | 20 | public static final String PROPERTY_LAST_TRANS_MAGNIFICATION = "LastTransMagnification"; 21 | public static final String PROPERTY_LAST_JOB_MAGNIFICATION = "LastJobMagnification"; 22 | 23 | private static ZoomLevel zoomLevel; 24 | 25 | private float lastTransMagnification; 26 | private float lastJobMagnification; 27 | 28 | private ZoomLevel() { 29 | lastTransMagnification = (float) Const.toDouble( PropsUI.getInstance().getProperty( PROPERTY_LAST_TRANS_MAGNIFICATION ), 1.0f ); 30 | lastJobMagnification = (float) Const.toDouble( PropsUI.getInstance().getProperty( PROPERTY_LAST_JOB_MAGNIFICATION ), 1.0f ); 31 | } 32 | 33 | public static ZoomLevel getInstance() { 34 | if ( zoomLevel == null ) { 35 | zoomLevel = new ZoomLevel(); 36 | } 37 | 38 | return zoomLevel; 39 | } 40 | 41 | public void saveProperties() { 42 | PropsUI.getInstance().setProperty( PROPERTY_LAST_TRANS_MAGNIFICATION, Float.toString( lastTransMagnification ) ); 43 | PropsUI.getInstance().setProperty( PROPERTY_LAST_JOB_MAGNIFICATION, Float.toString( lastJobMagnification ) ); 44 | } 45 | 46 | private static Listener zoomListener = event -> { 47 | // get the trans zoom level whenever it's changed actively anywhere by the user... 48 | // 49 | Combo combo = (Combo) event.widget; 50 | String oldZoom = combo.getText(); 51 | String newZoom = oldZoom.substring( 0, event.start ) + event.text + oldZoom.substring( event.end ); 52 | 53 | if ( StringUtils.isEmpty( oldZoom ) || StringUtils.isEmpty( newZoom ) ) { 54 | // Initial set, not worth remembering the incorrect value of 100% (sigh) 55 | // 56 | return; 57 | } 58 | 59 | String zoomText = newZoom.replace( "%", "" ); 60 | double magnification = Const.toDouble( zoomText, 100.0D ) / 100; 61 | ZoomLevel.getInstance().setLastTransMagnification( (float) magnification ); 62 | }; 63 | 64 | public static final void changeTransGraphZoomLevel() { 65 | Spoon spoon = Spoon.getInstance(); 66 | if ( spoon == null ) { 67 | // Not running in spoon, don't care about zoom level... 68 | // 69 | return; 70 | } 71 | 72 | ZoomLevel.getInstance().saveProperties(); 73 | 74 | float magnification = ZoomLevel.getInstance().getLastTransMagnification(); 75 | 76 | correctActiveGraphZoomLevel(spoon.getActiveTransGraph(), magnification); 77 | } 78 | 79 | private static void correctActiveGraphZoomLevel( AbstractGraph graph, float magnification ) { 80 | if ( graph == null ) { 81 | // Nothing to see here, move along! 82 | // 83 | return; 84 | } 85 | 86 | // Find the toolbar, try to set the zoom level based on the last transformation magnification... 87 | // 88 | for ( Control control : graph.getChildren() ) { 89 | if ( control instanceof ToolBar ) { 90 | ToolBar toolBar = (ToolBar) control; 91 | // This is the toolbar at the top of the transformation... 92 | // Find the Combo with the zoom level 93 | // 94 | for ( ToolItem toolItem : toolBar.getItems() ) { 95 | if ( toolItem.getControl() instanceof Combo ) { 96 | toolItem.setWidth( 150 ); 97 | 98 | Combo zoomLevelCombo = (Combo) toolItem.getControl(); 99 | if ( zoomLevelCombo.getItemCount() < 10 ) { 100 | zoomLevelCombo.add( " 400%", 0 ); 101 | zoomLevelCombo.add( " 500%", 0 ); 102 | zoomLevelCombo.add( " 750%", 0 ); 103 | zoomLevelCombo.add( " 1000%", 0 ); 104 | } 105 | 106 | zoomLevelCombo.setText( Integer.toString( Math.round( magnification * 100.0F ) ) + "%" ); 107 | 108 | // Now update the magnification used in TransGraph 109 | // 110 | zoomLevelCombo.removeListener( SWT.Verify, zoomListener ); 111 | zoomLevelCombo.notifyListeners( SWT.Selection, new Event() ); 112 | zoomLevelCombo.addListener( SWT.Verify, zoomListener ); 113 | return; 114 | } 115 | } 116 | } 117 | } 118 | } 119 | 120 | 121 | public static final void changeJobGraphZoomLevel() { 122 | Spoon spoon = Spoon.getInstance(); 123 | if ( spoon == null ) { 124 | // Not running in spoon, don't care about zoom level... 125 | // 126 | return; 127 | } 128 | 129 | ZoomLevel.getInstance().saveProperties(); 130 | 131 | float magnification = ZoomLevel.getInstance().getLastJobMagnification(); 132 | 133 | correctActiveGraphZoomLevel( spoon.getActiveJobGraph(), magnification ); 134 | } 135 | 136 | 137 | /** 138 | * Gets lastTransMagnification 139 | * 140 | * @return value of lastTransMagnification 141 | */ 142 | public float getLastTransMagnification() { 143 | return lastTransMagnification; 144 | } 145 | 146 | /** 147 | * @param lastTransMagnification The lastTransMagnification to set 148 | */ 149 | public void setLastTransMagnification( float lastTransMagnification ) { 150 | this.lastTransMagnification = lastTransMagnification; 151 | } 152 | 153 | /** 154 | * Gets lastJobMagnification 155 | * 156 | * @return value of lastJobMagnification 157 | */ 158 | public float getLastJobMagnification() { 159 | return lastJobMagnification; 160 | } 161 | 162 | /** 163 | * @param lastJobMagnification The lastJobMagnification to set 164 | */ 165 | public void setLastJobMagnification( float lastJobMagnification ) { 166 | this.lastJobMagnification = lastJobMagnification; 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/main/java/org/kettle/xp/SetStepDebugLevelExtensionPoint.java: -------------------------------------------------------------------------------- 1 | package org.kettle.xp; 2 | 3 | import org.kettle.StepDebugLevel; 4 | import org.kettle.util.DebugLevelUtil; 5 | import org.kettle.util.Defaults; 6 | import org.pentaho.di.core.Condition; 7 | import org.pentaho.di.core.exception.KettleException; 8 | import org.pentaho.di.core.exception.KettleStepException; 9 | import org.pentaho.di.core.extension.ExtensionPoint; 10 | import org.pentaho.di.core.extension.ExtensionPointInterface; 11 | import org.pentaho.di.core.logging.LogChannelInterface; 12 | import org.pentaho.di.core.logging.LogLevel; 13 | import org.pentaho.di.core.row.RowMetaInterface; 14 | import org.pentaho.di.trans.Trans; 15 | import org.pentaho.di.trans.step.BaseStep; 16 | import org.pentaho.di.trans.step.RowAdapter; 17 | import org.pentaho.di.trans.step.StepInterface; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | import java.util.Map; 22 | import java.util.concurrent.atomic.AtomicLong; 23 | 24 | @ExtensionPoint( 25 | id = "SetStepDebugLevelExtensionPoint", 26 | description = "Set Step Debug Level Extension Point Plugin", 27 | extensionPointId = "TransformationStartThreads" 28 | ) 29 | /** 30 | * set the debug level right before the step starts to run 31 | */ 32 | public class SetStepDebugLevelExtensionPoint implements ExtensionPointInterface { 33 | 34 | @Override public void callExtensionPoint( LogChannelInterface logChannelInterface, Object o ) throws KettleException { 35 | if (!(o instanceof Trans )) { 36 | return; 37 | } 38 | 39 | Trans trans= (Trans) o; 40 | 41 | Map stepLevelMap = trans.getTransMeta().getAttributesMap().get( Defaults.DEBUG_GROUP ); 42 | 43 | if (stepLevelMap!=null) { 44 | 45 | logChannelInterface.logDetailed( "Set debug level information on transformation : " + trans.getTransMeta().getName() ); 46 | 47 | List stepnames = new ArrayList<>( ); 48 | for ( String key : stepLevelMap.keySet() ) { 49 | 50 | int index = key.indexOf( " : " ); 51 | if ( index > 0 ) { 52 | String stepname = key.substring( 0, index ); 53 | if ( !stepnames.contains( stepname ) ) { 54 | stepnames.add( stepname ); 55 | } 56 | } 57 | } 58 | 59 | for ( String stepname : stepnames ) { 60 | 61 | logChannelInterface.logDetailed( "Handling debug level for step : "+stepname ); 62 | 63 | try { 64 | 65 | final StepDebugLevel debugLevel = DebugLevelUtil.getStepDebugLevel( stepLevelMap, stepname ); 66 | if ( debugLevel != null ) { 67 | 68 | logChannelInterface.logDetailed("Found debug level info for step "+stepname); 69 | 70 | List baseSteps = trans.findBaseSteps( stepname ); 71 | 72 | if (debugLevel.getStartRow()<0 && debugLevel.getEndRow()<0 && debugLevel.getCondition().isEmpty()) { 73 | 74 | logChannelInterface.logDetailed("Set logging level for step "+stepname+" to "+debugLevel.getLogLevel().getDescription()); 75 | 76 | // Just a general log level on the step 77 | // 78 | String logLevelCode = stepLevelMap.get( stepname ); 79 | for ( StepInterface stepInterface : baseSteps ) { 80 | if ( stepInterface instanceof BaseStep ) { 81 | BaseStep baseStep = (BaseStep) stepInterface; 82 | LogLevel logLevel = debugLevel.getLogLevel(); 83 | baseStep.setLogLevel( logLevel ); 84 | logChannelInterface.logDetailed( "SET LOGGING LEVEL " + logLevel.getDescription() + " ON STEP COPY " + baseStep.getStepname() + "." + baseStep.getCopy() ); 85 | } 86 | } 87 | } else { 88 | 89 | // We need to look at every row 90 | // 91 | for ( StepInterface stepInterface : baseSteps ) { 92 | 93 | final LogLevel baseLogLevel = stepInterface.getLogChannel().getLogLevel(); 94 | final AtomicLong rowCounter = new AtomicLong( 0L ); 95 | 96 | stepInterface.addRowListener( new RowAdapter() { 97 | 98 | @Override public void rowReadEvent( RowMetaInterface rowMeta, Object[] row ) throws KettleStepException { 99 | 100 | rowCounter.incrementAndGet(); 101 | boolean enabled = false; 102 | 103 | Condition condition = debugLevel.getCondition(); 104 | 105 | if (debugLevel.getStartRow()>0 && rowCounter.get()>=debugLevel.getStartRow() && debugLevel.getEndRow()>=0 && debugLevel.getEndRow()>=rowCounter.get()) { 106 | // If we have a start and an end, we want to stay between start and end 107 | enabled = true; 108 | } else if (debugLevel.getStartRow()<=0 && debugLevel.getEndRow()>=0 && rowCounter.get()<=debugLevel.getEndRow()) { 109 | // If don't have a start row, just and end... 110 | enabled = true; 111 | } else if (debugLevel.getEndRow()<=0 && debugLevel.getStartRow()>=0 && rowCounter.get()>=debugLevel.getStartRow()) { 112 | enabled = true; 113 | } 114 | 115 | if ( (debugLevel.getStartRow()<=0 && debugLevel.getEndRow()<=0 || enabled) && !condition.isEmpty()) { 116 | enabled = condition.evaluate( rowMeta, row ); 117 | } 118 | 119 | if (enabled) { 120 | stepInterface.getLogChannel().setLogLevel( debugLevel.getLogLevel() ); 121 | } 122 | } 123 | 124 | @Override public void rowWrittenEvent( RowMetaInterface rowMeta, Object[] row ) throws KettleStepException { 125 | 126 | // Set the log level back to the original value. 127 | // 128 | stepInterface.getLogChannel().setLogLevel( baseLogLevel ); 129 | } 130 | } ); 131 | 132 | } 133 | } 134 | } 135 | } catch ( Exception e ) { 136 | logChannelInterface.logError( "Unable to handle specific debug level for step : " + stepname, e ); 137 | } 138 | } 139 | 140 | } 141 | 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/main/java/org/kettle/util/DebugLevelUtil.java: -------------------------------------------------------------------------------- 1 | package org.kettle.util; 2 | 3 | import org.apache.commons.lang.StringUtils; 4 | import org.kettle.JobEntryDebugLevel; 5 | import org.kettle.StepDebugLevel; 6 | import org.pentaho.di.core.Condition; 7 | import org.pentaho.di.core.Const; 8 | import org.pentaho.di.core.exception.KettleValueException; 9 | import org.pentaho.di.core.exception.KettleXMLException; 10 | import org.pentaho.di.core.logging.LogLevel; 11 | 12 | import java.io.UnsupportedEncodingException; 13 | import java.util.Base64; 14 | import java.util.Map; 15 | import java.util.concurrent.TimeUnit; 16 | 17 | public class DebugLevelUtil { 18 | 19 | public static void storeStepDebugLevel( Map debugGroupAttributesMap, String stepName, StepDebugLevel debugLevel ) throws KettleValueException, UnsupportedEncodingException { 20 | debugGroupAttributesMap.put(stepName + " : " +Defaults.STEP_ATTR_LOGLEVEL, debugLevel.getLogLevel().getCode()); 21 | debugGroupAttributesMap.put(stepName + " : " +Defaults.STEP_ATTR_START_ROW, Integer.toString(debugLevel.getStartRow())); 22 | debugGroupAttributesMap.put(stepName + " : " +Defaults.STEP_ATTR_END_ROW, Integer.toString(debugLevel.getEndRow())); 23 | 24 | String conditionXmlString = Base64.getEncoder().encodeToString( debugLevel.getCondition().getXML().getBytes( "UTF-8" ) ); 25 | debugGroupAttributesMap.put(stepName + " : " +Defaults.STEP_ATTR_CONDITION, conditionXmlString); 26 | } 27 | 28 | public static StepDebugLevel getStepDebugLevel( Map debugGroupAttributesMap, String stepName ) throws UnsupportedEncodingException, KettleXMLException { 29 | 30 | 31 | String logLevelCode = debugGroupAttributesMap.get(stepName+ " : " + Defaults.STEP_ATTR_LOGLEVEL ); 32 | String startRowString = debugGroupAttributesMap.get(stepName+ " : " + Defaults.STEP_ATTR_START_ROW ); 33 | String endRowString = debugGroupAttributesMap.get(stepName+ " : " + Defaults.STEP_ATTR_END_ROW ); 34 | String conditionString = debugGroupAttributesMap.get(stepName+ " : " + Defaults.STEP_ATTR_CONDITION ); 35 | 36 | if ( StringUtils.isEmpty(logLevelCode)) { 37 | // Nothing to load 38 | // 39 | return null; 40 | } 41 | 42 | StepDebugLevel debugLevel = new StepDebugLevel(); 43 | debugLevel.setLogLevel( LogLevel.getLogLevelForCode( logLevelCode ) ); 44 | debugLevel.setStartRow( Const.toInt(startRowString, -1) ); 45 | debugLevel.setEndRow( Const.toInt(endRowString, -1) ); 46 | 47 | if (StringUtils.isNotEmpty( conditionString )) { 48 | String conditionXml = new String( Base64.getDecoder().decode( conditionString ), "UTF-8" ); 49 | debugLevel.setCondition( new Condition( conditionXml ) ); 50 | } 51 | return debugLevel; 52 | } 53 | 54 | 55 | public static void clearDebugLevel( Map debugGroupAttributesMap, String stepName) { 56 | debugGroupAttributesMap.remove(stepName+ " : " + Defaults.STEP_ATTR_LOGLEVEL ); 57 | debugGroupAttributesMap.remove(stepName+ " : " + Defaults.STEP_ATTR_START_ROW ); 58 | debugGroupAttributesMap.remove(stepName+ " : " + Defaults.STEP_ATTR_END_ROW ); 59 | debugGroupAttributesMap.remove(stepName+ " : " + Defaults.STEP_ATTR_CONDITION ); 60 | 61 | debugGroupAttributesMap.remove(stepName+ " : " + Defaults.JOBENTRY_ATTR_LOGLEVEL); 62 | debugGroupAttributesMap.remove(stepName+ " : " + Defaults.JOBENTRY_ATTR_LOG_RESULT); 63 | debugGroupAttributesMap.remove(stepName+ " : " + Defaults.JOBENTRY_ATTR_LOG_VARIABLES); 64 | debugGroupAttributesMap.remove(stepName+ " : " + Defaults.JOBENTRY_ATTR_LOG_RESULT_ROWS); 65 | debugGroupAttributesMap.remove(stepName+ " : " + Defaults.JOBENTRY_ATTR_LOG_RESULT_FILES); 66 | } 67 | 68 | public static void storeJobEntryDebugLevel( Map debugGroupAttributesMap, String entryName, JobEntryDebugLevel debugLevel ) throws KettleValueException, UnsupportedEncodingException { 69 | debugGroupAttributesMap.put(entryName + " : " +Defaults.JOBENTRY_ATTR_LOGLEVEL, debugLevel.getLogLevel().getCode()); 70 | debugGroupAttributesMap.put(entryName + " : " +Defaults.JOBENTRY_ATTR_LOG_RESULT, debugLevel.isLoggingResult() ? "Y" : "N"); 71 | debugGroupAttributesMap.put(entryName + " : " +Defaults.JOBENTRY_ATTR_LOG_VARIABLES, debugLevel.isLoggingVariables() ? "Y" : "N"); 72 | debugGroupAttributesMap.put(entryName + " : " +Defaults.JOBENTRY_ATTR_LOG_RESULT_ROWS, debugLevel.isLoggingResultRows() ? "Y" : "N"); 73 | debugGroupAttributesMap.put(entryName + " : " +Defaults.JOBENTRY_ATTR_LOG_RESULT_FILES, debugLevel.isLoggingResultFiles() ? "Y" : "N"); 74 | } 75 | 76 | public static JobEntryDebugLevel getJobEntryDebugLevel( Map debugGroupAttributesMap, String entryName ) throws UnsupportedEncodingException, KettleXMLException { 77 | 78 | String logLevelCode = debugGroupAttributesMap.get(entryName+ " : " + Defaults.JOBENTRY_ATTR_LOGLEVEL); 79 | boolean loggingResult = "Y".equalsIgnoreCase( debugGroupAttributesMap.get(entryName+ " : " + Defaults.JOBENTRY_ATTR_LOG_RESULT) ); 80 | boolean loggingVariables = "Y".equalsIgnoreCase( debugGroupAttributesMap.get(entryName+ " : " + Defaults.JOBENTRY_ATTR_LOG_VARIABLES) ); 81 | boolean loggingResultRows = "Y".equalsIgnoreCase( debugGroupAttributesMap.get(entryName+ " : " + Defaults.JOBENTRY_ATTR_LOG_RESULT_ROWS) ); 82 | boolean loggingResultFiles = "Y".equalsIgnoreCase( debugGroupAttributesMap.get(entryName+ " : " + Defaults.JOBENTRY_ATTR_LOG_RESULT_FILES) ); 83 | 84 | if ( StringUtils.isEmpty(logLevelCode)) { 85 | // Nothing to load 86 | // 87 | return null; 88 | } 89 | 90 | JobEntryDebugLevel debugLevel = new JobEntryDebugLevel(); 91 | debugLevel.setLogLevel( LogLevel.getLogLevelForCode( logLevelCode ) ); 92 | debugLevel.setLoggingResult( loggingResult ); 93 | debugLevel.setLoggingVariables( loggingVariables ); 94 | debugLevel.setLoggingResultRows( loggingResultRows ); 95 | debugLevel.setLoggingResultFiles( loggingResultFiles ); 96 | 97 | return debugLevel; 98 | } 99 | 100 | public static String getDurationHMS(double seconds) { 101 | int day = (int) TimeUnit.SECONDS.toDays((long)seconds); 102 | long hours = TimeUnit.SECONDS.toHours((long)seconds) - (day *24); 103 | long minute = TimeUnit.SECONDS.toMinutes((long)seconds) - (TimeUnit.SECONDS.toHours((long)seconds)* 60); 104 | long second = TimeUnit.SECONDS.toSeconds((long)seconds) - (TimeUnit.SECONDS.toMinutes((long)seconds) *60); 105 | long ms = (long)((seconds - ((long)seconds))*1000); 106 | 107 | StringBuilder hms = new StringBuilder(); 108 | if (day>0) { 109 | hms.append( day + "d " ); 110 | } 111 | if (day>0 || hours>0) { 112 | hms.append(hours + "h "); 113 | } 114 | if (day>0 || hours>0 || minute>0) { 115 | hms.append(String.format( "%2d", minute ) + "' "); 116 | } 117 | hms.append(String.format( "%2d", second ) + "."); 118 | hms.append(String.format("%03d", ms)+"\""); 119 | 120 | return hms.toString(); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/org/kettle/DebugPluginsHelper.java: -------------------------------------------------------------------------------- 1 | /*! ****************************************************************************** 2 | * 3 | * Pentaho Data Integration 4 | * 5 | * Copyright (C) 2002-2017 by Pentaho : http://www.pentaho.com 6 | * 7 | ******************************************************************************* 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with 11 | * the License. You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | * 21 | ******************************************************************************/ 22 | 23 | package org.kettle; 24 | 25 | import org.kettle.dialog.JobEntryDebugLevelDialog; 26 | import org.kettle.dialog.StepDebugLevelDialog; 27 | import org.kettle.util.DebugLevelUtil; 28 | import org.kettle.util.Defaults; 29 | import org.kettle.xp.util.ZoomLevel; 30 | import org.pentaho.di.core.gui.SpoonFactory; 31 | import org.pentaho.di.core.row.RowMeta; 32 | import org.pentaho.di.core.row.RowMetaInterface; 33 | import org.pentaho.di.job.JobMeta; 34 | import org.pentaho.di.job.entry.JobEntryCopy; 35 | import org.pentaho.di.trans.TransMeta; 36 | import org.pentaho.di.trans.step.StepMeta; 37 | import org.pentaho.di.ui.core.dialog.ErrorDialog; 38 | import org.pentaho.di.ui.spoon.ISpoonMenuController; 39 | import org.pentaho.di.ui.spoon.Spoon; 40 | import org.pentaho.di.ui.spoon.job.JobGraph; 41 | import org.pentaho.di.ui.spoon.trans.TransGraph; 42 | import org.pentaho.ui.xul.dom.Document; 43 | import org.pentaho.ui.xul.impl.AbstractXulEventHandler; 44 | 45 | import java.util.HashMap; 46 | import java.util.Map; 47 | 48 | public class DebugPluginsHelper extends AbstractXulEventHandler implements ISpoonMenuController { 49 | protected static Class PKG = DebugPluginsHelper.class; // for i18n 50 | 51 | private static DebugPluginsHelper instance = null; 52 | 53 | private DebugPluginsHelper() { 54 | } 55 | 56 | public static DebugPluginsHelper getInstance() { 57 | if ( instance == null ) { 58 | instance = new DebugPluginsHelper(); 59 | Spoon spoon = ( (Spoon) SpoonFactory.getInstance() ); 60 | spoon.addSpoonMenuController( instance ); 61 | } 62 | return instance; 63 | } 64 | 65 | public String getName() { 66 | return "debugHelper"; 67 | } 68 | 69 | public void updateMenu( Document doc ) { 70 | // Nothing so far. 71 | } 72 | 73 | public void lastZoomLevel() { 74 | 75 | Spoon spoon = ( (Spoon) SpoonFactory.getInstance() ); 76 | TransGraph transGraph = spoon.getActiveTransGraph(); 77 | if (transGraph!=null) { 78 | ZoomLevel.changeTransGraphZoomLevel(); 79 | } 80 | JobGraph jobGraph = spoon.getActiveJobGraph(); 81 | if (jobGraph!=null) { 82 | ZoomLevel.changeJobGraphZoomLevel(); 83 | } 84 | } 85 | 86 | public void setStepLoggingLevel() { 87 | setClearStepLogLevel(true); 88 | } 89 | 90 | public void clearStepLoggingLevel() { 91 | setClearStepLogLevel( false ); 92 | } 93 | 94 | private void setClearStepLogLevel( boolean set) { 95 | Spoon spoon = ( (Spoon) SpoonFactory.getInstance() ); 96 | try { 97 | TransGraph transGraph = spoon.getActiveTransGraph(); 98 | TransMeta transMeta = spoon.getActiveTransformation(); 99 | StepMeta stepMeta = transGraph.getCurrentStep(); 100 | if ( transGraph == null || transMeta == null || stepMeta == null ) { 101 | return; 102 | } 103 | 104 | Map> attributesMap = transMeta.getAttributesMap(); 105 | Map debugGroupAttributesMap = attributesMap.get( Defaults.DEBUG_GROUP ); 106 | 107 | if ( debugGroupAttributesMap == null ) { 108 | debugGroupAttributesMap = new HashMap<>(); 109 | attributesMap.put( Defaults.DEBUG_GROUP, debugGroupAttributesMap ); 110 | } 111 | 112 | if ( !set ) { 113 | DebugLevelUtil.clearDebugLevel( debugGroupAttributesMap, stepMeta.getName()); 114 | transMeta.setChanged(); 115 | spoon.refreshGraph(); 116 | return; 117 | } 118 | 119 | StepDebugLevel debugLevel = DebugLevelUtil.getStepDebugLevel( debugGroupAttributesMap, stepMeta.getName() ); 120 | if ( debugLevel==null ) { 121 | debugLevel = new StepDebugLevel(); 122 | } 123 | 124 | RowMetaInterface inputRowMeta = transMeta.getPrevStepFields( stepMeta ); 125 | StepDebugLevelDialog dialog = new StepDebugLevelDialog( spoon.getShell(), debugLevel, inputRowMeta ); 126 | if (dialog.open()) { 127 | DebugLevelUtil.storeStepDebugLevel(debugGroupAttributesMap, stepMeta.getName(), debugLevel); 128 | } 129 | 130 | transMeta.setChanged(); 131 | spoon.refreshGraph(); 132 | } catch(Exception e) { 133 | new ErrorDialog( spoon.getShell(), "Error", "Unexpected error", e ); 134 | } 135 | } 136 | 137 | 138 | public void clearAllTransLogging() { 139 | Spoon spoon = ( (Spoon) SpoonFactory.getInstance() ); 140 | TransGraph transGraph = spoon.getActiveTransGraph(); 141 | TransMeta transMeta = spoon.getActiveTransformation(); 142 | StepMeta stepMeta = transGraph.getCurrentStep(); 143 | if ( transGraph == null || transMeta == null || stepMeta == null ) { 144 | return; 145 | } 146 | Map> attributesMap = transMeta.getAttributesMap(); 147 | attributesMap.remove( Defaults.DEBUG_GROUP ); 148 | transMeta.setChanged(); 149 | spoon.refreshGraph(); 150 | } 151 | 152 | public void setJobEntryLoggingLevel() { 153 | setClearJobEntryLogLevel(true); 154 | } 155 | 156 | public void clearJobEntryLoggingLevel() { 157 | setClearJobEntryLogLevel( false ); 158 | } 159 | 160 | private void setClearJobEntryLogLevel( boolean set) { 161 | Spoon spoon = ( (Spoon) SpoonFactory.getInstance() ); 162 | try { 163 | JobGraph jobGraph = spoon.getActiveJobGraph(); 164 | JobMeta jobMeta = spoon.getActiveJob(); 165 | JobEntryCopy jobEntryCopy = jobGraph.getJobEntry(); 166 | if ( jobGraph == null || jobMeta == null || jobEntryCopy == null ) { 167 | 168 | return; 169 | } 170 | 171 | Map> attributesMap = jobMeta.getAttributesMap(); 172 | Map debugGroupAttributesMap = attributesMap.get( Defaults.DEBUG_GROUP ); 173 | if ( debugGroupAttributesMap == null ) { 174 | debugGroupAttributesMap = new HashMap<>(); 175 | attributesMap.put( Defaults.DEBUG_GROUP, debugGroupAttributesMap ); 176 | } 177 | 178 | if ( !set ) { 179 | DebugLevelUtil.clearDebugLevel( debugGroupAttributesMap, jobEntryCopy.toString()); 180 | jobMeta.setChanged(); 181 | spoon.refreshGraph(); 182 | return; 183 | } 184 | 185 | JobEntryDebugLevel debugLevel = DebugLevelUtil.getJobEntryDebugLevel( debugGroupAttributesMap, jobEntryCopy.toString() ); 186 | if ( debugLevel==null ) { 187 | debugLevel = new JobEntryDebugLevel(); 188 | } 189 | 190 | JobEntryDebugLevelDialog dialog = new JobEntryDebugLevelDialog( spoon.getShell(), debugLevel); 191 | if (dialog.open()) { 192 | DebugLevelUtil.storeJobEntryDebugLevel(debugGroupAttributesMap, jobEntryCopy.toString(), debugLevel); 193 | } 194 | 195 | jobMeta.setChanged(); 196 | spoon.refreshGraph(); 197 | } catch(Exception e) { 198 | new ErrorDialog( spoon.getShell(), "Error", "Unexpected error", e ); 199 | } 200 | } 201 | 202 | public void clearAllJobLogging() { 203 | Spoon spoon = ( (Spoon) SpoonFactory.getInstance() ); 204 | JobGraph jobGraph = spoon.getActiveJobGraph(); 205 | JobMeta jobMeta = spoon.getActiveJob(); 206 | JobEntryCopy jobEntry = jobGraph.getJobEntry(); 207 | if ( jobGraph == null || jobMeta == null || jobEntry == null ) { 208 | return; 209 | } 210 | Map> attributesMap = jobMeta.getAttributesMap(); 211 | attributesMap.remove( Defaults.DEBUG_GROUP ); 212 | jobMeta.setChanged(); 213 | spoon.refreshGraph(); 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /src/main/java/bee.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml -------------------------------------------------------------------------------- /src/main/java/org/kettle/dialog/StepDebugLevelDialog.java: -------------------------------------------------------------------------------- 1 | package org.kettle.dialog; 2 | 3 | import org.eclipse.swt.SWT; 4 | import org.eclipse.swt.events.SelectionAdapter; 5 | import org.eclipse.swt.events.SelectionEvent; 6 | import org.eclipse.swt.events.ShellAdapter; 7 | import org.eclipse.swt.events.ShellEvent; 8 | import org.eclipse.swt.layout.FormAttachment; 9 | import org.eclipse.swt.layout.FormData; 10 | import org.eclipse.swt.layout.FormLayout; 11 | import org.eclipse.swt.widgets.Button; 12 | import org.eclipse.swt.widgets.Combo; 13 | import org.eclipse.swt.widgets.Control; 14 | import org.eclipse.swt.widgets.Dialog; 15 | import org.eclipse.swt.widgets.Display; 16 | import org.eclipse.swt.widgets.Label; 17 | import org.eclipse.swt.widgets.Shell; 18 | import org.eclipse.swt.widgets.Text; 19 | import org.kettle.StepDebugLevel; 20 | import org.pentaho.di.core.Const; 21 | import org.pentaho.di.core.logging.LogLevel; 22 | import org.pentaho.di.core.row.RowMetaInterface; 23 | import org.pentaho.di.i18n.BaseMessages; 24 | import org.pentaho.di.ui.core.PropsUI; 25 | import org.pentaho.di.ui.core.gui.GUIResource; 26 | import org.pentaho.di.ui.core.gui.WindowProperty; 27 | import org.pentaho.di.ui.core.widget.ConditionEditor; 28 | import org.pentaho.di.ui.trans.step.BaseStepDialog; 29 | 30 | public class StepDebugLevelDialog extends Dialog { 31 | private static Class PKG = StepDebugLevelDialog.class; // for i18n purposes, needed by Translator2!! 32 | 33 | private StepDebugLevel input; 34 | private StepDebugLevel debugLevel; 35 | private RowMetaInterface inputRowMeta; 36 | 37 | private Shell shell; 38 | 39 | // Connection properties 40 | // 41 | private Combo wLogLevel; 42 | private Text wStartRow; 43 | private Text wEndRow; 44 | private ConditionEditor wCondition; 45 | 46 | private Control lastControl; 47 | 48 | private PropsUI props; 49 | 50 | private int middle; 51 | private int margin; 52 | 53 | private boolean ok; 54 | 55 | public StepDebugLevelDialog( Shell par, StepDebugLevel debugLevel, RowMetaInterface inputRowMeta) { 56 | super( par, SWT.NONE ); 57 | this.input = debugLevel; 58 | this.inputRowMeta = inputRowMeta; 59 | props = PropsUI.getInstance(); 60 | ok = false; 61 | 62 | this.debugLevel = input.clone(); 63 | 64 | } 65 | 66 | public boolean open() { 67 | Shell parent = getParent(); 68 | shell = new Shell( parent, SWT.DIALOG_TRIM | SWT.RESIZE | SWT.MAX | SWT.MIN ); 69 | props.setLook( shell ); 70 | shell.setImage( GUIResource.getInstance().getImageSlave() ); 71 | 72 | middle = props.getMiddlePct(); 73 | margin = Const.MARGIN + 2; 74 | 75 | FormLayout formLayout = new FormLayout(); 76 | formLayout.marginWidth = Const.FORM_MARGIN; 77 | formLayout.marginHeight = Const.FORM_MARGIN; 78 | 79 | shell.setText( "Step debug Level" ); 80 | shell.setLayout( formLayout ); 81 | 82 | // The name 83 | Label wlName = new Label( shell, SWT.RIGHT ); 84 | props.setLook( wlName ); 85 | wlName.setText( "Log level to set " ); 86 | FormData fdlName = new FormData(); 87 | fdlName.top = new FormAttachment( 0, margin ); 88 | fdlName.left = new FormAttachment( 0, 0 ); // First one in the left top corner 89 | fdlName.right = new FormAttachment( middle, -margin ); 90 | wlName.setLayoutData( fdlName ); 91 | wLogLevel = new Combo( shell, SWT.SINGLE | SWT.LEFT | SWT.BORDER ); 92 | wLogLevel.setItems( LogLevel.getLogLevelDescriptions()); 93 | props.setLook( wLogLevel ); 94 | FormData fdName = new FormData(); 95 | fdName.top = new FormAttachment( wlName, 0, SWT.CENTER ); 96 | fdName.left = new FormAttachment( middle, 0 ); // To the right of the label 97 | fdName.right = new FormAttachment( 100, 0 ); 98 | wLogLevel.setLayoutData( fdName ); 99 | lastControl = wLogLevel; 100 | 101 | // Start row option 102 | Label wlStartRow = new Label( shell, SWT.RIGHT ); 103 | props.setLook( wlStartRow ); 104 | wlStartRow.setText( "Start row "); 105 | FormData fdlStartRow = new FormData(); 106 | fdlStartRow.top = new FormAttachment( lastControl, margin ); 107 | fdlStartRow.left = new FormAttachment( 0, 0 ); // First one in the left top corner 108 | fdlStartRow.right = new FormAttachment( middle, -margin ); 109 | wlStartRow.setLayoutData( fdlStartRow ); 110 | wStartRow = new Text( shell, SWT.SINGLE | SWT.LEFT | SWT.BORDER ); 111 | props.setLook( wStartRow ); 112 | FormData fdStartRow = new FormData(); 113 | fdStartRow.top = new FormAttachment( wlStartRow, 0, SWT.CENTER ); 114 | fdStartRow.left = new FormAttachment( middle, 0 ); // To the right of the label 115 | fdStartRow.right = new FormAttachment( 100, 0 ); 116 | wStartRow.setLayoutData( fdStartRow ); 117 | lastControl = wStartRow; 118 | 119 | // End row 120 | Label wlEndRow = new Label( shell, SWT.RIGHT ); 121 | props.setLook( wlEndRow ); 122 | wlEndRow.setText( "End row " ); 123 | FormData fdlEndRow = new FormData(); 124 | fdlEndRow.top = new FormAttachment( lastControl, margin ); 125 | fdlEndRow.left = new FormAttachment( 0, 0 ); // First one in the left top corner 126 | fdlEndRow.right = new FormAttachment( middle, -margin ); 127 | wlEndRow.setLayoutData( fdlEndRow ); 128 | wEndRow = new Text( shell, SWT.SINGLE | SWT.LEFT | SWT.BORDER ); 129 | props.setLook( wEndRow ); 130 | FormData fdEndRow = new FormData(); 131 | fdEndRow.top = new FormAttachment( wlEndRow, 0, SWT.CENTER ); 132 | fdEndRow.left = new FormAttachment( middle, 0 ); // To the right of the label 133 | fdEndRow.right = new FormAttachment( 100, 0 ); 134 | wEndRow.setLayoutData( fdEndRow ); 135 | lastControl = wEndRow; 136 | 137 | // Buttons 138 | Button wOK = new Button( shell, SWT.PUSH ); 139 | wOK.setText( BaseMessages.getString( PKG, "System.Button.OK" ) ); 140 | wOK.addListener( SWT.Selection, e -> ok() ); 141 | 142 | Button wCancel = new Button( shell, SWT.PUSH ); 143 | wCancel.setText( BaseMessages.getString( PKG, "System.Button.Cancel" ) ); 144 | wCancel.addListener( SWT.Selection, e -> cancel() ); 145 | 146 | Button[] buttons = new Button[] { wOK, wCancel }; 147 | BaseStepDialog.positionBottomButtons( shell, buttons, margin, null ); 148 | 149 | SelectionAdapter selAdapter = new SelectionAdapter() { 150 | public void widgetDefaultSelected( SelectionEvent e ) { 151 | ok(); 152 | } 153 | }; 154 | 155 | wLogLevel.addSelectionListener( selAdapter ); 156 | wStartRow.addSelectionListener( selAdapter ); 157 | wEndRow.addSelectionListener( selAdapter ); 158 | 159 | // Condition 160 | Label wlCondition = new Label( shell, SWT.RIGHT); 161 | wlCondition.setText( "Condition : " ); 162 | props.setLook( wlCondition ); 163 | FormData fdlCondition = new FormData(); 164 | fdlCondition.top = new FormAttachment( lastControl, margin ); 165 | fdlCondition.left = new FormAttachment( 0, 0 ); 166 | fdlCondition.right = new FormAttachment( middle, -margin ); 167 | wlCondition.setLayoutData( fdlCondition ); 168 | wCondition = new ConditionEditor( shell, SWT.NONE, debugLevel.getCondition(), inputRowMeta); 169 | props.setLook( wCondition ); 170 | FormData fdCondition = new FormData(); 171 | fdCondition.top = new FormAttachment( lastControl, margin ); 172 | fdCondition.left = new FormAttachment( middle, 0 ); 173 | fdCondition.right = new FormAttachment( 100, 0 ); 174 | fdCondition.bottom= new FormAttachment( wOK, 0 ); 175 | wCondition.setLayoutData( fdCondition ); 176 | lastControl = wCondition; 177 | 178 | // Detect X or ALT-F4 or something that kills this window... 179 | shell.addShellListener( new ShellAdapter() { 180 | public void shellClosed( ShellEvent e ) { 181 | cancel(); 182 | } 183 | } ); 184 | 185 | getData(); 186 | 187 | BaseStepDialog.setSize( shell ); 188 | 189 | shell.open(); 190 | Display display = parent.getDisplay(); 191 | while ( !shell.isDisposed() ) { 192 | if ( !display.readAndDispatch() ) { 193 | display.sleep(); 194 | } 195 | } 196 | return ok; 197 | } 198 | 199 | public void dispose() { 200 | props.setScreen( new WindowProperty( shell ) ); 201 | shell.dispose(); 202 | } 203 | 204 | public void getData() { 205 | wLogLevel.setText( debugLevel.getLogLevel().getDescription() ); 206 | wStartRow.setText( debugLevel.getStartRow() < 0 ? "" : Integer.toString( debugLevel.getStartRow() ) ); 207 | wEndRow.setText( debugLevel.getEndRow() < 0 ? "" : Integer.toString( debugLevel.getEndRow() ) ); 208 | 209 | wLogLevel.setFocus(); 210 | } 211 | 212 | private void cancel() { 213 | ok = false; 214 | dispose(); 215 | } 216 | 217 | public void ok() { 218 | getInfo( input ); 219 | ok = true; 220 | dispose(); 221 | } 222 | 223 | // Get dialog info in securityService 224 | private void getInfo( StepDebugLevel level ) { 225 | int index = Const.indexOfString( wLogLevel.getText(), LogLevel.getLogLevelDescriptions() ); 226 | level.setLogLevel( LogLevel.values()[index] ); 227 | level.setStartRow( Const.toInt( wStartRow.getText(), -1) ); 228 | level.setEndRow( Const.toInt( wEndRow.getText(), -1) ); 229 | level.setCondition( debugLevel.getCondition() ); 230 | } 231 | 232 | } 233 | -------------------------------------------------------------------------------- /src/main/java/org/kettle/xp/ModifyJobEntryLogLevelExtensionPoint.java: -------------------------------------------------------------------------------- 1 | package org.kettle.xp; 2 | 3 | import org.kettle.JobEntryDebugLevel; 4 | import org.kettle.util.DebugLevelUtil; 5 | import org.kettle.util.Defaults; 6 | import org.pentaho.di.core.Result; 7 | import org.pentaho.di.core.ResultFile; 8 | import org.pentaho.di.core.RowMetaAndData; 9 | import org.pentaho.di.core.exception.KettleException; 10 | import org.pentaho.di.core.extension.ExtensionPoint; 11 | import org.pentaho.di.core.extension.ExtensionPointInterface; 12 | import org.pentaho.di.core.logging.LogChannelInterface; 13 | import org.pentaho.di.core.logging.LogLevel; 14 | import org.pentaho.di.core.logging.LoggingObjectInterface; 15 | import org.pentaho.di.core.variables.VariableSpace; 16 | import org.pentaho.di.core.variables.Variables; 17 | import org.pentaho.di.job.Job; 18 | import org.pentaho.di.job.JobEntryListener; 19 | import org.pentaho.di.job.JobExecutionExtension; 20 | import org.pentaho.di.job.JobMeta; 21 | import org.pentaho.di.job.entry.JobEntryCopy; 22 | import org.pentaho.di.job.entry.JobEntryInterface; 23 | import org.pentaho.di.trans.Trans; 24 | 25 | import java.util.HashSet; 26 | import java.util.Map; 27 | import java.util.Set; 28 | 29 | @ExtensionPoint( 30 | id = "ModifyJobEntryLogLevelExtensionPoint", 31 | extensionPointId = "JobStart", 32 | description = "Modify the logging level of an individual job entry if needed" ) 33 | public class ModifyJobEntryLogLevelExtensionPoint implements ExtensionPointInterface { 34 | 35 | public static final String STRING_REFERENCE_VARIABLE_SPACE = "REFERENCE_VARIABLE_SPACE"; 36 | 37 | 38 | @Override public void callExtensionPoint( LogChannelInterface jobLog, Object object ) throws KettleException { 39 | 40 | if ( !( object instanceof Job ) ) { 41 | return; 42 | } 43 | 44 | Job job = (Job) object; 45 | 46 | Job rootJob = job; 47 | Trans rootTrans = null; 48 | while (rootJob!=null || rootTrans!=null) { 49 | 50 | if (rootJob!=null) { 51 | if (rootJob.getParentJob()==null && rootJob.getParentTrans()==null) { 52 | break; 53 | } 54 | rootJob = rootJob.getParentJob(); 55 | rootTrans = rootJob.getParentTrans(); 56 | } else { 57 | if (rootTrans.getParentJob()==null && rootTrans.getParentTrans()==null) { 58 | break; 59 | } 60 | rootJob = rootTrans.getParentJob(); 61 | rootTrans = rootTrans.getParentTrans(); 62 | } 63 | } 64 | Map rootDataMap; 65 | if (rootJob!=null) { 66 | rootDataMap = rootJob.getExtensionDataMap(); 67 | } else { 68 | rootDataMap = rootTrans.getExtensionDataMap(); 69 | } 70 | 71 | // Look for a reference variable space in the root job. 72 | // If non exists, add it. Only do this at the start of the root job, afterwards, never again. 73 | // 74 | final VariableSpace referenceSpace; 75 | synchronized ( rootDataMap ) { 76 | VariableSpace space = (VariableSpace) rootDataMap.get( STRING_REFERENCE_VARIABLE_SPACE ); 77 | if (space==null) { 78 | space = new Variables(); 79 | space.initializeVariablesFrom( job.getJobMeta() ); 80 | rootDataMap.put(STRING_REFERENCE_VARIABLE_SPACE, space); 81 | } 82 | referenceSpace = space; 83 | } 84 | 85 | // Find the debug info in the job metadata 86 | // 87 | JobMeta jobMeta = job.getJobMeta(); 88 | 89 | Map entryLevelMap = jobMeta.getAttributesMap().get( Defaults.DEBUG_GROUP ); 90 | if ( entryLevelMap == null ) { 91 | return; 92 | } 93 | 94 | jobLog.logDetailed( "Set debug level information on job : " + jobMeta.getName() ); 95 | 96 | final Set entries = new HashSet<>(); 97 | for ( String key : entryLevelMap.keySet() ) { 98 | 99 | int index = key.indexOf( " : " ); 100 | if ( index > 0 ) { 101 | String entryName = key.substring( 0, index ); 102 | if ( !entries.contains( entryName ) ) { 103 | entries.add( entryName ); 104 | } 105 | } 106 | } 107 | 108 | if ( entries.isEmpty() ) { 109 | return; 110 | } 111 | 112 | try { 113 | 114 | final LogLevel jobLogLevel = job.getLogLevel(); 115 | final Set variablesToIgnore = Defaults.VARIABLES_TO_IGNORE; 116 | 117 | jobLog.logDetailed( "Found debug level info for job entries : " + entries.toString() ); 118 | 119 | job.addJobEntryListener( new JobEntryListener() { 120 | @Override public void beforeExecution( Job job, JobEntryCopy jobEntryCopy, JobEntryInterface jobEntryInterface ) { 121 | 122 | LogChannelInterface log = jobEntryInterface.getLogChannel(); 123 | 124 | try { 125 | // Is this a job entry with debugging set on it? 126 | // 127 | if ( entries.contains( jobEntryCopy.toString() ) ) { 128 | final JobEntryDebugLevel debugLevel = DebugLevelUtil.getJobEntryDebugLevel( entryLevelMap, jobEntryCopy.toString() ); 129 | if ( debugLevel != null ) { 130 | // Set the debug level for this one... 131 | // 132 | log.setLogLevel( debugLevel.getLogLevel() ); 133 | job.setLogLevel( debugLevel.getLogLevel() ); 134 | } 135 | } 136 | } catch ( Exception e ) { 137 | log.logError( "Error setting logging level on entry" ); 138 | } 139 | } 140 | 141 | @Override public void afterExecution( Job job, JobEntryCopy jobEntryCopy, JobEntryInterface jobEntryInterface, Result result ) { 142 | LogChannelInterface log = jobEntryInterface.getLogChannel(); 143 | 144 | try { 145 | // Is this a job entry with debugging set on it? 146 | // 147 | if ( entries.contains( jobEntryCopy.toString() ) ) { 148 | final JobEntryDebugLevel debugLevel = DebugLevelUtil.getJobEntryDebugLevel( entryLevelMap, jobEntryCopy.toString() ); 149 | if ( debugLevel != null ) { 150 | // Set the debug level for this one... 151 | // 152 | log.setLogLevel( jobLogLevel ); 153 | job.setLogLevel( jobLogLevel ); 154 | 155 | // Set the debug level back to normal... 156 | // 157 | 158 | if ( debugLevel.isLoggingResult() ) { 159 | log.logMinimal( "JOB ENTRY RESULT: " ); 160 | log.logMinimal( " - result=" + result.getResult() ); 161 | log.logMinimal( " - stopped=" + result.isStopped() ); 162 | log.logMinimal( " - linesRead=" + result.getNrLinesRead() ); 163 | log.logMinimal( " - linesWritten=" + result.getNrLinesWritten() ); 164 | log.logMinimal( " - linesInput=" + result.getNrLinesInput() ); 165 | log.logMinimal( " - linesOutput=" + result.getNrLinesOutput() ); 166 | log.logMinimal( " - linesRejected=" + result.getNrLinesRejected() ); 167 | log.logMinimal( " - result row count=" + result.getRows().size() ); 168 | log.logMinimal( " - result files count=" + result.getResultFilesList().size() ); 169 | } 170 | if ( debugLevel.isLoggingResultRows() ) { 171 | log.logMinimal( "JOB ENTRY RESULT ROWS: " ); 172 | for ( RowMetaAndData rmad : result.getRows() ) { 173 | log.logMinimal( " - " + rmad.toString() ); 174 | } 175 | } 176 | if ( debugLevel.isLoggingResultFiles() ) { 177 | log.logMinimal( "JOB ENTRY RESULT FILES: " ); 178 | for ( ResultFile resultFile : result.getResultFilesList() ) { 179 | log.logMinimal( " - " + resultFile.getFile().toString() + " from " + resultFile.getOrigin() + " : " + resultFile.getComment() + " / " + resultFile.getTypeCode() ); 180 | } 181 | } 182 | if ( debugLevel.isLoggingVariables() ) { 183 | if ( jobEntryInterface instanceof VariableSpace ) { 184 | log.logMinimal( "JOB ENTRY NOTEABLE VARIABLES: " ); 185 | 186 | VariableSpace space = (VariableSpace) jobEntryInterface; 187 | // See the variables set differently from the parent job 188 | for ( String var : space.listVariables() ) { 189 | if ( !variablesToIgnore.contains( var ) ) { 190 | String value = space.getVariable( var ); 191 | String refValue = referenceSpace.getVariable( var ); 192 | 193 | if ( refValue == null || !refValue.equals( value ) ) { 194 | // Something different! 195 | // 196 | log.logMinimal( " - " + var + "=" + value ); 197 | } 198 | } 199 | } 200 | } 201 | } 202 | } 203 | } 204 | } catch ( Exception e ) { 205 | log.logError( "Error re-setting logging level on entry" ); 206 | } 207 | } 208 | } ); 209 | } catch ( Exception e ) { 210 | jobLog.logError( "Unable to handle specific debug level for job", e ); 211 | } 212 | } 213 | } 214 | 215 | -------------------------------------------------------------------------------- /src/main/java/org/kettle/dialog/JobEntryDebugLevelDialog.java: -------------------------------------------------------------------------------- 1 | package org.kettle.dialog; 2 | 3 | import org.eclipse.swt.SWT; 4 | import org.eclipse.swt.events.SelectionAdapter; 5 | import org.eclipse.swt.events.SelectionEvent; 6 | import org.eclipse.swt.events.ShellAdapter; 7 | import org.eclipse.swt.events.ShellEvent; 8 | import org.eclipse.swt.layout.FormAttachment; 9 | import org.eclipse.swt.layout.FormData; 10 | import org.eclipse.swt.layout.FormLayout; 11 | import org.eclipse.swt.widgets.Button; 12 | import org.eclipse.swt.widgets.Combo; 13 | import org.eclipse.swt.widgets.Control; 14 | import org.eclipse.swt.widgets.Dialog; 15 | import org.eclipse.swt.widgets.Display; 16 | import org.eclipse.swt.widgets.Label; 17 | import org.eclipse.swt.widgets.Shell; 18 | import org.kettle.JobEntryDebugLevel; 19 | import org.pentaho.di.core.Const; 20 | import org.pentaho.di.core.logging.LogLevel; 21 | import org.pentaho.di.i18n.BaseMessages; 22 | import org.pentaho.di.ui.core.PropsUI; 23 | import org.pentaho.di.ui.core.gui.GUIResource; 24 | import org.pentaho.di.ui.core.gui.WindowProperty; 25 | import org.pentaho.di.ui.trans.step.BaseStepDialog; 26 | 27 | public class JobEntryDebugLevelDialog extends Dialog { 28 | private static Class PKG = JobEntryDebugLevelDialog.class; // for i18n purposes, needed by Translator2!! 29 | 30 | private JobEntryDebugLevel input; 31 | private JobEntryDebugLevel debugLevel; 32 | 33 | private Shell shell; 34 | 35 | // Connection properties 36 | // 37 | private Combo wLogLevel; 38 | private Button wLoggingResult; 39 | private Button wLoggingVariables; 40 | private Button wLoggingRows; 41 | private Button wLoggingFiles; 42 | 43 | private Control lastControl; 44 | 45 | private PropsUI props; 46 | 47 | private int middle; 48 | private int margin; 49 | 50 | private boolean ok; 51 | 52 | public JobEntryDebugLevelDialog( Shell par, JobEntryDebugLevel debugLevel) { 53 | super( par, SWT.NONE ); 54 | this.input = debugLevel; 55 | props = PropsUI.getInstance(); 56 | ok = false; 57 | 58 | this.debugLevel = input.clone(); 59 | } 60 | 61 | public boolean open() { 62 | Shell parent = getParent(); 63 | shell = new Shell( parent, SWT.DIALOG_TRIM | SWT.RESIZE | SWT.MAX | SWT.MIN ); 64 | props.setLook( shell ); 65 | shell.setImage( GUIResource.getInstance().getImageSlave() ); 66 | 67 | middle = props.getMiddlePct(); 68 | margin = Const.MARGIN + 2; 69 | 70 | FormLayout formLayout = new FormLayout(); 71 | formLayout.marginWidth = Const.FORM_MARGIN; 72 | formLayout.marginHeight = Const.FORM_MARGIN; 73 | 74 | shell.setText( "Job entry debug Level" ); 75 | shell.setLayout( formLayout ); 76 | 77 | // The name 78 | Label wlName = new Label( shell, SWT.RIGHT ); 79 | props.setLook( wlName ); 80 | wlName.setText( "Log level " ); 81 | FormData fdlName = new FormData(); 82 | fdlName.top = new FormAttachment( 0, margin ); 83 | fdlName.left = new FormAttachment( 0, 0 ); // First one in the left top corner 84 | fdlName.right = new FormAttachment( middle, -margin ); 85 | wlName.setLayoutData( fdlName ); 86 | wLogLevel = new Combo( shell, SWT.SINGLE | SWT.LEFT | SWT.BORDER ); 87 | wLogLevel.setItems( LogLevel.getLogLevelDescriptions()); 88 | props.setLook( wLogLevel ); 89 | FormData fdName = new FormData(); 90 | fdName.top = new FormAttachment( wlName, 0, SWT.CENTER ); 91 | fdName.left = new FormAttachment( middle, 0 ); // To the right of the label 92 | fdName.right = new FormAttachment( 100, 0 ); 93 | wLogLevel.setLayoutData( fdName ); 94 | lastControl = wLogLevel; 95 | 96 | // Log result details? 97 | Label wlLoggingResult = new Label( shell, SWT.RIGHT ); 98 | props.setLook( wlLoggingResult ); 99 | wlLoggingResult.setText( "Log result?"); 100 | FormData fdlLoggingResult = new FormData(); 101 | fdlLoggingResult.top = new FormAttachment( lastControl, margin ); 102 | fdlLoggingResult.left = new FormAttachment( 0, 0 ); // First one in the left top corner 103 | fdlLoggingResult.right = new FormAttachment( middle, -margin ); 104 | wlLoggingResult.setLayoutData( fdlLoggingResult ); 105 | wLoggingResult = new Button( shell, SWT.CHECK | SWT.LEFT ); 106 | props.setLook( wLoggingResult ); 107 | FormData fdLoggingResult = new FormData(); 108 | fdLoggingResult.top = new FormAttachment( wlLoggingResult, 0, SWT.CENTER ); 109 | fdLoggingResult.left = new FormAttachment( middle, 0 ); // To the right of the label 110 | fdLoggingResult.right = new FormAttachment( 100, 0 ); 111 | wLoggingResult.setLayoutData( fdLoggingResult ); 112 | lastControl = wLoggingResult; 113 | 114 | // Log result details? 115 | Label wlLoggingVariables = new Label( shell, SWT.RIGHT ); 116 | props.setLook( wlLoggingVariables ); 117 | wlLoggingVariables.setText( "Log variables?"); 118 | FormData fdlLoggingVariables = new FormData(); 119 | fdlLoggingVariables.top = new FormAttachment( lastControl, margin ); 120 | fdlLoggingVariables.left = new FormAttachment( 0, 0 ); // First one in the left top corner 121 | fdlLoggingVariables.right = new FormAttachment( middle, -margin ); 122 | wlLoggingVariables.setLayoutData( fdlLoggingVariables ); 123 | wLoggingVariables = new Button( shell, SWT.CHECK | SWT.LEFT ); 124 | props.setLook( wLoggingVariables ); 125 | FormData fdLoggingVariables = new FormData(); 126 | fdLoggingVariables.top = new FormAttachment( wlLoggingVariables, 0, SWT.CENTER ); 127 | fdLoggingVariables.left = new FormAttachment( middle, 0 ); // To the right of the label 128 | fdLoggingVariables.right = new FormAttachment( 100, 0 ); 129 | wLoggingVariables.setLayoutData( fdLoggingVariables ); 130 | lastControl = wLoggingVariables; 131 | 132 | // Log result details? 133 | Label wlLoggingRows = new Label( shell, SWT.RIGHT ); 134 | props.setLook( wlLoggingRows ); 135 | wlLoggingRows.setText( "Log result rows?"); 136 | FormData fdlLoggingRows = new FormData(); 137 | fdlLoggingRows.top = new FormAttachment( lastControl, margin ); 138 | fdlLoggingRows.left = new FormAttachment( 0, 0 ); // First one in the left top corner 139 | fdlLoggingRows.right = new FormAttachment( middle, -margin ); 140 | wlLoggingRows.setLayoutData( fdlLoggingRows ); 141 | wLoggingRows = new Button( shell, SWT.CHECK | SWT.LEFT ); 142 | props.setLook( wLoggingRows ); 143 | FormData fdLoggingRows = new FormData(); 144 | fdLoggingRows.top = new FormAttachment( wlLoggingRows, 0, SWT.CENTER ); 145 | fdLoggingRows.left = new FormAttachment( middle, 0 ); // To the right of the label 146 | fdLoggingRows.right = new FormAttachment( 100, 0 ); 147 | wLoggingRows.setLayoutData( fdLoggingRows ); 148 | lastControl = wLoggingRows; 149 | 150 | // Log result details? 151 | Label wlLoggingFiles = new Label( shell, SWT.RIGHT ); 152 | props.setLook( wlLoggingFiles ); 153 | wlLoggingFiles.setText( "Log result files?"); 154 | FormData fdlLoggingFiles = new FormData(); 155 | fdlLoggingFiles.top = new FormAttachment( lastControl, margin ); 156 | fdlLoggingFiles.left = new FormAttachment( 0, 0 ); // First one in the left top corner 157 | fdlLoggingFiles.right = new FormAttachment( middle, -margin ); 158 | wlLoggingFiles.setLayoutData( fdlLoggingFiles ); 159 | wLoggingFiles = new Button( shell, SWT.CHECK | SWT.LEFT ); 160 | props.setLook( wLoggingFiles ); 161 | FormData fdLoggingFiles = new FormData(); 162 | fdLoggingFiles.top = new FormAttachment( wlLoggingFiles, 0, SWT.CENTER ); 163 | fdLoggingFiles.left = new FormAttachment( middle, 0 ); // To the right of the label 164 | fdLoggingFiles.right = new FormAttachment( 100, 0 ); 165 | wLoggingFiles.setLayoutData( fdLoggingFiles ); 166 | lastControl = wLoggingFiles; 167 | 168 | // Buttons 169 | Button wOK = new Button( shell, SWT.PUSH ); 170 | wOK.setText( BaseMessages.getString( PKG, "System.Button.OK" ) ); 171 | wOK.addListener( SWT.Selection, e -> ok() ); 172 | 173 | Button wCancel = new Button( shell, SWT.PUSH ); 174 | wCancel.setText( BaseMessages.getString( PKG, "System.Button.Cancel" ) ); 175 | wCancel.addListener( SWT.Selection, e -> cancel() ); 176 | 177 | Button[] buttons = new Button[] { wOK, wCancel }; 178 | BaseStepDialog.positionBottomButtons( shell, buttons, margin*2, lastControl ); 179 | 180 | SelectionAdapter selAdapter = new SelectionAdapter() { 181 | public void widgetDefaultSelected( SelectionEvent e ) { 182 | ok(); 183 | } 184 | }; 185 | 186 | wLogLevel.addSelectionListener( selAdapter ); 187 | 188 | // Detect X or ALT-F4 or something that kills this window... 189 | shell.addShellListener( new ShellAdapter() { 190 | public void shellClosed( ShellEvent e ) { 191 | cancel(); 192 | } 193 | } ); 194 | 195 | getData(); 196 | 197 | BaseStepDialog.setSize( shell ); 198 | 199 | shell.open(); 200 | Display display = parent.getDisplay(); 201 | while ( !shell.isDisposed() ) { 202 | if ( !display.readAndDispatch() ) { 203 | display.sleep(); 204 | } 205 | } 206 | return ok; 207 | } 208 | 209 | public void dispose() { 210 | props.setScreen( new WindowProperty( shell ) ); 211 | shell.dispose(); 212 | } 213 | 214 | public void getData() { 215 | wLogLevel.setText( debugLevel.getLogLevel().getDescription() ); 216 | wLoggingResult.setSelection( debugLevel.isLoggingResult() ); 217 | wLoggingVariables.setSelection( debugLevel.isLoggingVariables() ); 218 | wLoggingRows.setSelection( debugLevel.isLoggingResultRows() ); 219 | wLoggingFiles.setSelection( debugLevel.isLoggingResultFiles() ); 220 | 221 | wLogLevel.setFocus(); 222 | } 223 | 224 | private void cancel() { 225 | ok = false; 226 | dispose(); 227 | } 228 | 229 | public void ok() { 230 | getInfo( input ); 231 | ok = true; 232 | dispose(); 233 | } 234 | 235 | // Get dialog info in securityService 236 | private void getInfo( JobEntryDebugLevel level ) { 237 | int index = Const.indexOfString( wLogLevel.getText(), LogLevel.getLogLevelDescriptions() ); 238 | level.setLogLevel( LogLevel.values()[index] ); 239 | level.setLoggingResult( wLoggingResult.getSelection() ); 240 | level.setLoggingVariables( wLoggingVariables.getSelection() ); 241 | level.setLoggingResultRows( wLoggingRows.getSelection() ); 242 | level.setLoggingResultFiles( wLoggingFiles.getSelection() ); 243 | } 244 | 245 | } 246 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /kettle-debug-plugin.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | --------------------------------------------------------------------------------