aboutsummaryrefslogtreecommitdiff
path: root/core/src/main/scala/org
diff options
context:
space:
mode:
authorMatei Zaharia <matei@databricks.com>2015-05-08 14:41:42 -0400
committerMatei Zaharia <matei@databricks.com>2015-05-08 14:41:42 -0400
commita1ec08f7edc8d956afcfbb92d10b26b7619486e8 (patch)
tree9b1ce3161ea21e205592ca7ba3c07667b78d2941 /core/src/main/scala/org
parent35d6a99cbe3f67da5d56888e63baf9bc69f3de91 (diff)
downloadspark-a1ec08f7edc8d956afcfbb92d10b26b7619486e8.tar.gz
spark-a1ec08f7edc8d956afcfbb92d10b26b7619486e8.tar.bz2
spark-a1ec08f7edc8d956afcfbb92d10b26b7619486e8.zip
[SPARK-7298] Harmonize style of new visualizations
- Colors on the timeline now match the rest of the UI - The expandable buttons to show timeline view, DAG, etc are now more visible - Timeline text is smaller - DAG visualization text and colors are more consistent throughout - Fix some JavaScript style issues - Various small fixes throughout (e.g. inconsistent capitalization, some confusing names, HTML escaping, etc) Author: Matei Zaharia <matei@databricks.com> Closes #5942 from mateiz/ui and squashes the following commits: def38d0 [Matei Zaharia] Add some tooltips 4c5a364 [Matei Zaharia] Reduce stage and rank separation slightly 43dcbe3 [Matei Zaharia] Some updates to DAG fac734a [Matei Zaharia] tweaks 6a6705d [Matei Zaharia] More fixes 67629f5 [Matei Zaharia] Various small tweaks
Diffstat (limited to 'core/src/main/scala/org')
-rw-r--r--core/src/main/scala/org/apache/spark/ui/ToolTips.scala19
-rw-r--r--core/src/main/scala/org/apache/spark/ui/UIUtils.scala15
-rw-r--r--core/src/main/scala/org/apache/spark/ui/jobs/AllJobsPage.scala31
-rw-r--r--core/src/main/scala/org/apache/spark/ui/jobs/AllStagesPage.scala15
-rw-r--r--core/src/main/scala/org/apache/spark/ui/jobs/JobPage.scala23
-rw-r--r--core/src/main/scala/org/apache/spark/ui/jobs/StagePage.scala16
-rw-r--r--core/src/main/scala/org/apache/spark/ui/scope/RDDOperationGraph.scala2
7 files changed, 70 insertions, 51 deletions
diff --git a/core/src/main/scala/org/apache/spark/ui/ToolTips.scala b/core/src/main/scala/org/apache/spark/ui/ToolTips.scala
index 24f3236456..063e2a1f8b 100644
--- a/core/src/main/scala/org/apache/spark/ui/ToolTips.scala
+++ b/core/src/main/scala/org/apache/spark/ui/ToolTips.scala
@@ -57,4 +57,23 @@ private[spark] object ToolTips {
val GC_TIME =
"""Time that the executor spent paused for Java garbage collection while the task was
running."""
+
+ val JOB_TIMELINE =
+ """Shows when jobs started and ended and when executors joined or left. Drag to scroll.
+ Click Enable Zooming and use mouse wheel to zoom in/out."""
+
+ val STAGE_TIMELINE =
+ """Shows when stages started and ended and when executors joined or left. Drag to scroll.
+ Click Enable Zooming and use mouse wheel to zoom in/out."""
+
+ val JOB_DAG =
+ """Shows a graph of stages executed for this job, each of which can contain
+ multiple RDD operations (e.g. map() and filter()), and of RDDs inside each operation
+ (shown as dots)."""
+
+ val STAGE_DAG =
+ """Shows a graph of RDD operations in this stage, and RDDs inside each one. A stage can run
+ multiple operations (e.g. two map() functions) if they can be pipelined. Some operations
+ also create multiple RDDs internally. Cached RDDs are shown in green.
+ """
}
diff --git a/core/src/main/scala/org/apache/spark/ui/UIUtils.scala b/core/src/main/scala/org/apache/spark/ui/UIUtils.scala
index 97eed13c2d..6a0f5c5d16 100644
--- a/core/src/main/scala/org/apache/spark/ui/UIUtils.scala
+++ b/core/src/main/scala/org/apache/spark/ui/UIUtils.scala
@@ -156,10 +156,10 @@ private[spark] object UIUtils extends Logging {
def commonHeaderNodes: Seq[Node] = {
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
- <link rel="stylesheet" href={prependBaseUri("/static/bootstrap.min.css")} type="text/css" />
- <link rel="stylesheet" href={prependBaseUri("/static/webui.css")} type="text/css" />
- <link rel="stylesheet" href={prependBaseUri("/static/vis.min.css")} type="text/css" />
- <link rel="stylesheet" href={prependBaseUri("/static/timeline-view.css")} type="text/css" />
+ <link rel="stylesheet" href={prependBaseUri("/static/bootstrap.min.css")} type="text/css"/>
+ <link rel="stylesheet" href={prependBaseUri("/static/vis.min.css")} type="text/css"/>
+ <link rel="stylesheet" href={prependBaseUri("/static/webui.css")} type="text/css"/>
+ <link rel="stylesheet" href={prependBaseUri("/static/timeline-view.css")} type="text/css"/>
<script src={prependBaseUri("/static/sorttable.js")} ></script>
<script src={prependBaseUri("/static/jquery-1.11.1.min.js")}></script>
<script src={prependBaseUri("/static/vis.min.js")}></script>
@@ -250,7 +250,7 @@ private[spark] object UIUtils extends Logging {
<h3 style="vertical-align: middle; display: inline-block;">
<a style="text-decoration: none" href={prependBaseUri("/")}>
<img src={prependBaseUri("/static/spark-logo-77x50px-hd.png")} />
- <span class="version"
+ <span class="version"
style="margin-right: 15px;">{org.apache.spark.SPARK_VERSION}</span>
</a>
{title}
@@ -350,7 +350,10 @@ private[spark] object UIUtils extends Logging {
<div>
<span class="expand-dag-viz" onclick={s"toggleDagViz($forJob);"}>
<span class="expand-dag-viz-arrow arrow-closed"></span>
- <strong>DAG visualization</strong>
+ <a data-toggle="tooltip" title={if (forJob) ToolTips.JOB_DAG else ToolTips.STAGE_DAG}
+ data-placement="right">
+ DAG Visualization
+ </a>
</span>
<div id="dag-viz-graph"></div>
<div id="dag-viz-metadata">
diff --git a/core/src/main/scala/org/apache/spark/ui/jobs/AllJobsPage.scala b/core/src/main/scala/org/apache/spark/ui/jobs/AllJobsPage.scala
index 09323d1d80..e010ebef3b 100644
--- a/core/src/main/scala/org/apache/spark/ui/jobs/AllJobsPage.scala
+++ b/core/src/main/scala/org/apache/spark/ui/jobs/AllJobsPage.scala
@@ -18,12 +18,12 @@
package org.apache.spark.ui.jobs
import scala.collection.mutable.{HashMap, ListBuffer}
-import scala.xml.{Node, NodeSeq, Unparsed}
+import scala.xml.{Node, NodeSeq, Unparsed, Utility}
import java.util.Date
import javax.servlet.http.HttpServletRequest
-import org.apache.spark.ui.{UIUtils, WebUIPage}
+import org.apache.spark.ui.{ToolTips, UIUtils, WebUIPage}
import org.apache.spark.ui.jobs.UIData.{ExecutorUIData, JobUIData}
import org.apache.spark.JobExecutionStatus
@@ -81,6 +81,9 @@ private[ui] class AllJobsPage(parent: JobsTab) extends WebUIPage("") {
case JobExecutionStatus.RUNNING => "running"
}
+ // The timeline library treats contents as HTML, so we have to escape them; for the
+ // data-title attribute string we have to escape them twice since that's in a string.
+ val escapedDesc = Utility.escape(displayJobDescription)
val jobEventJsonAsStr =
s"""
|{
@@ -90,16 +93,17 @@ private[ui] class AllJobsPage(parent: JobsTab) extends WebUIPage("") {
| 'end': new Date(${completionTime}),
| 'content': '<div class="application-timeline-content"' +
| 'data-html="true" data-placement="top" data-toggle="tooltip"' +
- | 'data-title="${displayJobDescription} (Job ${jobId})<br>Status: ${status}<br>' +
- | 'Submission Time: ${UIUtils.formatDate(new Date(submissionTime))}' +
+ | 'data-title="${Utility.escape(escapedDesc)} (Job ${jobId})<br>' +
+ | 'Status: ${status}<br>' +
+ | 'Submitted: ${UIUtils.formatDate(new Date(submissionTime))}' +
| '${
if (status != JobExecutionStatus.RUNNING) {
- s"""<br>Completion Time: ${UIUtils.formatDate(new Date(completionTime))}"""
+ s"""<br>Completed: ${UIUtils.formatDate(new Date(completionTime))}"""
} else {
""
}
}">' +
- | '${displayJobDescription} (Job ${jobId})</div>'
+ | '${escapedDesc} (Job ${jobId})</div>'
|}
""".stripMargin
jobEventJsonAsStr
@@ -179,13 +183,15 @@ private[ui] class AllJobsPage(parent: JobsTab) extends WebUIPage("") {
<span class="expand-application-timeline">
<span class="expand-application-timeline-arrow arrow-closed"></span>
- <strong>Event timeline</strong>
+ <a data-toggle="tooltip" title={ToolTips.JOB_TIMELINE} data-placement="right">
+ Event Timeline
+ </a>
</span> ++
<div id="application-timeline" class="collapsed">
<div class="control-panel">
<div id="application-timeline-zoom-lock">
- <input type="checkbox" checked="checked"></input>
- <span>Zoom Lock</span>
+ <input type="checkbox"></input>
+ <span>Enable zooming</span>
</div>
</div>
</div> ++
@@ -283,7 +289,7 @@ private[ui] class AllJobsPage(parent: JobsTab) extends WebUIPage("") {
{if (parent.sc.isDefined) {
// Total duration is not meaningful unless the UI is live
<li>
- <strong>Total Duration: </strong>
+ <strong>Total Uptime: </strong>
{UIUtils.formatDuration(System.currentTimeMillis() - startTime)}
</li>
}}
@@ -336,9 +342,8 @@ private[ui] class AllJobsPage(parent: JobsTab) extends WebUIPage("") {
failedJobsTable
}
- val helpText = """A job is triggered by an action, like "count()" or "saveAsTextFile()".""" +
- " Click on a job's title to see information about the stages of tasks associated with" +
- " the job."
+ val helpText = """A job is triggered by an action, like count() or saveAsTextFile().""" +
+ " Click on a job to see information about the stages of tasks inside it."
UIUtils.headerSparkPage("Spark Jobs", content, parent, helpText = Some(helpText))
}
diff --git a/core/src/main/scala/org/apache/spark/ui/jobs/AllStagesPage.scala b/core/src/main/scala/org/apache/spark/ui/jobs/AllStagesPage.scala
index a37f739ab9..5e52942b64 100644
--- a/core/src/main/scala/org/apache/spark/ui/jobs/AllStagesPage.scala
+++ b/core/src/main/scala/org/apache/spark/ui/jobs/AllStagesPage.scala
@@ -74,19 +74,6 @@ private[ui] class AllStagesPage(parent: StagesTab) extends WebUIPage("") {
<div>
<ul class="unstyled">
{
- if (sc.isDefined) {
- // Total duration is not meaningful unless the UI is live
- <li>
- <strong>Total Duration: </strong>
- {UIUtils.formatDuration(now - sc.get.startTime)}
- </li>
- }
- }
- <li>
- <strong>Scheduling Mode: </strong>
- {listener.schedulingMode.map(_.toString).getOrElse("Unknown")}
- </li>
- {
if (shouldShowActiveStages) {
<li>
<a href="#active"><strong>Active Stages:</strong></a>
@@ -145,7 +132,7 @@ private[ui] class AllStagesPage(parent: StagesTab) extends WebUIPage("") {
content ++= <h4 id ="failed">Failed Stages ({numFailedStages})</h4> ++
failedStagesTable.toNodeSeq
}
- UIUtils.headerSparkPage("Spark Stages (for all jobs)", content, parent)
+ UIUtils.headerSparkPage("Stages for All Jobs", content, parent)
}
}
}
diff --git a/core/src/main/scala/org/apache/spark/ui/jobs/JobPage.scala b/core/src/main/scala/org/apache/spark/ui/jobs/JobPage.scala
index 7163217e1f..2cad0a7969 100644
--- a/core/src/main/scala/org/apache/spark/ui/jobs/JobPage.scala
+++ b/core/src/main/scala/org/apache/spark/ui/jobs/JobPage.scala
@@ -20,13 +20,13 @@ package org.apache.spark.ui.jobs
import java.util.Date
import scala.collection.mutable.{Buffer, HashMap, ListBuffer}
-import scala.xml.{NodeSeq, Node, Unparsed}
+import scala.xml.{NodeSeq, Node, Unparsed, Utility}
import javax.servlet.http.HttpServletRequest
import org.apache.spark.JobExecutionStatus
import org.apache.spark.scheduler.StageInfo
-import org.apache.spark.ui.{UIUtils, WebUIPage}
+import org.apache.spark.ui.{ToolTips, UIUtils, WebUIPage}
import org.apache.spark.ui.jobs.UIData.ExecutorUIData
/** Page showing statistics and stage list for a given job */
@@ -64,6 +64,9 @@ private[ui] class JobPage(parent: JobsTab) extends WebUIPage("job") {
val submissionTime = stage.submissionTime.get
val completionTime = stage.completionTime.getOrElse(System.currentTimeMillis())
+ // The timeline library treats contents as HTML, so we have to escape them; for the
+ // data-title attribute string we have to escape them twice since that's in a string.
+ val escapedName = Utility.escape(name)
s"""
|{
| 'className': 'stage job-timeline-object ${status}',
@@ -72,17 +75,17 @@ private[ui] class JobPage(parent: JobsTab) extends WebUIPage("job") {
| 'end': new Date(${completionTime}),
| 'content': '<div class="job-timeline-content" data-toggle="tooltip"' +
| 'data-placement="top" data-html="true"' +
- | 'data-title="${name} (Stage ${stageId}.${attemptId})<br>' +
+ | 'data-title="${Utility.escape(escapedName)} (Stage ${stageId}.${attemptId})<br>' +
| 'Status: ${status.toUpperCase}<br>' +
- | 'Submission Time: ${UIUtils.formatDate(new Date(submissionTime))}' +
+ | 'Submitted: ${UIUtils.formatDate(new Date(submissionTime))}' +
| '${
if (status != "running") {
- s"""<br>Completion Time: ${UIUtils.formatDate(new Date(completionTime))}"""
+ s"""<br>Completed: ${UIUtils.formatDate(new Date(completionTime))}"""
} else {
""
}
}">' +
- | '${name} (Stage ${stageId}.${attemptId})</div>',
+ | '${escapedName} (Stage ${stageId}.${attemptId})</div>',
|}
""".stripMargin
}
@@ -161,13 +164,15 @@ private[ui] class JobPage(parent: JobsTab) extends WebUIPage("job") {
<span class="expand-job-timeline">
<span class="expand-job-timeline-arrow arrow-closed"></span>
- <strong>Event timeline</strong>
+ <a data-toggle="tooltip" title={ToolTips.STAGE_TIMELINE} data-placement="right">
+ Event Timeline
+ </a>
</span> ++
<div id="job-timeline" class="collapsed">
<div class="control-panel">
<div id="job-timeline-zoom-lock">
- <input type="checkbox" checked="checked"></input>
- <span>Zoom Lock</span>
+ <input type="checkbox"></input>
+ <span>Enable zooming</span>
</div>
</div>
</div> ++
diff --git a/core/src/main/scala/org/apache/spark/ui/jobs/StagePage.scala b/core/src/main/scala/org/apache/spark/ui/jobs/StagePage.scala
index b01fad8e45..8f7b1c2f09 100644
--- a/core/src/main/scala/org/apache/spark/ui/jobs/StagePage.scala
+++ b/core/src/main/scala/org/apache/spark/ui/jobs/StagePage.scala
@@ -81,7 +81,7 @@ private[ui] class StagePage(parent: StagesTab) extends WebUIPage("stage") {
<div>
<ul class="unstyled">
<li>
- <strong>Total task time across all tasks: </strong>
+ <strong>Total Time Across All Tasks: </strong>
{UIUtils.formatDuration(stageData.executorRunTime)}
</li>
{if (stageData.hasInput) {
@@ -98,25 +98,25 @@ private[ui] class StagePage(parent: StagesTab) extends WebUIPage("stage") {
}}
{if (stageData.hasShuffleRead) {
<li>
- <strong>Shuffle read: </strong>
+ <strong>Shuffle Read: </strong>
{s"${Utils.bytesToString(stageData.shuffleReadTotalBytes)} / " +
s"${stageData.shuffleReadRecords}"}
</li>
}}
{if (stageData.hasShuffleWrite) {
<li>
- <strong>Shuffle write: </strong>
+ <strong>Shuffle Write: </strong>
{s"${Utils.bytesToString(stageData.shuffleWriteBytes)} / " +
s"${stageData.shuffleWriteRecords}"}
</li>
}}
{if (stageData.hasBytesSpilled) {
<li>
- <strong>Shuffle spill (memory): </strong>
+ <strong>Shuffle Spill (Memory): </strong>
{Utils.bytesToString(stageData.memoryBytesSpilled)}
</li>
<li>
- <strong>Shuffle spill (disk): </strong>
+ <strong>Shuffle Spill (Disk): </strong>
{Utils.bytesToString(stageData.diskBytesSpilled)}
</li>
}}
@@ -127,10 +127,10 @@ private[ui] class StagePage(parent: StagesTab) extends WebUIPage("stage") {
<div>
<span class="expand-additional-metrics">
<span class="expand-additional-metrics-arrow arrow-closed"></span>
- <strong>Show additional metrics</strong>
+ <a>Show Additional Metrics</a>
</span>
<div class="additional-metrics collapsed">
- <ul style="list-style-type:none">
+ <ul>
<li>
<input type="checkbox" id="select-all-metrics"/>
<span class="additional-metric-title"><em>(De)select All</em></span>
@@ -457,9 +457,9 @@ private[ui] class StagePage(parent: StagesTab) extends WebUIPage("stage") {
val content =
summary ++
- showAdditionalMetrics ++
dagViz ++
maybeExpandDagViz ++
+ showAdditionalMetrics ++
<h4>Summary Metrics for {numCompleted} Completed Tasks</h4> ++
<div>{summaryTable.getOrElse("No tasks have reported metrics yet.")}</div> ++
<h4>Aggregated Metrics by Executor</h4> ++ executorTable.toNodeSeq ++
diff --git a/core/src/main/scala/org/apache/spark/ui/scope/RDDOperationGraph.scala b/core/src/main/scala/org/apache/spark/ui/scope/RDDOperationGraph.scala
index 2b2db9e62b..c7045c98c8 100644
--- a/core/src/main/scala/org/apache/spark/ui/scope/RDDOperationGraph.scala
+++ b/core/src/main/scala/org/apache/spark/ui/scope/RDDOperationGraph.scala
@@ -182,7 +182,7 @@ private[ui] object RDDOperationGraph extends Logging {
if (forJob) {
s"""${node.id} [label="$label" shape="circle" padding="5" labelStyle="font-size: 0"]"""
} else {
- s"""${node.id} [label="$label" padding="5" labelStyle="font-size: 10"]"""
+ s"""${node.id} [label="$label" padding="5" labelStyle="font-size: 12px"]"""
}
}