All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.lithium.flow.util.Progress Maven / Gradle / Ivy

/*
 * Copyright 2015 Lithium Technologies, Inc.
 *
 * 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 com.lithium.flow.util;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.lithium.flow.util.PrintUtils.time;

import com.lithium.flow.config.Config;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;

import javax.annotation.Nonnull;

import org.slf4j.Logger;

/**
 * @author Matt Ayres
 */
public class Progress {
	private static final Logger log = Logs.getLogger();

	private final List measures = new ArrayList<>();
	private Thread loop;
	private long startTime;
	private boolean stopped;

	@Nonnull
	public Measure measure(@Nonnull String name) {
		return measure(name, n -> String.valueOf(n.longValue()));
	}

	@Nonnull
	public Measure counter(@Nonnull String name) {
		return measure(name, PrintUtils::count);
	}

	@Nonnull
	public Measure bandwidth(@Nonnull String name) {
		return measure(name, PrintUtils::bytes);
	}

	@Nonnull
	public Measure unique(@Nonnull String name) {
		return measure(name).hideAvg();
	}

	@Nonnull
	public synchronized Measure measure(@Nonnull String name, @Nonnull Function printer) {
		checkNotNull(name);
		checkNotNull(printer);

		Measure measure = new Measure(name, printer);
		measures.add(measure);
		return measure;
	}

	@Nonnull
	public synchronized Progress start(long logInterval, long avgInterval) {
		if (loop != null) {
			throw new IllegalStateException("already started");
		} else if (stopped) {
			throw new IllegalStateException("already used");
		}

		startTime = System.currentTimeMillis();
		loop = new LoopThread(logInterval, () -> log(avgInterval));
		return this;
	}

	public synchronized void stop() {
		if (loop == null) {
			throw new IllegalStateException("not started");
		} else if (stopped) {
			throw new IllegalStateException("already used");
		}

		loop.interrupt();
		stopped = true;
	}

	public void finish() {
		stop();
		log(System.currentTimeMillis() - startTime);
	}

	public synchronized void log(long avgInterval) {
		long elapsed = System.currentTimeMillis() - startTime;
		if (!stopped && elapsed < 1000) {
			return;
		}

		StringBuilder sb = new StringBuilder();
		if (startTime > 0) {
			sb.append(time(elapsed));
		}

		if (stopped) {
			sb.append(" (done)");
		} else {
			double eta = avgInterval > 0 ? getEta(avgInterval) : 0;
			if (eta > 0) {
				sb.append(" (").append(time(eta)).append(" eta)");
			}
		}

		for (Measure measure : measures) {
			Function printer = measure.getPrinter();
			long todo = measure.getTodo();
			long done = measure.getDone();

			sb.append(", ").append(printer.apply(done));
			if (todo > 0) {
				sb.append("/").append(printer.apply(todo));
			}
			sb.append(" ").append(measure.getName());
			if (!measure.isHideAvg()) {
				sb.append(" (").append(printer.apply(measure.avg(avgInterval)));
				sb.append("/s)");
			}
		}

		log.info(sb.toString());
	}

	private double getEta(long avgInterval) {
		return measures.stream()
				.filter(Measure::isForEta)
				.filter(m -> m.avg(avgInterval) > 0)
				.mapToDouble(m -> m.getLeft() * 1000 / m.avg(avgInterval))
				.max().orElse(0);
	}

	@Nonnull
	public static Progress start(@Nonnull Config config) {
		return new Progress().start(config.getTime("log.interval", "5s"), config.getTime("avg.interval", "1m"));
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy