<commit_before>/*******************************************************************************
 *
 * Pentaho Data Integration
 *
 * Copyright (C) 2002-2012 by Pentaho : http://www.pentaho.com
 *
 *******************************************************************************
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ******************************************************************************/

package org.pentaho.di.core.logging;

import java.io.PrintStream;
import java.util.Date;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicBoolean;

import org.pentaho.di.core.Const;
import org.pentaho.di.core.util.EnvUtil;

public class CentralLogStore {
  
  public static final PrintStream OriginalSystemOut = System.out;
  public static final PrintStream OriginalSystemErr = System.err;
  
	private static CentralLogStore store;
	
	private LoggingBuffer appender;

	private Timer logCleanerTimer;
	
	private static AtomicBoolean initialized = new AtomicBoolean(false); 
		
	/**
	 * Create the central log store with optional limitation to the size
	 * 
	 * @param maxSize the maximum size
	 * @param maxLogTimeoutMinutes The maximum time that a log line times out in Minutes.
	 */
	private CentralLogStore(int maxSize, int maxLogTimeoutMinutes) {
		this.appender = new LoggingBuffer(maxSize);
		replaceLogCleaner(maxLogTimeoutMinutes);
		
		System.setOut(new LoggingPrintStream(OriginalSystemOut));
		System.setErr(new LoggingPrintStream(OriginalSystemErr));
	}
	
  public void replaceLogCleaner(final int maxLogTimeoutMinutes) {
    if (logCleanerTimer!=null) {
      logCleanerTimer.cancel();
    }
    logCleanerTimer = new Timer(true);
    
    TimerTask timerTask = new TimerTask() {
      public void run() {

        if (maxLogTimeoutMinutes>0) {
          long minTimeBoundary = new Date().getTime() - maxLogTimeoutMinutes*60*1000;

          // Get the old lines to be removed
          //
          List<BufferLine> linesToRemove = appender.getBufferLinesBefore(minTimeBoundary);
          
          // Remove all lines at once to prevent concurrent modification problems.
          //
          appender.removeBufferLines(linesToRemove);
        }
      }
    };

    // Clean out the rows every 10 seconds to get a nice steady purge operation...
    //
    logCleanerTimer.schedule(timerTask, 10000, 10000);

  }
  
	/**
	 * Initialize the central log store with optional limitation to the size
	 * 
	 * @param maxSize the maximum size
	 * @param maxLogTimeoutHours The maximum time that a log line times out in hours.
	 */
	public static void init(int maxSize, int maxLogTimeoutMinutes) {
		if (maxSize>0 || maxLogTimeoutMinutes>0) {
		  init0(maxSize, maxLogTimeoutMinutes);
		} else {
			init();
		}
	}
	
	public static void init() {
		int maxSize = Const.toInt(EnvUtil.getSystemProperty(Const.KETTLE_MAX_LOG_SIZE_IN_LINES), 5000); 
		int maxLogTimeoutMinutes = Const.toInt(EnvUtil.getSystemProperty(Const.KETTLE_MAX_LOG_TIMEOUT_IN_MINUTES), 1440); 
		init0(maxSize, maxLogTimeoutMinutes);
	}

	/**
	 * Initialize the central log store.  If it has already been initialized the configuration
	 * will be updated.
	 * 
	 * @param maxSize the maximum size of the log buffer
	 * @param maxLogTimeoutMinutes The maximum time that a log line times out in minutes
	 */
	private synchronized static void init0(int maxSize, int maxLogTimeoutMinutes) {
    if (store != null) {
      // CentralLogStore already initialized.  Just update the values.
      store.appender.setMaxNrLines(maxSize);
      store.replaceLogCleaner(maxLogTimeoutMinutes);
    } else  {
      store = new CentralLogStore(maxSize, maxLogTimeoutMinutes);
    }
    initialized.set(true);
	}
	
	private static CentralLogStore getInstance() {
		if (store==null) {
			throw new RuntimeException("Central Log Store is not initialized!!!");
		}
		return store;
	}
	
    /**
     * @return the number (sequence, 1..N) of the last log line.
     * If no records are present in the buffer, 0 is returned.
     */
    public static int getLastBufferLineNr() {
    	return getInstance().appender.getLastBufferLineNr();
    }
    
    /**
     * 
     * Get all the log lines pertaining to the specified parent log channel id (including all children)
     * 
     * @param parentLogChannelId the parent log channel ID to grab
     * @param includeGeneral include general log lines
     * @param from
     * @param to
     * @return the log lines found
     */<commit_msg>PDI-9419 - Overloading CentralLogStore.init to optionally take arguments specifying whether to redirect stdout and stderr<commit_after>/*******************************************************************************
 *
 * Pentaho Data Integration
 *
 * Copyright (C) 2002-2012 by Pentaho : http://www.pentaho.com
 *
 *******************************************************************************
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ******************************************************************************/

package org.pentaho.di.core.logging;

import java.io.PrintStream;
import java.util.Date;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicBoolean;

import org.pentaho.di.core.Const;
import org.pentaho.di.core.util.EnvUtil;

public class CentralLogStore {
  
