StatusLogger Class

package javaxt.express.utils;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;


//******************************************************************************
//**  StatusLogger
//******************************************************************************
/**
 *  Used to print status messages to the standard output stream. Status
 *  messages are written every second and appear in the following format:
 *  <pre>0 records processed (0 records per second)</pre>
 *  A percent completion is appended to the status message if a "totalRecords"
 *  counter is given.<br/>
 *  The status logger is run in a separate thread. The "recordCounter" is
 *  updated by the caller. Example:
 <pre>
    AtomicLong recordCounter = new AtomicLong(0);
    StatusLogger statusLogger = new StatusLogger(recordCounter);
    while (true){
       //Execute some process then update the counter
       recordCounter.incrementAndGet();
    }
    statusLogger.shutdown();
 </pre>
 *
 ******************************************************************************/

public class StatusLogger {

    private long startTime;
    private AtomicLong totalRecords;
    private String statusText = "0 records processed (0 records per second)";
    private ScheduledExecutorService executor;
    private Runnable r;
    private boolean separateMessages = false;


  //**************************************************************************
  //** Constructor
  //**************************************************************************
    public StatusLogger(AtomicLong recordCounter){
        this(recordCounter, null);
    }


  //**************************************************************************
  //** Constructor
  //**************************************************************************
    public StatusLogger(AtomicLong recordCounter, AtomicLong totalRecords){
        startTime = System.currentTimeMillis();
        this.totalRecords = totalRecords==null ? new AtomicLong(0) : totalRecords;

        StatusLogger me = this;
        r = new Runnable(){
            public void run() {
                long currTime = System.currentTimeMillis();
                double elapsedTime = (currTime-startTime)/1000; //seconds
                long x = recordCounter.get();
                AtomicLong totalRecords = me.totalRecords;

                String rate = "0";
                try{
                    long r = Math.round(x/elapsedTime);
                    if (totalRecords!=null && totalRecords.get()>0){
                        if (r>totalRecords.get()) r = totalRecords.get();
                    }
                    rate = StringUtils.format(r);
                }
                catch(Exception e){}

                int len = statusText.length();
                if (!separateMessages){
                    for (int i=0; i<len; i++){
                        System.out.print("\b");
                    }
                }

                statusText = StringUtils.format(x) + " records processed (" + rate + " records per second)";


                if (totalRecords!=null && totalRecords.get()>0){
                    double p = ((double) x / (double) totalRecords.get());
                    int currPercent = (int) Math.round(p*100);
                    statusText += " " + x + "/" + totalRecords.get() + " " + currPercent + "%";
                }

                while (statusText.length()<len) statusText += " ";


                System.out.print(statusText + (separateMessages ? "\r\n" : ""));
            }
        };


        executor = Executors.newScheduledThreadPool(1);
        executor.scheduleAtFixedRate(r, 0, 1, TimeUnit.SECONDS);
    }


  //**************************************************************************
  //** setTotalRecords
  //**************************************************************************
  /** Used to set the total number of records expected to be processed. By
   *  setting the total record count, the status logger will print a percent
   *  completion status update.
   */
    public void setTotalRecords(long n){
        totalRecords.set(n);
        r.run();
    }


  //**************************************************************************
  //** separateMessages
  //**************************************************************************
  /** By default, status messages are written to a single line and overwritten
   *  with every update. However, this may not be appropriate when an app is
   *  writing debug messages to the same output stream. In such cases, it's
   *  best to have the status logger write status updates to a new line.
   */
    public void separateMessages(boolean b){
        separateMessages = b;
    }


  //**************************************************************************
  //** shutdown
  //**************************************************************************
  /** Used to stop the status logger.
   */
    public void shutdown(){

      //Send one last status update
        r.run();

      //Clean up
        executor.shutdown();
    }
}