azkaban-developers
Changes
src/web/js/azkaban/model/job-log.js 80(+80 -0)
src/web/js/azkaban/view/job-details.js 268(+3 -265)
Details
diff --git a/src/java/azkaban/webapp/AzkabanWebServer.java b/src/java/azkaban/webapp/AzkabanWebServer.java
index 2133db8..ebb0aa0 100644
--- a/src/java/azkaban/webapp/AzkabanWebServer.java
+++ b/src/java/azkaban/webapp/AzkabanWebServer.java
@@ -999,7 +999,7 @@ public class AzkabanWebServer extends AzkabanServer {
String pluginName = pluginProps.getString("viewer.name");
String pluginWebPath = pluginProps.getString("viewer.path");
- String pluginJobType = pluginProps.getString("viewer.jobtype", null);
+ String pluginJobTypes = pluginProps.getString("viewer.jobtypes", null);
int pluginOrder = pluginProps.getInt("viewer.order", 0);
boolean pluginHidden = pluginProps.getBoolean("viewer.hidden", false);
List<String> extLibClasspath = pluginProps.getStringList("viewer.external.classpaths", (List<String>)null);
@@ -1108,7 +1108,7 @@ public class AzkabanWebServer extends AzkabanServer {
pluginWebPath,
pluginOrder,
pluginHidden,
- pluginJobType));
+ pluginJobTypes));
}
// Velocity needs the jar resource paths to be set.
diff --git a/src/java/azkaban/webapp/plugin/PluginRegistry.java b/src/java/azkaban/webapp/plugin/PluginRegistry.java
index b9382b6..a6f74d2 100644
--- a/src/java/azkaban/webapp/plugin/PluginRegistry.java
+++ b/src/java/azkaban/webapp/plugin/PluginRegistry.java
@@ -24,34 +24,37 @@ import java.util.TreeSet;
public class PluginRegistry {
- private static PluginRegistry registry;
+ private static PluginRegistry registry;
- public TreeSet<ViewerPlugin> viewerPlugins;
+ public TreeSet<ViewerPlugin> viewerPlugins;
public Map<String, TreeSet<ViewerPlugin>> jobTypeViewerPlugins;
- private PluginRegistry() {
+ private PluginRegistry() {
viewerPlugins = new TreeSet<ViewerPlugin>(ViewerPlugin.COMPARATOR);
jobTypeViewerPlugins = new HashMap<String, TreeSet<ViewerPlugin>>();
- }
+ }
- public void register(ViewerPlugin plugin) {
+ public void register(ViewerPlugin plugin) {
viewerPlugins.add(plugin);
- String jobType = plugin.getJobType();
- if (jobType == null) {
+ List<String> jobTypes = plugin.getJobTypes();
+ if (jobTypes == null) {
return;
}
- TreeSet<ViewerPlugin> plugins = null;
- if (!jobTypeViewerPlugins.containsKey(jobType)) {
- plugins = new TreeSet<ViewerPlugin>(ViewerPlugin.COMPARATOR);
- plugins.add(plugin);
- jobTypeViewerPlugins.put(jobType, plugins);
- }
- else {
- plugins = jobTypeViewerPlugins.get(jobType);
- plugins.add(plugin);
+
+ for (String jobType : jobTypes) {
+ TreeSet<ViewerPlugin> plugins = null;
+ if (!jobTypeViewerPlugins.containsKey(jobType)) {
+ plugins = new TreeSet<ViewerPlugin>(ViewerPlugin.COMPARATOR);
+ plugins.add(plugin);
+ jobTypeViewerPlugins.put(jobType, plugins);
+ }
+ else {
+ plugins = jobTypeViewerPlugins.get(jobType);
+ plugins.add(plugin);
+ }
}
- }
+ }
public List<ViewerPlugin> getViewerPlugins() {
return new ArrayList<ViewerPlugin>(viewerPlugins);
@@ -65,10 +68,10 @@ public class PluginRegistry {
return new ArrayList<ViewerPlugin>(plugins);
}
- public static PluginRegistry getRegistry() {
- if (registry == null) {
- registry = new PluginRegistry();
- }
- return registry;
- }
+ public static PluginRegistry getRegistry() {
+ if (registry == null) {
+ registry = new PluginRegistry();
+ }
+ return registry;
+ }
}
diff --git a/src/java/azkaban/webapp/plugin/ViewerPlugin.java b/src/java/azkaban/webapp/plugin/ViewerPlugin.java
index 9e167ff..b2bb851 100644
--- a/src/java/azkaban/webapp/plugin/ViewerPlugin.java
+++ b/src/java/azkaban/webapp/plugin/ViewerPlugin.java
@@ -16,14 +16,16 @@
package azkaban.webapp.plugin;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Comparator;
public class ViewerPlugin {
private final String pluginName;
private final String pluginPath;
- private final String jobType;
private final int order;
private boolean hidden;
+ private final List<String> jobTypes;
public static final Comparator<ViewerPlugin> COMPARATOR =
new Comparator<ViewerPlugin>() {
@@ -41,12 +43,12 @@ public class ViewerPlugin {
String pluginPath,
int order,
boolean hidden,
- String jobType) {
+ String jobTypes) {
this.pluginName = pluginName;
this.pluginPath = pluginPath;
this.order = order;
this.setHidden(hidden);
- this.jobType = jobType;
+ this.jobTypes = parseJobTypes(jobTypes);
}
public String getPluginName() {
@@ -69,7 +71,19 @@ public class ViewerPlugin {
this.hidden = hidden;
}
- public String getJobType() {
- return jobType;
+ protected List<String> parseJobTypes(String jobTypesStr) {
+ if (jobTypesStr == null) {
+ return null;
+ }
+ String[] parts = jobTypesStr.split(",");
+ List<String> jobTypes = new ArrayList<String>();
+ for (int i = 0; i < parts.length; ++i) {
+ jobTypes.add(parts[i].trim());
+ }
+ return jobTypes;
+ }
+
+ public List<String> getJobTypes() {
+ return jobTypes;
}
}
diff --git a/src/java/azkaban/webapp/servlet/velocity/jobdetailsheader.vm b/src/java/azkaban/webapp/servlet/velocity/jobdetailsheader.vm
index 27863ea..8d11704 100644
--- a/src/java/azkaban/webapp/servlet/velocity/jobdetailsheader.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/jobdetailsheader.vm
@@ -49,11 +49,9 @@
<ul class="nav nav-tabs nav-sm" id="headertabs">
#if ($current_page == "executing")
- <li id="jobLogViewLink"><a href="#logs">Job Logs</a></li>
- <li id="jobSummaryViewLink"><a href="#summary">Summary</a></li>
+ <li class="active" id="jobLogViewLink"><a href="#logs">Job Logs</a></li>
#else
<li id="jobLogViewLink"><a href="${context}/executor?execid=${execid}&job=${jobid}#logs">Job Logs</a></li>
- <li id="jobSummaryViewLink"><a href="${context}/executor?execid=${execid}&job=${jobid}#summary">Summary</a></li>
#end
#foreach ($jobViewerPlugin in $jobViewerPlugins)
<li#if($current_page == $jobViewerPlugin.pluginName) class="active"#end><a href="$!context/${jobViewerPlugin.pluginPath}?execid=${execid}&jobid=${jobid}">$jobViewerPlugin.pluginName</a></li>
diff --git a/src/java/azkaban/webapp/servlet/velocity/jobdetailspage.vm b/src/java/azkaban/webapp/servlet/velocity/jobdetailspage.vm
index 27f5d40..80d90e1 100644
--- a/src/java/azkaban/webapp/servlet/velocity/jobdetailspage.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/jobdetailspage.vm
@@ -22,7 +22,7 @@
#parse("azkaban/webapp/servlet/velocity/javascript.vm")
<script type="text/javascript" src="${context}/js/azkaban/util/ajax.js"></script>
- <script type="text/javascript" src="${context}/js/azkaban/model/log-data.js"></script>
+ <script type="text/javascript" src="${context}/js/azkaban/model/job-log.js"></script>
<script type="text/javascript" src="${context}/js/azkaban/view/job-details.js"></script>
<script type="text/javascript">
var contextURL = "${context}";
@@ -68,75 +68,10 @@
</div>
</div>
</div>
- </div>
- </div>
-
- ## Job Summary
-
- <div class="container-full" id="jobSummaryView">
- <div class="row">
- <div class="col-lg-12">
- <h3>
- Job Summary
- <div class="pull-right">
- <button type="button" id="updateSummaryBtn" class="btn btn-xs btn-default">Refresh</button>
- </div>
- </h3>
-
- <div id="jobType">
- <table id="jobTypeTable" class="table table-striped table-bordered table-hover">
- </table>
- </div>
-
- <div id="command-summary">
- <h4>Command Summary</h4>
- <table id="commandTable" class="table table-striped table-bordered table-hover">
- </table>
- </div>
-
- <div id="pigJobSummary">
- <h4>Pig Job Summary</h4>
- <table class="table table-striped table-bordered table-hover">
- <thead id="summaryHeader">
- </thead>
- <tbody id="summaryBody">
- </tbody>
- </table>
- </div>
-
- <div id="pigJobStats">
- <h4>Pig Job Stats</h4>
- <div class="panel-body-stats">
- <table class="table table-striped table-bordered table-hover table-condensed">
- <thead id="statsHeader">
- </thead>
- <tbody id="statsBody">
- </tbody>
- </table>
- </div>
- </div>
-
- <div id="hiveJobSummary">
- <h4>Hive Job Summary</h4>
- <table class="table table-striped table-bordered table-hover" id="hiveTable">
- <thead id="hiveTableHeader">
- </thead>
- <tbody id="hiveTableBody">
- </tbody>
- </table>
- </div>
-
- <div id="jobIds">
- <h4>Map Reduce Jobs</h4>
- <table class="table table-striped table-bordered table-hover">
- <tbody id="jobIdsTableBody"></tbody>
- </table>
- </div>
- </div>
</div>
</div>
-
- ## Error message message dialog.
+
+ ## Error message message dialog.
<div class="container-full">
<div class="modal" id="messageDialog">
src/web/js/azkaban/model/job-log.js 80(+80 -0)
diff --git a/src/web/js/azkaban/model/job-log.js b/src/web/js/azkaban/model/job-log.js
new file mode 100644
index 0000000..d295578
--- /dev/null
+++ b/src/web/js/azkaban/model/job-log.js
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2014 LinkedIn Corp.
+ *
+ * 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.
+ */
+
+$.namespace('azkaban');
+
+azkaban.JobLogModel = Backbone.Model.extend({
+ initialize: function() {
+ this.set("offset", 0);
+ this.set("logData", "");
+ },
+
+ refresh: function() {
+ var requestURL = contextURL + "/executor";
+ var finished = false;
+
+ var date = new Date();
+ var startTime = date.getTime();
+
+ while (!finished) {
+ var requestData = {
+ "execid": execId,
+ "jobId": jobId,
+ "ajax":"fetchExecJobLogs",
+ "offset": this.get("offset"),
+ "length": 50000,
+ "attempt": attempt
+ };
+
+ var self = this;
+
+ var successHandler = function(data) {
+ console.log("fetchLogs");
+ if (data.error) {
+ console.log(data.error);
+ finished = true;
+ }
+ else if (data.length == 0) {
+ finished = true;
+ }
+ else {
+ var date = new Date();
+ var endTime = date.getTime();
+ if ((endTime - startTime) > 10000) {
+ finished = true;
+ showDialog("Alert", "The log is taking a long time to finish loading. Azkaban has stopped loading them. Please click Refresh to restart the load.");
+ }
+
+ self.set("offset", data.offset + data.length);
+ self.set("logData", self.get("logData") + data.data);
+ }
+ }
+
+ $.ajax({
+ url: requestURL,
+ type: "get",
+ async: false,
+ data: requestData,
+ dataType: "json",
+ error: function(data) {
+ console.log(data);
+ finished = true;
+ },
+ success: successHandler
+ });
+ }
+ },
+});
src/web/js/azkaban/view/job-details.js 268(+3 -265)
diff --git a/src/web/js/azkaban/view/job-details.js b/src/web/js/azkaban/view/job-details.js
index 07b823e..7620b5a 100644
--- a/src/web/js/azkaban/view/job-details.js
+++ b/src/web/js/azkaban/view/job-details.js
@@ -38,247 +38,6 @@ azkaban.JobLogView = Backbone.View.extend({
}
});
-var jobSummaryView;
-azkaban.JobSummaryView = Backbone.View.extend({
- events: {
- "click #updateSummaryBtn" : "refresh"
- },
-
- initialize: function(settings) {
- $("#jobType").hide();
- $("#commandSummary").hide();
- $("#pigJobSummary").hide();
- $("#pigJobStats").hide();
- $("#hiveJobSummary").hide();
- $("#jobIds").hide();
-
- this.listenTo(this.model, "change:jobType", this.renderJobTypeTable);
- this.listenTo(this.model, "change:commandProperties", this.renderCommandTable);
- this.listenTo(this.model, "change:pigSummary", this.renderPigSummaryTable);
- this.listenTo(this.model, "change:pigStats", this.renderPigStatsTable);
- this.listenTo(this.model, "change:hiveSummary", this.renderHiveTable);
- this.listenTo(this.model, "change:jobIds", this.renderJobIdsTable);
- },
-
- refresh: function() {
- this.model.refresh();
- },
-
- handleUpdate: function(evt) {
- renderJobTable(jobSummary.summaryTableHeaders, jobSummary.summaryTableData, "summary");
- renderJobTable(jobSummary.statTableHeaders, jobSummary.statTableData, "stats");
- renderHiveTable(jobSummary.hiveQueries, jobSummary.hiveQueryJobs);
- },
-
- renderJobTypeTable: function() {
- var jobTypeTable = $("#jobTypeTable");
- var jobType = this.model.get("jobType");
-
- var tr = document.createElement("tr");
- var td = document.createElement("td");
- $(td).html("<b>Job Type</b>");
- $(tr).append(td);
- td = document.createElement("td");
- $(td).html(jobType);
- $(tr).append(td);
-
- jobTypeTable.append(tr);
-
- $("#jobType").show();
- },
-
- renderJobIdsTable: function() {
- var oldBody = $("#jobIdsTableBody");
- var newBody = $(document.createElement("tbody")).attr("id", "jobIdsTableBody");
-
- var jobIds = this.model.get("jobIds");
- var jobUrls = this.model.get("jobTrackerUrls");
- var numJobs = jobIds.length;
- for (var i = 0; i < numJobs; i++) {
- var job = jobIds[i];
- var tr = document.createElement("tr");
- var td = document.createElement("td");
- var html = jobUrls[job] ? "<a href='" + jobUrls[job] + "'>" + job + "</a>" : job;
- $(td).html(html);
- $(tr).append(td);
- newBody.append(tr);
- }
-
- oldBody.replaceWith(newBody);
-
- $("#jobIds").show();
- },
-
- renderCommandTable: function() {
- var commandTable = $("#commandTable");
- var commandProperties = this.model.get("commandProperties");
-
- for (var key in commandProperties) {
- if (commandProperties.hasOwnProperty(key)) {
- var value = commandProperties[key];
- if (Array.isArray(value)) {
- value = value.join("<br/>");
- }
- var tr = document.createElement("tr");
- var keyTd = document.createElement("td");
- var valueTd = document.createElement("td");
- $(keyTd).html("<b>" + key + "</b>");
- $(valueTd).html(value);
- $(tr).append(keyTd);
- $(tr).append(valueTd);
- commandTable.append(tr);
- }
- }
-
- $("#commandSummary").show();
- },
- renderPigTable: function(tableName, data) {
- // Add table headers
- var header = $("#" + tableName + "Header");
- var tr = document.createElement("tr");
- var i;
- var headers = data[0];
- var numColumns = headers.length;
- for (i = 0; i < numColumns; i++) {
- var th = document.createElement("th");
- $(th).text(headers[i]);
- $(tr).append(th);
- }
- header.append(tr);
-
- // Add table body
- var body = $("#" + tableName + "Body");
- for (i = 1; i < data.length; i++) {
- tr = document.createElement("tr");
- var row = data[i];
- for (var j = 0; j < numColumns; j++) {
- var td = document.createElement("td");
- if (j == 0) {
- // first column is a link to job details page
- $(td).html(row[j]);
- } else {
- $(td).text(row[j]);
- }
- $(tr).append(td);
- }
- body.append(tr);
- }
-
- $("#pigJob" + tableName.charAt(0).toUpperCase() + tableName.substring(1)).show();
- },
- renderPigSummaryTable: function() {
- this.renderPigTable("summary", this.model.get("pigSummary"));
- },
- renderPigStatsTable: function() {
- this.renderPigTable("stats", this.model.get("pigStats"));
- },
- renderHiveTable: function() {
- var hiveSummary = this.model.get("hiveSummary");
- var queries = hiveSummary.hiveQueries;
- var queryJobs = hiveSummary.hiveQueryJobs;
-
- // Set up table column headers
- var header = $("#hiveTableHeader");
- var tr = document.createElement("tr");
-
- var headers;
- if (this.model.get("hasCumulativeCPU")) {
- headers = ["Query","Job","Map","Reduce","Cumulative CPU","HDFS Read","HDFS Write"];
- } else {
- headers = ["Query","Job","Map","Reduce","HDFS Read","HDFS Write"];
- }
-
- var i;
- for (i = 0; i < headers.length; i++) {
- var th = document.createElement("th");
- $(th).text(headers[i]);
- $(tr).append(th);
- }
- header.html(tr);
-
- // Construct table body
- var oldBody = $("#hiveTableBody");
- var newBody = $(document.createElement("tbody")).attr("id", "hiveTableBody");
- for (i = 0; i < queries.length; i++) {
- // new query
- tr = document.createElement("tr");
- var td = document.createElement("td");
- $(td).html("<b>" + queries[i] + "</b>");
- $(tr).append(td);
-
- var jobs = queryJobs[i];
- if (jobs != null) {
- // add first job for this query
- var jobValues = jobs[0];
- var j;
- for (j = 0; j < jobValues.length; j++) {
- td = document.createElement("td");
- $(td).html(jobValues[j]);
- $(tr).append(td);
- }
- newBody.append(tr);
-
- // add remaining jobs for this query
- for (j = 1; j < jobs.length; j++) {
- jobValues = jobs[j];
- tr = document.createElement("tr");
-
- // add empty cell for query column
- td = document.createElement("td");
- $(td).html(" ");
- $(tr).append(td);
-
- // add job values
- for (var k = 0; k < jobValues.length; k++) {
- td = document.createElement("td");
- $(td).html(jobValues[k]);
- $(tr).append(td);
- }
- newBody.append(tr);
- }
-
- } else {
- newBody.append(tr);
- }
- }
- oldBody.replaceWith(newBody);
-
- $("#hiveJobSummary").show();
- }
-});
-
-var jobTabView;
-azkaban.JobTabView = Backbone.View.extend({
- events: {
- 'click #jobSummaryViewLink': 'handleJobSummaryViewLinkClick',
- 'click #jobLogViewLink': 'handleJobLogViewLinkClick'
- },
-
- initialize: function(settings) {
- var selectedView = settings.selectedView;
- if (selectedView == 'summary') {
- this.handleJobSummaryViewLinkClick();
- }
- else {
- this.handleJobLogViewLinkClick();
- }
- },
-
- handleJobLogViewLinkClick: function() {
- $('#jobSummaryViewLink').removeClass('active');
- $('#jobSummaryView').hide();
- $('#jobLogViewLink').addClass('active');
- $('#jobLogView').show();
- },
-
- handleJobSummaryViewLinkClick: function() {
- $('#jobSummaryViewLink').addClass('active');
- $('#jobSummaryView').show();
- $('#jobLogViewLink').removeClass('active');
- $('#jobLogView').hide();
- },
-});
-
var showDialog = function(title, message) {
$('#messageTitle').text(title);
$('#messageBox').text(message);
@@ -296,31 +55,10 @@ var showDialog = function(title, message) {
}
$(function() {
- var logDataModel = new azkaban.LogDataModel();
-
+ var jobLogModel = new azkaban.JobLogModel();
jobLogView = new azkaban.JobLogView({
el: $('#jobLogView'),
- model: logDataModel
- });
-
- jobSummaryView = new azkaban.JobSummaryView({
- el: $('#jobSummaryView'),
- model: logDataModel
+ model: jobLogModel
});
-
- jobTabView = new azkaban.JobTabView({
- el: $('#headertabs')
- });
-
- logDataModel.refresh();
-
- if (window.location.hash) {
- var hash = window.location.hash;
- if (hash == '#logs') {
- jobTabView.handleJobLogViewLinkClick();
- }
- else if (hash == '#summary') {
- jobTabView.handleJobSummaryViewLinkClick();
- }
- }
+ jobLogModel.refresh();
});