package htsjdk.samtools.util;
import htsjdk.samtools.SAMRecord;
import java.text.DecimalFormat;
import java.text.NumberFormat;
/**
* Little progress logging class to facilitate consistent output of useful information when progressing
* through a stream of SAM records.
*
* @author Tim Fennell
*/
public class ProgressLogger implements ProgressLoggerInterface {
private final Log log;
private final int n;
private final String verb;
private final String noun;
private final long startTime = System.currentTimeMillis();
private final NumberFormat fmt = new DecimalFormat("#,###");
private final NumberFormat timeFmt = new DecimalFormat("00");
private long processed = 0;
// Set to -1 until the first record is added
private long lastStartTime = -1;
/**
* Construct a progress logger.
* @param log the Log object to write outputs to
* @param n the frequency with which to output (i.e. every N records)
* @param verb the verb to log, e.g. "Processed, Read, Written".
* @param noun the noun to use when logging, e.g. "Records, Variants, Loci"
*/
public ProgressLogger(final Log log, final int n, final String verb, final String noun) {
this.log = log;
this.n = n;
this.verb = verb;
this.noun = noun;
}
/**
* Construct a progress logger.
* @param log the Log object to write outputs to
* @param n the frequency with which to output (i.e. every N records)
* @param verb the verb to log, e.g. "Processed, Read, Written".
*/
public ProgressLogger(final Log log, final int n, final String verb) {
this(log, n, verb, "records");
}
/**
* Construct a progress logger with the desired log and frequency and the verb "Processed".
* @param log the Log object to write outputs to
* @param n the frequency with which to output (i.e. every N records)
*/
public ProgressLogger(final Log log, final int n) { this(log, n, "Processed"); }
/**
* Construct a progress logger with the desired log, the verb "Processed" and a period of 1m records.
* @param log the Log object to write outputs to
*/
public ProgressLogger(final Log log) { this(log, 1000000); }
public synchronized boolean record(final String chrom, final int pos) {
if (this.lastStartTime == -1) this.lastStartTime = System.currentTimeMillis();
if (++this.processed % this.n == 0) {
final long now = System.currentTimeMillis();
final long lastPeriodSeconds = (now - this.lastStartTime) / 1000;
this.lastStartTime = now;
final long seconds = (System.currentTimeMillis() - startTime) / 1000;
final String elapsed = formatElapseTime(seconds);
final String period = pad(fmt.format(lastPeriodSeconds), 4);
final String processed = pad(fmt.format(this.processed), 13);
final String readInfo;
if (chrom == null) readInfo = "*/*";
else readInfo = chrom + ":" + fmt.format(pos);
log.info(this.verb, " ", processed, " " + noun + ". Elapsed time: ", elapsed, "s. Time for last ", fmt.format(this.n),
": ", period, "s. Last read position: ", readInfo);
return true;
}
else {
return false;
}
}
/**
* Records that a given record has been processed and triggers logging if necessary.
* @return boolean true if logging was triggered, false otherwise
*/
public synchronized boolean record(final SAMRecord rec) {
if (rec.getReferenceIndex() == SAMRecord.NO_ALIGNMENT_REFERENCE_INDEX) {
return record(null, 0);
}
else {
return record(rec.getReferenceName(), rec.getAlignmentStart());
}
}
/** Records multiple SAMRecords and triggers logging if necessary. */
public boolean record(final SAMRecord... recs) {
boolean triggered = false;
for (final SAMRecord rec : recs) triggered = record(rec) || triggered;
return triggered;
}
/** Returns the count of records processed. */
public long getCount() { return this.processed; }
/** Returns the number of seconds since progress tracking began. */
public long getElapsedSeconds() { return (System.currentTimeMillis() - this.startTime) / 1000; }
/** Left pads a string until it is at least the given length. */
private String pad (String in, final int length) {
while (in.length() < length) {
in = " " + in;
}
return in;
}
/** Formats a number of seconds into hours:minutes:seconds. */
private String formatElapseTime(final long seconds) {
final long s = seconds % 60;
final long allMinutes = seconds / 60;
final long m = allMinutes % 60;
final long h = allMinutes / 60;
return timeFmt.format(h) + ":" + timeFmt.format(m) + ":" + timeFmt.format(s);
}
}