/******************************************************************************* * * Pentaho Data Integration * * Copyright (C) 2002-2015 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 org.pentaho.di.core.Const; import java.util.Collections; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.LinkedList; /** * This class keeps the last N lines in a buffer * * @author matt * */ public class LoggingBuffer { private String name; private List<BufferLine> buffer; private int bufferSize; private KettleLogLayout layout; private List<KettleLoggingEventListener> eventListeners; public LoggingBuffer( int bufferSize ) { this.bufferSize = bufferSize; buffer = Collections.synchronizedList( new LinkedList<BufferLine>() ); layout = new KettleLogLayout( true ); eventListeners = Collections.synchronizedList( new ArrayList<KettleLoggingEventListener>() ); } /** * @return the number (sequence, 1..N) of the last log line. If no records are present in the buffer, 0 is returned. */ public int getLastBufferLineNr() { synchronized ( buffer ) { if ( buffer.size() > 0 ) { return buffer.get( buffer.size() - 1 ).getNr(); } else { return 0; } } } /** * * @param channelId * channel IDs to grab * @param includeGeneral * include general log lines * @param from * @param to * @return */ public List<KettleLoggingEvent> getLogBufferFromTo( List<String> channelId, boolean includeGeneral, int from, int to ) { List<KettleLoggingEvent> lines = new ArrayList<KettleLoggingEvent>(); synchronized ( buffer ) { for ( BufferLine line : buffer ) { if ( line.getNr() > from && line.getNr() <= to ) { Object payload = line.getEvent().getMessage(); if ( payload instanceof LogMessage ) { LogMessage message = (LogMessage) payload; // Typically, the log channel id is the one from the transformation or job running currently. // However, we also want to see the details of the steps etc. // So we need to look at the parents all the way up if needed... // boolean include = channelId == null; // See if we should include generic messages // if ( !include ) { LoggingObjectInterface loggingObject = LoggingRegistry.getInstance().getLoggingObject( message.getLogChannelId() ); if ( loggingObject != null && includeGeneral && LoggingObjectType.GENERAL.equals( loggingObject.getObjectType() ) ) { include = true; } // See if we should include a certain channel id (zero, one or more) // if ( !include ) { for ( String id : channelId ) { if ( message.getLogChannelId().equals( id ) ) { include = true; break; } } } } if ( include ) { try { // String string = layout.format(line.getEvent()); lines.add( line.getEvent() ); } catch ( Exception e ) { e.printStackTrace(); } } } } } } return lines; } /** * * @param parentLogChannelId * the parent log channel ID to grab * @param includeGeneral * include general log lines * @param from * @param to * @return */ public List<KettleLoggingEvent> getLogBufferFromTo( String parentLogChannelId, boolean includeGeneral, int from, int to ) { // Typically, the log channel id is the one from the transformation or job running currently. // However, we also want to see the details of the steps etc. // So we need to look at the parents all the way up if needed... // List<String> childIds = LoggingRegistry.getInstance().getLogChannelChildren( parentLogChannelId ); return getLogBufferFromTo( childIds, includeGeneral, from, to ); } public StringBuffer getBuffer( String parentLogChannelId, boolean includeGeneral, int startLineNr, int endLineNr ) { StringBuffer stringBuffer = new StringBuffer( 10000 ); List<KettleLoggingEvent> events = getLogBufferFromTo( parentLogChannelId, includeGeneral, startLineNr, endLineNr ); for ( KettleLoggingEvent event : events ) { stringBuffer.append( layout.format( event ) ).append( Const.CR ); } return stringBuffer; } public StringBuffer getBuffer( String parentLogChannelId, boolean includeGeneral ) { return getBuffer( parentLogChannelId, includeGeneral, 0 ); } public StringBuffer getBuffer( String parentLogChannelId, boolean includeGeneral, int startLineNr ) { return getBuffer( parentLogChannelId, includeGeneral, startLineNr, getLastBufferLineNr() ); } public StringBuffer getBuffer() { return getBuffer( null, true ); } public void close() { } public void doAppend( KettleLoggingEvent event ) { synchronized ( buffer ) { buffer.add( new BufferLine( event ) ); while ( bufferSize > 0 && buffer.size() > bufferSize ) { buffer.remove( 0 ); } } } public void setName( String name ) { this.name = name; } public String getName() { return name; } public void setLayout( KettleLogLayout layout ) { this.layout = layout; } public KettleLogLayout getLayout() { return layout; } public boolean requiresLayout() { return true; } public void clear() { buffer.clear(); } /** * @return the maximum number of lines that this buffer contains, 0 or lower means: no limit */ public int getMaxNrLines() { return bufferSize; } /** * @param maxNrLines * the maximum number of lines that this buffer should contain, 0 or lower means: no limit */ public void setMaxNrLines( int maxNrLines ) { this.bufferSize = maxNrLines; } /** * @return the nrLines */ public int getNrLines() { return buffer.size(); } /** * Removes all rows for the channel with the specified id * * @param id * the id of the logging channel to remove */ public void removeChannelFromBuffer( String id ) { synchronized ( buffer ) { Iterator<BufferLine> iterator = buffer.iterator(); while ( iterator.hasNext() ) { BufferLine bufferLine = iterator.next(); Object payload = bufferLine.getEvent().getMessage(); if ( payload instanceof LogMessage ) { LogMessage message = (LogMessage) payload; if ( id.equals( message.getLogChannelId() ) ) { iterator.remove(); } } } } } public int size() { return buffer.size(); } public void removeGeneralMessages() { synchronized ( buffer ) { Iterator<BufferLine> iterator = buffer.iterator(); while ( iterator.hasNext() ) { BufferLine bufferLine = iterator.next(); Object payload = bufferLine.getEvent().getMessage(); if ( payload instanceof LogMessage ) { LogMessage message = (LogMessage) payload; LoggingObjectInterface loggingObject = LoggingRegistry.getInstance().getLoggingObject( message.getLogChannelId() ); if ( loggingObject != null && LoggingObjectType.GENERAL.equals( loggingObject.getObjectType() ) ) { iterator.remove(); } } } } } public Iterator<BufferLine> getBufferIterator() { return buffer.iterator(); } /** * It looks like this method is not used in the project. */ @Deprecated public String dump() { StringBuilder buf = new StringBuilder( 50000 ); synchronized ( buffer ) { for ( BufferLine line : buffer ) { Object payload = line.getEvent().getMessage(); if ( payload instanceof LogMessage ) { LogMessage message = (LogMessage) payload; // LoggingObjectInterface loggingObject = // LoggingRegistry.getInstance().getLoggingObject(message.getLogChannelId()); buf .append( message.getLogChannelId() + "\t" + message.getSubject() + "\t" + message.getMessage() + "\n" ); } } } return buf.toString(); } public void removeBufferLines( List<BufferLine> linesToRemove ) { buffer.removeAll( linesToRemove ); } public List<BufferLine> getBufferLinesBefore( long minTimeBoundary ) { List<BufferLine> linesToRemove = new ArrayList<BufferLine>(); synchronized ( buffer ) { for ( Iterator<BufferLine> i = buffer.iterator(); i.hasNext(); ) { BufferLine bufferLine = i.next(); if ( bufferLine.getEvent().timeStamp < minTimeBoundary ) { linesToRemove.add( bufferLine ); } else { break; } } } return linesToRemove; } public void addLogggingEvent( KettleLoggingEvent loggingEvent ) { doAppend( loggingEvent ); synchronized ( eventListeners ) { for ( KettleLoggingEventListener listener : eventListeners ) { listener.eventAdded( loggingEvent ); } } } public void addLoggingEventListener( KettleLoggingEventListener listener ) { eventListeners.add( listener ); } public void removeLoggingEventListener( KettleLoggingEventListener listener ) { eventListeners.remove( listener ); } }