org.apache.hadoop.hive.common.log.InPlaceUpdate Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.hadoop.hive.common.log;
import com.facebook.presto.hive.$internal.com.google.common.base.Function;
import com.facebook.presto.hive.$internal.com.google.common.collect.Lists;
import jline.TerminalFactory;
import com.facebook.presto.hive.$internal.org.apache.commons.lang.StringUtils;
import org.apache.hadoop.hive.conf.HiveConf;
import org.fusesource.jansi.Ansi;
import javax.annotation.Nullable;
import java.io.PrintStream;
import java.io.StringWriter;
import java.text.DecimalFormat;
import java.util.List;
import static org.fusesource.jansi.Ansi.ansi;
import static org.fusesource.jansi.internal.CLibrary.*;
/**
* Renders information from ProgressMonitor to the stream provided.
*/
public class InPlaceUpdate {
public static final int MIN_TERMINAL_WIDTH = 94;
// keep this within 80 chars width. If more columns needs to be added then update min terminal
// width requirement and SEPARATOR width accordingly
private static final String HEADER_FORMAT = "%16s%10s %13s %5s %9s %7s %7s %6s %6s ";
private static final String VERTEX_FORMAT = "%-16s%10s %13s %5s %9s %7s %7s %6s %6s ";
private static final String FOOTER_FORMAT = "%-15s %-30s %-4s %-25s";
private static final int PROGRESS_BAR_CHARS = 30;
private static final String SEPARATOR = new String(new char[MIN_TERMINAL_WIDTH]).replace("\0", "-");
/* Pretty print the values */
private final DecimalFormat secondsFormatter = new DecimalFormat("#0.00");
private int lines = 0;
private PrintStream out;
public InPlaceUpdate(PrintStream out) {
this.out = out;
}
public InPlaceUpdate() {
this(System.out);
}
public static void reprintLine(PrintStream out, String line) {
out.print(ansi().eraseLine(Ansi.Erase.ALL).a(line).a('\n').toString());
out.flush();
}
public static void rePositionCursor(PrintStream ps) {
ps.print(ansi().cursorUp(0).toString());
ps.flush();
}
/**
* NOTE: Use this method only if isUnixTerminal is true.
* Erases the current line and prints the given line.
*
* @param line - line to print
*/
private void reprintLine(String line) {
reprintLine(out, line);
lines++;
}
/**
* NOTE: Use this method only if isUnixTerminal is true.
* Erases the current line and prints the given line with the specified color.
*
* @param line - line to print
* @param color - color for the line
*/
private void reprintLineWithColorAsBold(String line, Ansi.Color color) {
out.print(ansi().eraseLine(Ansi.Erase.ALL).fg(color).bold().a(line).a('\n').boldOff().reset()
.toString());
out.flush();
lines++;
}
/**
* NOTE: Use this method only if isUnixTerminal is true.
* Erases the current line and prints the given multiline. Make sure the specified line is not
* terminated by linebreak.
*
* @param line - line to print
*/
private void reprintMultiLine(String line) {
int numLines = line.split("\r\n|\r|\n").length;
out.print(ansi().eraseLine(Ansi.Erase.ALL).a(line).a('\n').toString());
out.flush();
lines += numLines;
}
/**
* NOTE: Use this method only if isUnixTerminal is true.
* Repositions the cursor back to line 0.
*/
private void repositionCursor() {
if (lines > 0) {
out.print(ansi().cursorUp(lines).toString());
out.flush();
lines = 0;
}
}
// [==================>>-----]
private String getInPlaceProgressBar(double percent) {
StringWriter bar = new StringWriter();
bar.append("[");
int remainingChars = PROGRESS_BAR_CHARS - 4;
int completed = (int) (remainingChars * percent);
int pending = remainingChars - completed;
for (int i = 0; i < completed; i++) {
bar.append("=");
}
bar.append(">>");
for (int i = 0; i < pending; i++) {
bar.append("-");
}
bar.append("]");
return bar.toString();
}
public void render(ProgressMonitor monitor) {
if (monitor == null) return;
// position the cursor to line 0
repositionCursor();
// print header
// -------------------------------------------------------------------------------
// VERTICES STATUS TOTAL COMPLETED RUNNING PENDING FAILED KILLED
// -------------------------------------------------------------------------------
reprintLine(SEPARATOR);
reprintLineWithColorAsBold(String.format(HEADER_FORMAT, monitor.headers().toArray()),
Ansi.Color.CYAN);
reprintLine(SEPARATOR);
// Map 1 .......... container SUCCEEDED 7 7 0 0 0 0
List printReady = Lists.transform(monitor.rows(), new Function, String>() {
@Nullable
@Override
public String apply(@Nullable List row) {
return String.format(VERTEX_FORMAT, row.toArray());
}
});
reprintMultiLine(StringUtils.join(printReady, "\n"));
// -------------------------------------------------------------------------------
// VERTICES: 03/04 [=================>>-----] 86% ELAPSED TIME: 1.71 s
// -------------------------------------------------------------------------------
String progressStr = "" + (int) (monitor.progressedPercentage() * 100) + "%";
float et = (float) (System.currentTimeMillis() - monitor.startTime()) / (float) 1000;
String elapsedTime = "ELAPSED TIME: " + secondsFormatter.format(et) + " s";
String footer = String.format(
FOOTER_FORMAT,
monitor.footerSummary(),
getInPlaceProgressBar(monitor.progressedPercentage()),
progressStr,
elapsedTime);
reprintLine(SEPARATOR);
reprintLineWithColorAsBold(footer, Ansi.Color.RED);
reprintLine(SEPARATOR);
}
public static boolean canRenderInPlace(HiveConf conf) {
String engine = HiveConf.getVar(conf, HiveConf.ConfVars.HIVE_EXECUTION_ENGINE);
boolean inPlaceUpdates = false;
if (engine.equals("tez")) {
inPlaceUpdates = HiveConf.getBoolVar(conf, HiveConf.ConfVars.TEZ_EXEC_INPLACE_PROGRESS);
}
if (engine.equals("spark")) {
inPlaceUpdates = HiveConf.getBoolVar(conf, HiveConf.ConfVars.SPARK_EXEC_INPLACE_PROGRESS);
}
return inPlaceUpdates && isUnixTerminal();
}
private static boolean isUnixTerminal() {
String os = System.getProperty("os.name");
if (os.startsWith("Windows")) {
// we do not support Windows, we will revisit this if we really need it for windows.
return false;
}
// We must be on some unix variant..
// check if standard out is a terminal
try {
// isatty system call will return 1 if the file descriptor is terminal else 0
if (isatty(STDOUT_FILENO) == 0) {
return false;
}
if (isatty(STDERR_FILENO) == 0) {
return false;
}
} catch (NoClassDefFoundError | UnsatisfiedLinkError ignore) {
// These errors happen if the JNI lib is not available for your platform.
return false;
}
return true;
}
}