  public static final PrintStream OriginalSystemOut = System.out;
  public static final PrintStream OriginalSystemErr = System.err;
  
	private static CentralLogStore store;
	
	private LoggingBuffer appender;

	private Timer logCleanerTimer;
	
	private static AtomicBoolean initialized = new AtomicBoolean(false); 
		
	/**
	 * Create the central log store with optional limitation to the size
	 * 
	 * @param maxSize the maximum size
	 * @param maxLogTimeoutMinutes The maximum time that a log line times out in Minutes.
	 */
	private CentralLogStore(int maxSize, int maxLogTimeoutMinutes, boolean redirectStdOut, boolean redirectStdErr) {
		this.appender = new LoggingBuffer(maxSize);
		replaceLogCleaner(maxLogTimeoutMinutes);
		
		if (redirectStdOut) {
		  System.setOut(new LoggingPrintStream(OriginalSystemOut));
		}
		
		if (redirectStdErr) {
	  	System.setErr(new LoggingPrintStream(OriginalSystemErr));
		}
	}
	
  public void replaceLogCleaner(final int maxLogTimeoutMinutes) {
    if (logCleanerTimer!=null) {
      logCleanerTimer.cancel();
    }
    logCleanerTimer = new Timer(true);
    
    TimerTask timerTask = new TimerTask() {
      public void run() {

        if (maxLogTimeoutMinutes>0) {
          long minTimeBoundary = new Date().getTime() - maxLogTimeoutMinutes*60*1000;

          // Get the old lines to be removed
          //
          List<BufferLine> linesToRemove = appender.getBufferLinesBefore(minTimeBoundary);
          
          // Remove all lines at once to prevent concurrent modification problems.
          //
          appender.removeBufferLines(linesToRemove);
        }
      }
    };

    // Clean out the rows every 10 seconds to get a nice steady purge operation...
    //
    logCleanerTimer.schedule(timerTask, 10000, 10000);

  }
  
	/**
	 * Initialize the central log store with optional limitation to the size
	 * 
	 * @param maxSize the maximum size
	 * @param maxLogTimeoutHours The maximum time that a log line times out in hours.
	 */
	public static void init(int maxSize, int maxLogTimeoutMinutes) {
		if (maxSize>0 || maxLogTimeoutMinutes>0) {
		  init0(maxSize, maxLogTimeoutMinutes, true, true);
		} else {
			init();
		}
	}
	
	public static void init() {
	  init(true, true);
	}
	
	public static void init(boolean redirectStdOut, boolean redirectStdErr) {
		int maxSize = Const.toInt(EnvUtil.getSystemProperty(Const.KETTLE_MAX_LOG_SIZE_IN_LINES), 5000); 
		int maxLogTimeoutMinutes = Const.toInt(EnvUtil.getSystemProperty(Const.KETTLE_MAX_LOG_TIMEOUT_IN_MINUTES), 1440); 
		init0(maxSize, maxLogTimeoutMinutes, redirectStdOut, redirectStdErr);
	}

	/**
	 * Initialize the central log store.  If it has already been initialized the configuration
	 * will be updated.
	 * 
	 * @param maxSize the maximum size of the log buffer
	 * @param maxLogTimeoutMinutes The maximum time that a log line times out in minutes
	 */
	private synchronized static void init0(int maxSize, int maxLogTimeoutMinutes, boolean redirectStdOut, boolean redirectStdErr) {
    if (store != null) {
      // CentralLogStore already initialized.  Just update the values.
      store.appender.setMaxNrLines(maxSize);
      store.replaceLogCleaner(maxLogTimeoutMinutes);
    } else  {
      store = new CentralLogStore(maxSize, maxLogTimeoutMinutes, redirectStdOut, redirectStdErr);
    }
    initialized.set(true);
	}
	
	private static CentralLogStore getInstance() {
		if (store==null) {
			throw new RuntimeException("Central Log Store is not initialized!!!");
		}
		return store;
	}
	
    /**
     * @return the number (sequence, 1..N) of the last log line.
     * If no records are present in the buffer, 0 is returned.
     */
    public static int getLastBufferLineNr() {
    	return getInstance().appender.getLastBufferLineNr();
    }
    
    /**
     * 
     * Get all the log lines pertaining to the specified parent log channel id (including all children)
     * 
     * @param parentLogChannelId the parent log channel ID to grab
     * @param includeGeneral include general log lines
     * @param from
     * @param to
     * @return the log lines found
     */
{'fraction_non_alphanumeric': 0.12326157363307297, 'fraction_numerical': 0.009144598971232615, 'mean_word_length': 3.610891523935002, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '977578', 'n_tokens_mistral': 3059, 'n_tokens_neox': 2805, 'n_words': 932}