/*! ****************************************************************************** * * Pentaho Data Integration * * Copyright (C) 2002-2017 by Pentaho : http://www.pentaho.com * ******************************************************************************* * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ******************************************************************************/ package org.pentaho.di.core.logging; import java.util.ArrayList; import java.util.List; import org.pentaho.di.core.Const; import org.pentaho.di.core.util.Utils; import org.pentaho.di.core.KettleClientEnvironment; import org.pentaho.di.core.Result; import org.pentaho.di.core.RowMetaAndData; import org.pentaho.di.core.database.DatabaseMeta; import org.pentaho.di.core.exception.KettleException; import org.pentaho.di.core.row.RowMeta; import org.pentaho.di.core.row.RowMetaInterface; import org.pentaho.di.core.row.ValueMeta; import org.pentaho.di.core.row.ValueMetaInterface; import org.pentaho.di.core.variables.VariableSpace; import org.pentaho.di.core.xml.XMLHandler; import org.pentaho.di.i18n.BaseMessages; import org.pentaho.di.repository.RepositoryAttributeInterface; import org.pentaho.di.trans.HasDatabasesInterface; import org.pentaho.di.trans.Trans; import org.pentaho.di.trans.step.StepMeta; import org.w3c.dom.Node; /** * This class describes a transformation logging table * * @author matt * */ public class TransLogTable extends BaseLogTable implements Cloneable, LogTableInterface { private static Class<?> PKG = TransLogTable.class; // for i18n purposes, needed by Translator2!! public static final String XML_TAG = "trans-log-table"; /** The client executing the transformation */ // private String client; public enum ID { ID_BATCH( "ID_BATCH" ), CHANNEL_ID( "CHANNEL_ID" ), TRANSNAME( "TRANSNAME" ), STATUS( "STATUS" ), LINES_READ( "LINES_READ" ), LINES_WRITTEN( "LINES_WRITTEN" ), LINES_UPDATED( "LINES_UPDATED" ), LINES_INPUT( "LINES_INPUT" ), LINES_OUTPUT( "LINES_OUTPUT" ), LINES_REJECTED( "LINES_REJECTED" ), ERRORS( "ERRORS" ), STARTDATE( "STARTDATE" ), ENDDATE( "ENDDATE" ), LOGDATE( "LOGDATE" ), DEPDATE( "DEPDATE" ), REPLAYDATE( "REPLAYDATE" ), LOG_FIELD( "LOG_FIELD" ), EXECUTING_SERVER( "EXECUTING_SERVER" ), EXECUTING_USER( "EXECUTING_USER" ), CLIENT( "CLIENT" ); private String id; private ID( String id ) { this.id = id; } public String toString() { return id; } } private String logInterval; private String logSizeLimit; private List<StepMeta> steps; public TransLogTable( VariableSpace space, HasDatabasesInterface databasesInterface, List<StepMeta> steps ) { super( space, databasesInterface, null, null, null ); this.steps = steps; } @Override public Object clone() { try { TransLogTable table = (TransLogTable) super.clone(); table.fields = new ArrayList<LogTableField>(); for ( LogTableField field : this.fields ) { table.fields.add( (LogTableField) field.clone() ); } return table; } catch ( CloneNotSupportedException e ) { return null; } } public String getXML() { StringBuilder retval = new StringBuilder(); retval.append( " " ).append( XMLHandler.openTag( XML_TAG ) ).append( Const.CR ); retval.append( " " ).append( XMLHandler.addTagValue( "connection", connectionName ) ); retval.append( " " ).append( XMLHandler.addTagValue( "schema", schemaName ) ); retval.append( " " ).append( XMLHandler.addTagValue( "table", tableName ) ); retval.append( " " ).append( XMLHandler.addTagValue( "size_limit_lines", logSizeLimit ) ); retval.append( " " ).append( XMLHandler.addTagValue( "interval", logInterval ) ); retval.append( " " ).append( XMLHandler.addTagValue( "timeout_days", timeoutInDays ) ); retval.append( super.getFieldsXML() ); retval.append( " " ).append( XMLHandler.closeTag( XML_TAG ) ).append( Const.CR ); return retval.toString(); } public void loadXML( Node node, List<DatabaseMeta> databases, List<StepMeta> steps ) { connectionName = XMLHandler.getTagValue( node, "connection" ); schemaName = XMLHandler.getTagValue( node, "schema" ); tableName = XMLHandler.getTagValue( node, "table" ); logSizeLimit = XMLHandler.getTagValue( node, "size_limit_lines" ); logInterval = XMLHandler.getTagValue( node, "interval" ); timeoutInDays = XMLHandler.getTagValue( node, "timeout_days" ); int nr = XMLHandler.countNodes( node, BaseLogTable.XML_TAG ); for ( int i = 0; i < nr; i++ ) { Node fieldNode = XMLHandler.getSubNodeByNr( node, BaseLogTable.XML_TAG, i ); String id = XMLHandler.getTagValue( fieldNode, "id" ); LogTableField field = findField( id ); if ( field == null ) { field = fields.get( i ); } if ( field != null ) { field.setFieldName( XMLHandler.getTagValue( fieldNode, "name" ) ); field.setEnabled( "Y".equalsIgnoreCase( XMLHandler.getTagValue( fieldNode, "enabled" ) ) ); field.setSubject( StepMeta.findStep( steps, XMLHandler.getTagValue( fieldNode, "subject" ) ) ); } } } public void saveToRepository( RepositoryAttributeInterface attributeInterface ) throws KettleException { super.saveToRepository( attributeInterface ); // Also save the log interval and log size limit // attributeInterface.setAttribute( getLogTableCode() + PROP_LOG_TABLE_INTERVAL, logInterval ); attributeInterface.setAttribute( getLogTableCode() + PROP_LOG_TABLE_SIZE_LIMIT, logSizeLimit ); } public void loadFromRepository( RepositoryAttributeInterface attributeInterface ) throws KettleException { super.loadFromRepository( attributeInterface ); logInterval = attributeInterface.getAttributeString( getLogTableCode() + PROP_LOG_TABLE_INTERVAL ); logSizeLimit = attributeInterface.getAttributeString( getLogTableCode() + PROP_LOG_TABLE_SIZE_LIMIT ); for ( int i = 0; i < getFields().size(); i++ ) { String id = attributeInterface.getAttributeString( getLogTableCode() + PROP_LOG_TABLE_FIELD_ID + i ); // Only read further if the ID is available. // For backward compatibility, this might not be provided yet! // if ( id != null ) { LogTableField field = findField( id ); if ( field.isSubjectAllowed() ) { // BaseLogTable.loadFromRepository sets subject as a String // String stepname = (String) field.getSubject(); if ( !Utils.isEmpty( stepname ) ) { field.setSubject( StepMeta.findStep( steps, stepname ) ); } else { field.setSubject( null ); } } } } } @Override public void replaceMeta( LogTableCoreInterface logTableInterface ) { if ( !( logTableInterface instanceof TransLogTable ) ) { return; } TransLogTable logTable = (TransLogTable) logTableInterface; super.replaceMeta( logTable ); } //CHECKSTYLE:LineLength:OFF public static TransLogTable getDefault( VariableSpace space, HasDatabasesInterface databasesInterface, List<StepMeta> steps ) { TransLogTable table = new TransLogTable( space, databasesInterface, steps ); table.fields.add( new LogTableField( ID.ID_BATCH.id, true, false, "ID_BATCH", BaseMessages.getString( PKG, "TransLogTable.FieldName.BatchID" ), BaseMessages.getString( PKG, "TransLogTable.FieldDescription.BatchID" ), ValueMetaInterface.TYPE_INTEGER, 8 ) ); table.fields.add( new LogTableField( ID.CHANNEL_ID.id, true, false, "CHANNEL_ID", BaseMessages.getString( PKG, "TransLogTable.FieldName.ChannelID" ), BaseMessages.getString( PKG, "TransLogTable.FieldDescription.ChannelID" ), ValueMetaInterface.TYPE_STRING, 255 ) ); table.fields.add( new LogTableField( ID.TRANSNAME.id, true, false, "TRANSNAME", BaseMessages.getString( PKG, "TransLogTable.FieldName.TransName" ), BaseMessages.getString( PKG, "TransLogTable.FieldDescription.TransName" ), ValueMetaInterface.TYPE_STRING, 255 ) ); table.fields.add( new LogTableField( ID.STATUS.id, true, false, "STATUS", BaseMessages.getString( PKG, "TransLogTable.FieldName.Status" ), BaseMessages.getString( PKG, "TransLogTable.FieldDescription.Status" ), ValueMetaInterface.TYPE_STRING, 15 ) ); table.fields.add( new LogTableField( ID.LINES_READ.id, true, true, "LINES_READ", BaseMessages.getString( PKG, "TransLogTable.FieldName.LinesRead" ), BaseMessages.getString( PKG, "TransLogTable.FieldDescription.LinesRead" ), ValueMetaInterface.TYPE_INTEGER, 18 ) ); table.fields.add( new LogTableField( ID.LINES_WRITTEN.id, true, true, "LINES_WRITTEN", BaseMessages.getString( PKG, "TransLogTable.FieldName.LinesWritten" ), BaseMessages.getString( PKG, "TransLogTable.FieldDescription.LinesWritten" ), ValueMetaInterface.TYPE_INTEGER, 18 ) ); table.fields.add( new LogTableField( ID.LINES_UPDATED.id, true, true, "LINES_UPDATED", BaseMessages.getString( PKG, "TransLogTable.FieldName.LinesUpdated" ), BaseMessages.getString( PKG, "TransLogTable.FieldDescription.LinesUpdated" ), ValueMetaInterface.TYPE_INTEGER, 18 ) ); table.fields.add( new LogTableField( ID.LINES_INPUT.id, true, true, "LINES_INPUT", BaseMessages.getString( PKG, "TransLogTable.FieldName.LinesInput" ), BaseMessages.getString( PKG, "TransLogTable.FieldDescription.LinesInput" ), ValueMetaInterface.TYPE_INTEGER, 18 ) ); table.fields.add( new LogTableField( ID.LINES_OUTPUT.id, true, true, "LINES_OUTPUT", BaseMessages.getString( PKG, "TransLogTable.FieldName.LinesOutput" ), BaseMessages.getString( PKG, "TransLogTable.FieldDescription.LinesOutput" ), ValueMetaInterface.TYPE_INTEGER, 18 ) ); table.fields.add( new LogTableField( ID.LINES_REJECTED.id, true, true, "LINES_REJECTED", BaseMessages.getString( PKG, "TransLogTable.FieldName.LinesRejected" ), BaseMessages.getString( PKG, "TransLogTable.FieldDescription.LinesRejected" ), ValueMetaInterface.TYPE_INTEGER, 18 ) ); table.fields.add( new LogTableField( ID.ERRORS.id, true, false, "ERRORS", BaseMessages.getString( PKG, "TransLogTable.FieldName.Errors" ), BaseMessages.getString( PKG, "TransLogTable.FieldDescription.Errors" ), ValueMetaInterface.TYPE_INTEGER, 18 ) ); table.fields.add( new LogTableField( ID.STARTDATE.id, true, false, "STARTDATE", BaseMessages.getString( PKG, "TransLogTable.FieldName.StartDateRange" ), BaseMessages.getString( PKG, "TransLogTable.FieldDescription.StartDateRange" ), ValueMetaInterface.TYPE_DATE, -1 ) ); table.fields.add( new LogTableField( ID.ENDDATE.id, true, false, "ENDDATE", BaseMessages.getString( PKG, "TransLogTable.FieldName.EndDateRange" ), BaseMessages.getString( PKG, "TransLogTable.FieldDescription.EndDateRange" ), ValueMetaInterface.TYPE_DATE, -1 ) ); table.fields.add( new LogTableField( ID.LOGDATE.id, true, false, "LOGDATE", BaseMessages.getString( PKG, "TransLogTable.FieldName.LogDate" ), BaseMessages.getString( PKG, "TransLogTable.FieldDescription.LogDate" ), ValueMetaInterface.TYPE_DATE, -1 ) ); table.fields.add( new LogTableField( ID.DEPDATE.id, true, false, "DEPDATE", BaseMessages.getString( PKG, "TransLogTable.FieldName.DepDate" ), BaseMessages.getString( PKG, "TransLogTable.FieldDescription.DepDate" ), ValueMetaInterface.TYPE_DATE, -1 ) ); table.fields.add( new LogTableField( ID.REPLAYDATE.id, true, false, "REPLAYDATE", BaseMessages.getString( PKG, "TransLogTable.FieldName.ReplayDate" ), BaseMessages.getString( PKG, "TransLogTable.FieldDescription.ReplayDate" ), ValueMetaInterface.TYPE_DATE, -1 ) ); table.fields.add( new LogTableField( ID.LOG_FIELD.id, true, false, "LOG_FIELD", BaseMessages.getString( PKG, "TransLogTable.FieldName.LogField" ), BaseMessages.getString( PKG, "TransLogTable.FieldDescription.LogField" ), ValueMetaInterface.TYPE_STRING, DatabaseMeta.CLOB_LENGTH ) ); table.fields.add( new LogTableField( ID.EXECUTING_SERVER.id, false, false, "EXECUTING_SERVER", BaseMessages.getString( PKG, "TransLogTable.FieldName.ExecutingServer" ), BaseMessages.getString( PKG, "TransLogTable.FieldDescription.ExecutingServer" ), ValueMetaInterface.TYPE_STRING, 255 ) ); table.fields.add( new LogTableField( ID.EXECUTING_USER.id, false, false, "EXECUTING_USER", BaseMessages.getString( PKG, "TransLogTable.FieldName.ExecutingUser" ), BaseMessages.getString( PKG, "TransLogTable.FieldDescription.ExecutingUser" ), ValueMetaInterface.TYPE_STRING, 255 ) ); table.fields.add( new LogTableField( ID.CLIENT.id, false, false, "CLIENT", BaseMessages.getString( PKG, "TransLogTable.FieldName.Client" ), BaseMessages.getString( PKG, "TransLogTable.FieldDescription.Client" ), ValueMetaInterface.TYPE_STRING, 255 ) ); table.findField( ID.ID_BATCH ).setKey( true ); table.findField( ID.LOGDATE ).setLogDateField( true ); table.findField( ID.LOG_FIELD ).setLogField( true ); table.findField( ID.CHANNEL_ID ).setVisible( false ); table.findField( ID.TRANSNAME ).setVisible( false ); table.findField( ID.STATUS ).setStatusField( true ); table.findField( ID.ERRORS ).setErrorsField( true ); table.findField( ID.TRANSNAME ).setNameField( true ); return table; } public LogTableField findField( ID id ) { return super.findField( id.id ); } public Object getSubject( ID id ) { return super.getSubject( id.id ); } public String getSubjectString( ID id ) { return super.getSubjectString( id.id ); } public void setBatchIdUsed( boolean use ) { findField( ID.ID_BATCH ).setEnabled( use ); } public boolean isBatchIdUsed() { return findField( ID.ID_BATCH ).isEnabled(); } public void setLogFieldUsed( boolean use ) { findField( ID.LOG_FIELD ).setEnabled( use ); } public boolean isLogFieldUsed() { return findField( ID.LOG_FIELD ).isEnabled(); } public String getStepnameRead() { return getSubjectString( ID.LINES_READ ); } public void setStepRead( StepMeta read ) { findField( ID.LINES_READ ).setSubject( read ); } public String getStepnameWritten() { return getSubjectString( ID.LINES_WRITTEN ); } public void setStepWritten( StepMeta written ) { findField( ID.LINES_WRITTEN ).setSubject( written ); } public String getStepnameInput() { return getSubjectString( ID.LINES_INPUT ); } public void setStepInput( StepMeta input ) { findField( ID.LINES_INPUT ).setSubject( input ); } public String getStepnameOutput() { return getSubjectString( ID.LINES_OUTPUT ); } public void setStepOutput( StepMeta output ) { findField( ID.LINES_OUTPUT ).setSubject( output ); } public String getStepnameUpdated() { return getSubjectString( ID.LINES_UPDATED ); } public void setStepUpdate( StepMeta update ) { findField( ID.LINES_UPDATED ).setSubject( update ); } public String getStepnameRejected() { return getSubjectString( ID.LINES_REJECTED ); } public void setStepRejected( StepMeta rejected ) { findField( ID.LINES_REJECTED ).setSubject( rejected ); } /** * Sets the logging interval in seconds. Disabled if the logging interval is <=0. * * @param logInterval * The log interval value. A value higher than 0 means that the log table is updated every 'logInterval' * seconds. */ public void setLogInterval( String logInterval ) { this.logInterval = logInterval; } /** * Get the logging interval in seconds. Disabled if the logging interval is <=0. A value higher than 0 means that the * log table is updated every 'logInterval' seconds. * * @param logInterval * The log interval, */ public String getLogInterval() { return logInterval; } /** * @return the logSizeLimit */ public String getLogSizeLimit() { return logSizeLimit; } /** * @param logSizeLimit * the logSizeLimit to set */ public void setLogSizeLimit( String logSizeLimit ) { this.logSizeLimit = logSizeLimit; } /** * This method calculates all the values that are required * * @param id * the id to use or -1 if no id is needed * @param status * the log status to use * @param subject * the subject to query, in this case a Trans object */ public RowMetaAndData getLogRecord( LogStatus status, Object subject, Object parent ) { if ( subject == null || subject instanceof Trans ) { Trans trans = (Trans) subject; Result result = null; if ( trans != null ) { result = trans.getResult(); } RowMetaAndData row = new RowMetaAndData(); for ( LogTableField field : fields ) { if ( field.isEnabled() ) { Object value = null; if ( trans != null ) { switch ( ID.valueOf( field.getId() ) ) { case ID_BATCH: value = new Long( trans.getBatchId() ); break; case CHANNEL_ID: value = trans.getLogChannelId(); break; case TRANSNAME: value = trans.getName(); break; case STATUS: value = status.getStatus(); break; case LINES_READ: value = new Long( result.getNrLinesRead() ); break; case LINES_WRITTEN: value = new Long( result.getNrLinesWritten() ); break; case LINES_INPUT: value = new Long( result.getNrLinesInput() ); break; case LINES_OUTPUT: value = new Long( result.getNrLinesOutput() ); break; case LINES_UPDATED: value = new Long( result.getNrLinesUpdated() ); break; case LINES_REJECTED: value = new Long( result.getNrLinesRejected() ); break; case ERRORS: value = new Long( result.getNrErrors() ); break; case STARTDATE: value = trans.getStartDate(); break; case LOGDATE: value = trans.getLogDate(); break; case ENDDATE: value = trans.getEndDate(); break; case DEPDATE: value = trans.getDepDate(); break; case REPLAYDATE: value = trans.getCurrentDate(); break; case LOG_FIELD: value = getLogBuffer( trans, trans.getLogChannelId(), status, logSizeLimit ); break; case EXECUTING_SERVER: value = trans.getExecutingServer(); break; case EXECUTING_USER: value = trans.getExecutingUser(); break; case CLIENT: value = KettleClientEnvironment.getInstance().getClient() != null ? KettleClientEnvironment .getInstance().getClient().toString() : "unknown"; break; default: break; } } row.addValue( field.getFieldName(), field.getDataType(), value ); row.getRowMeta().getValueMeta( row.size() - 1 ).setLength( field.getLength() ); } } return row; } else { return null; } } public String getLogTableCode() { return "TRANS"; } public String getLogTableType() { return BaseMessages.getString( PKG, "TransLogTable.Type.Description" ); } public String getConnectionNameVariable() { return Const.KETTLE_TRANS_LOG_DB; } public String getSchemaNameVariable() { return Const.KETTLE_TRANS_LOG_SCHEMA; } public String getTableNameVariable() { return Const.KETTLE_TRANS_LOG_TABLE; } public List<RowMetaInterface> getRecommendedIndexes() { List<RowMetaInterface> indexes = new ArrayList<RowMetaInterface>(); // First index : ID_BATCH if any is used. // if ( isBatchIdUsed() ) { RowMetaInterface batchIndex = new RowMeta(); LogTableField keyField = getKeyField(); ValueMetaInterface keyMeta = new ValueMeta( keyField.getFieldName(), keyField.getDataType() ); keyMeta.setLength( keyField.getLength() ); batchIndex.addValueMeta( keyMeta ); indexes.add( batchIndex ); } // The next index includes : ERRORS, STATUS, TRANSNAME: RowMetaInterface lookupIndex = new RowMeta(); LogTableField errorsField = findField( ID.ERRORS ); if ( errorsField != null ) { ValueMetaInterface valueMeta = new ValueMeta( errorsField.getFieldName(), errorsField.getDataType() ); valueMeta.setLength( errorsField.getLength() ); lookupIndex.addValueMeta( valueMeta ); } LogTableField statusField = findField( ID.STATUS ); if ( statusField != null ) { ValueMetaInterface valueMeta = new ValueMeta( statusField.getFieldName(), statusField.getDataType() ); valueMeta.setLength( statusField.getLength() ); lookupIndex.addValueMeta( valueMeta ); } LogTableField transNameField = findField( ID.TRANSNAME ); if ( transNameField != null ) { ValueMetaInterface valueMeta = new ValueMeta( transNameField.getFieldName(), transNameField.getDataType() ); valueMeta.setLength( transNameField.getLength() ); lookupIndex.addValueMeta( valueMeta ); } indexes.add( lookupIndex ); return indexes; } @Override public void setAllGlobalParametersToNull() { boolean clearGlobalVariables = Boolean.valueOf( System.getProperties().getProperty( Const.KETTLE_GLOBAL_LOG_VARIABLES_CLEAR_ON_EXPORT, "false" ) ); if ( clearGlobalVariables ) { super.setAllGlobalParametersToNull(); logInterval = isGlobalParameter( logInterval ) ? null : logInterval; logSizeLimit = isGlobalParameter( logSizeLimit ) ? null : logSizeLimit; } } }