Browse Source

HUE-832 [jobbrowser] Easier access to the task logs

Added pointer to specific page for attempt logs
Enrico Berti 13 years ago
parent
commit
df83f77fc2

+ 6 - 41
apps/jobbrowser/src/jobbrowser/templates/attempt.mako

@@ -51,10 +51,10 @@ ${commonheader(_('Task Attempt: %(attemptId)s - Job Browser') % dict(attemptId=a
         </div>
         </div>
 
 
         <div class="span10">
         <div class="span10">
-            <ul class="nav nav-tabs">
+            <ul id="tabs" class="nav nav-tabs">
                 <li class="active"><a href="#metadata" data-toggle="tab">${_('Metadata')}</a></li>
                 <li class="active"><a href="#metadata" data-toggle="tab">${_('Metadata')}</a></li>
                 <li><a href="#counters" data-toggle="tab">${_('Counters')}</a></li>
                 <li><a href="#counters" data-toggle="tab">${_('Counters')}</a></li>
-                <li><a href="#logs" data-toggle="tab">${_('Logs')}</a></li>
+                <li><a href="${ url('jobbrowser.views.single_task_attempt_logs', jobid=task.jobId, taskid=task.taskId, attemptid=attempt.attemptId) }">${_('Logs')}</a></li>
             </ul>
             </ul>
 
 
             <div class="tab-content">
             <div class="tab-content">
@@ -118,45 +118,6 @@ ${commonheader(_('Task Attempt: %(attemptId)s - Job Browser') % dict(attemptId=a
                 <div class="tab-pane" id="counters">
                 <div class="tab-pane" id="counters">
                     ${comps.task_counters(task.counters)}
                     ${comps.task_counters(task.counters)}
                 </div>
                 </div>
-
-                <div class="tab-pane jt-logs" id="logs">
-                    <%
-                        log_diagnostic = logs[0]
-                        log_stdout = logs[1]
-                        log_stderr = logs[2]
-                        log_syslog = logs[3]
-                    %>
-                    <%def name="format_log(raw)">
-                        ## have to remove any indentation here or it breaks inside the pre tags
-                          % for line in raw.split('\n'):
-                              ${ line | h,trim }
-                        % endfor
-                    </%def>
-                    <h2>${_('task diagnostic log')}</h2>
-                    % if not log_diagnostic:
-                            <pre>-- empty --</pre>
-                    % else:
-                            <pre>${format_log(log_diagnostic)}</pre>
-                    % endif
-                    <h2>${_('stdout')}</h2>
-                    % if not log_stdout:
-                            <pre>-- empty --</pre>
-                    % else:
-                            <pre>${format_log(log_stdout)}</pre>
-                    % endif
-                    <h2>${_('stderr')}</h2>
-                    % if not log_stderr:
-                            <pre>-- empty --</pre>
-                    % else:
-                            <pre>${format_log(log_stderr)}</pre>
-                    % endif
-                    <h2>${_('syslog')}</h2>
-                    % if not log_syslog:
-                            <pre>-- empty --</pre>
-                    % else:
-                            <pre>${format_log(log_syslog)}</pre>
-                    % endif
-                </div>
             </div>
             </div>
         </div>
         </div>
     </div>
     </div>
@@ -187,6 +148,10 @@ ${commonheader(_('Task Attempt: %(attemptId)s - Job Browser') % dict(attemptId=a
                 { "sWidth": "70%" }
                 { "sWidth": "70%" }
             ]
             ]
         });
         });
+
+        if (window.location.hash != null && window.location.hash.length > 1){
+            $('#tabs a[href="#'+window.location.hash.substring(2)+'"]').tab('show');
+        }
     });
     });
 </script>
 </script>
 ${commonfooter(messages)}
 ${commonfooter(messages)}

+ 131 - 0
apps/jobbrowser/src/jobbrowser/templates/attempt_logs.mako

@@ -0,0 +1,131 @@
+## Licensed to Cloudera, Inc. under one
+## or more contributor license agreements.  See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership.  Cloudera, Inc. 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.
+<%!
+  from desktop.views import commonheader, commonfooter
+  from django.utils.translation import ugettext as _
+%>
+<%namespace name="comps" file="jobbrowser_components.mako" />
+
+${commonheader(_('Task Attempt: %(attemptId)s - Job Browser') % dict(attemptId=attempt.attemptId), "jobbrowser")}
+<div class="container-fluid">
+    <h1>${_('Task Attempt: %(attemptId)s - Job Browser') % dict(attemptId=attempt.attemptId)}</h1>
+    <div class="row-fluid">
+        <div class="span2">
+            <div class="well sidebar-nav">
+                <ul class="nav nav-list">
+                    <li class="nav-header">${_('Attempt ID')}</li>
+                    <li>${attempt.attemptId_short}</li>
+                    <li class="nav-header">${_('Task')}</li>
+                    <li><a href="${url('jobbrowser.views.single_task', jobid=joblnk.jobId, taskid=taskid)}" title="${_('View this task')}">${task.taskId_short}</a>
+                    </li>
+                    <li class="nav-header">${_('Job')}</li>
+                    <li><a href="${url('jobbrowser.views.single_job', jobid=joblnk.jobId)}" title="${_('View this job')}">${joblnk.jobId_short}</a></li>
+                    <li class="nav-header">${_('Status')}</li>
+                    <li>
+                        <%
+                            status = attempt.state.lower()
+                        %>
+                        % if status == 'running' or status == 'pending':
+                                <span class="label label-warning">${status}</span>
+                        % elif status == 'succeeded':
+                                <span class="label label-success">${status}</span>
+                        % else:
+                                <span class="label">${status}</span>
+                        % endif
+                    </li>
+                </ul>
+            </div>
+        </div>
+
+        <div class="span10">
+            <ul class="nav nav-tabs">
+                <li><a href="${ url('jobbrowser.views.single_task_attempt', jobid=joblnk.jobId, taskid=task.taskId, attemptid=attempt.attemptId) }#tmetadata">${_('Metadata')}</a></li>
+                <li><a href="${ url('jobbrowser.views.single_task_attempt', jobid=joblnk.jobId, taskid=task.taskId, attemptid=attempt.attemptId) }#tcounters">${_('Counters')}</a></li>
+                <li class="active"><a href="#logs" data-toggle="tab">${_('Logs')}</a></li>
+            </ul>
+
+            <div class="tab-content">
+                <div class="tab-pane active" id="logs">
+                    <%
+                        log_diagnostic = logs[0]
+                        log_stdout = logs[1]
+                        log_stderr = logs[2]
+                        log_syslog = logs[3]
+                    %>
+                    <%def name="format_log(raw)">
+                        ## have to remove any indentation here or it breaks inside the pre tags
+                          % for line in raw.split('\n'):
+                              ${ line | h,trim }
+                        % endfor
+                    </%def>
+                    <h2>${_('task diagnostic log')}</h2>
+                    % if not log_diagnostic:
+                            <pre>-- empty --</pre>
+                    % else:
+                            <pre>${format_log(log_diagnostic)}</pre>
+                    % endif
+                    <h2>${_('stdout')}</h2>
+                    % if not log_stdout:
+                            <pre>-- empty --</pre>
+                    % else:
+                            <pre>${format_log(log_stdout)}</pre>
+                    % endif
+                    <h2>${_('stderr')}</h2>
+                    % if not log_stderr:
+                            <pre>-- empty --</pre>
+                    % else:
+                            <pre>${format_log(log_stderr)}</pre>
+                    % endif
+                    <h2>${_('syslog')}</h2>
+                    % if not log_syslog:
+                            <pre>-- empty --</pre>
+                    % else:
+                            <pre>${format_log(log_syslog)}</pre>
+                    % endif
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+
+<script type="text/javascript" charset="utf-8">
+    $(document).ready(function(){
+        $("#metadataTable").dataTable({
+            "bPaginate": false,
+            "bLengthChange": false,
+            "bInfo": false,
+            "bAutoWidth": false,
+            "bFilter": false,
+            "aoColumns": [
+                { "sWidth": "30%" },
+                { "sWidth": "70%" }
+            ]
+        });
+
+        $(".taskCountersTable").dataTable({
+            "bPaginate": false,
+            "bLengthChange": false,
+            "bInfo": false,
+            "bFilter": false,
+            "bAutoWidth": false,
+            "aoColumns": [
+                { "sWidth": "30%" },
+                { "sWidth": "70%" }
+            ]
+        });
+    });
+</script>
+${commonfooter(messages)}

+ 10 - 4
apps/jobbrowser/src/jobbrowser/templates/job.mako

@@ -30,6 +30,7 @@
     <table class="taskTable table table-striped table-condensed">
     <table class="taskTable table table-striped table-condensed">
         <thead>
         <thead>
         <tr>
         <tr>
+            <th>${_('Log')}</th>
             <th>${_('Tasks')}</th>
             <th>${_('Tasks')}</th>
             <th>${_('Type')}</th>
             <th>${_('Type')}</th>
         </tr>
         </tr>
@@ -37,6 +38,11 @@
         <tbody>
         <tbody>
             % for task in tasks:
             % for task in tasks:
             <tr>
             <tr>
+                <td data-row-selector-exclude="true">
+                %if task.taskAttemptIds:
+                    <a href="${ url('jobbrowser.views.single_task_attempt_logs', jobid=task.jobId, taskid=task.taskId, attemptid=task.taskAttemptIds[-1]) }" data-row-selector-exclude="true"><i class="icon-tasks"></i></a>
+                %endif
+                </td>
                 <td><a title="${_('View this task')}" href="${ url('jobbrowser.views.single_task', jobid=job.jobId, taskid=task.taskId) }" data-row-selector="true">${task.taskId_short}</a></td>
                 <td><a title="${_('View this task')}" href="${ url('jobbrowser.views.single_task', jobid=job.jobId, taskid=task.taskId) }" data-row-selector="true">${task.taskId_short}</a></td>
                 <td>${task.taskType}</td>
                 <td>${task.taskType}</td>
             </tr>
             </tr>
@@ -251,8 +257,6 @@ ${commonheader(_('Job: %(jobId)s - Job Browser') % dict(jobId=job.jobId), "jobbr
     </div>
     </div>
 </div>
 </div>
 
 
-
-
 <script type="text/javascript" charset="utf-8">
 <script type="text/javascript" charset="utf-8">
     $(document).ready(function(){
     $(document).ready(function(){
         $(".taskTable").dataTable({
         $(".taskTable").dataTable({
@@ -261,9 +265,11 @@ ${commonheader(_('Job: %(jobId)s - Job Browser') % dict(jobId=job.jobId), "jobbr
             "bInfo": false,
             "bInfo": false,
             "bAutoWidth": false,
             "bAutoWidth": false,
             "aoColumns": [
             "aoColumns": [
+                { "sWidth": "1%", "bSortable": false },
                 { "sWidth": "50%" },
                 { "sWidth": "50%" },
-                { "sWidth": "50%" }
-            ]
+                { "sWidth": "49%" }
+            ],
+            "aaSorting": [[ 1, "asc" ]]
         });
         });
         var _metadataTable = $("#metadataTable").dataTable({
         var _metadataTable = $("#metadataTable").dataTable({
             "bPaginate": false,
             "bPaginate": false,

+ 1 - 1
apps/jobbrowser/src/jobbrowser/templates/jobbrowser_components.mako

@@ -138,4 +138,4 @@
     endif
     endif
     return additional_class
     return additional_class
     %>
     %>
-</%def>
+</%def>

+ 4 - 4
apps/jobbrowser/src/jobbrowser/templates/jobs.mako

@@ -103,7 +103,7 @@ ${commonheader(_('Job Browser'), "jobbrowser", user)}
                     ${comps.get_status(job)}
                     ${comps.get_status(job)}
                 </a>
                 </a>
             </td>
             </td>
-            <td class="center">
+            <td>
                 <a href="${url('jobbrowser.views.jobs')}?${get_state_link(request, 'user', job.user.lower())}" title="${_('Show only %(status)s jobs') % dict(status=job.user.lower())}">${job.user}</a>
                 <a href="${url('jobbrowser.views.jobs')}?${get_state_link(request, 'user', job.user.lower())}" title="${_('Show only %(status)s jobs') % dict(status=job.user.lower())}">${job.user}</a>
             </td>
             </td>
             <td data-sort-value="${job.maps_percent_complete}">
             <td data-sort-value="${job.maps_percent_complete}">
@@ -120,9 +120,9 @@ ${commonheader(_('Job Browser'), "jobbrowser", user)}
                     ${comps.mr_graph_reduces(job)}
                     ${comps.mr_graph_reduces(job)}
                 % endif
                 % endif
             </td>
             </td>
-            <td class="center">${job.queueName}</td>
-            <td class="center">${job.priority.lower()}</td>
-            <td class="center" data-sort-value="${job.durationInMillis}" data-row-selector-exclude="true">
+            <td>${job.queueName}</td>
+            <td>${job.priority.lower()}</td>
+            <td data-sort-value="${job.durationInMillis}" data-row-selector-exclude="true">
                 % if job.is_retired:
                 % if job.is_retired:
                     ${_('N/A')}
                     ${_('N/A')}
                 % else:
                 % else:

+ 4 - 3
apps/jobbrowser/src/jobbrowser/templates/task.mako

@@ -56,6 +56,7 @@ ${commonheader(_('Job Task: %(taskId)s - Job Browser') % dict(taskId=task.taskId
                     <table id="attemptsTable" class="table table-striped table-condensed">
                     <table id="attemptsTable" class="table table-striped table-condensed">
                         <thead>
                         <thead>
                         <tr>
                         <tr>
+                            <th>${_('Log')}</th>
                             <th>${_('Attempt ID')}</th>
                             <th>${_('Attempt ID')}</th>
                             <th>${_('Progress')}</th>
                             <th>${_('Progress')}</th>
                             <th>${_('State')}</th>
                             <th>${_('State')}</th>
@@ -72,6 +73,7 @@ ${commonheader(_('Job Task: %(taskId)s - Job Browser') % dict(taskId=task.taskId
                         <tbody>
                         <tbody>
                                 % for attempt in task.attempts:
                                 % for attempt in task.attempts:
                                 <tr>
                                 <tr>
+                                    <td data-row-selector-exclude="true"><a href="${ url('jobbrowser.views.single_task_attempt_logs', jobid=joblnk.jobId, taskid=task.taskId, attemptid=attempt.attemptId) }" data-row-selector-exclude="true"><i class="icon-tasks"></i></a></td>
                                     <td><a title="${_('View this attempt')}"
                                     <td><a title="${_('View this attempt')}"
                                            href="${ url('jobbrowser.views.single_task_attempt', jobid=joblnk.jobId, taskid=task.taskId, attemptid=attempt.attemptId) }" data-row-selector="true">${attempt.attemptId_short}</a></td>
                                            href="${ url('jobbrowser.views.single_task_attempt', jobid=joblnk.jobId, taskid=task.taskId, attemptid=attempt.attemptId) }" data-row-selector="true">${attempt.attemptId_short}</a></td>
                                     <td>${"%d" % (attempt.progress * 100)}%</td>
                                     <td>${"%d" % (attempt.progress * 100)}%</td>
@@ -143,15 +145,14 @@ ${commonheader(_('Job Task: %(taskId)s - Job Browser') % dict(taskId=task.taskId
     </div>
     </div>
 </div>
 </div>
 
 
-
-
 <script type="text/javascript" charset="utf-8">
 <script type="text/javascript" charset="utf-8">
     $(document).ready(function(){
     $(document).ready(function(){
         $("#attemptsTable").dataTable({
         $("#attemptsTable").dataTable({
             "bPaginate": false,
             "bPaginate": false,
             "bLengthChange": false,
             "bLengthChange": false,
             "bInfo": false,
             "bInfo": false,
-            "bFilter": false
+            "bFilter": false,
+            "aaSorting": [[ 1, "asc" ]]
         });
         });
         $("#metadataTable").dataTable({
         $("#metadataTable").dataTable({
             "bPaginate": false,
             "bPaginate": false,

+ 8 - 1
apps/jobbrowser/src/jobbrowser/templates/tasks.mako

@@ -68,6 +68,7 @@ ${commonheader(_('Task View: Job: %(jobId)s - Job Browser') % dict(jobId=jobid),
         <table class="datatables table table-striped table-condensed">
         <table class="datatables table table-striped table-condensed">
             <thead>
             <thead>
             <tr>
             <tr>
+                <th>${_('Log')}</th>
                 <th>${_('Task ID')}</th>
                 <th>${_('Task ID')}</th>
                 <th>${_('Type')}</th>
                 <th>${_('Type')}</th>
                 <th>${_('Progress')}</th>
                 <th>${_('Progress')}</th>
@@ -81,6 +82,11 @@ ${commonheader(_('Task View: Job: %(jobId)s - Job Browser') % dict(jobId=jobid),
 	        <tbody>
 	        <tbody>
 	            %for t in page.object_list:
 	            %for t in page.object_list:
 	            <tr>
 	            <tr>
+                    <td data-row-selector-exclude="true">
+                        %if t.taskAttemptIds:
+                            <a href="${ url('jobbrowser.views.single_task_attempt_logs', jobid=t.jobId, taskid=t.taskId, attemptid=t.taskAttemptIds[-1]) }" data-row-selector-exclude="true"><i class="icon-tasks"></i></a>
+                        %endif
+                    </td>
 	                <td>${t.taskId_short}</td>
 	                <td>${t.taskId_short}</td>
 	                <td>${t.taskType}</td>
 	                <td>${t.taskType}</td>
 	                <td>
 	                <td>
@@ -109,7 +115,8 @@ ${commonheader(_('Task View: Job: %(jobId)s - Job Browser') % dict(jobId=jobid),
             "bPaginate": false,
             "bPaginate": false,
             "bLengthChange": false,
             "bLengthChange": false,
             "bFilter": false,
             "bFilter": false,
-            "bInfo": false
+            "bInfo": false,
+            "aaSorting": [[ 1, "asc" ]]
         });
         });
         $("a[data-row-selector='true']").jHueRowSelector();
         $("a[data-row-selector='true']").jHueRowSelector();
     });
     });

+ 2 - 0
apps/jobbrowser/src/jobbrowser/urls.py

@@ -34,6 +34,8 @@ urlpatterns = patterns('jobbrowser.views',
       'single_task_attempt',name='single_task_attempt'),
       'single_task_attempt',name='single_task_attempt'),
   url(r'^jobs/(?P<jobid>\w+)/tasks/(?P<taskid>\w+)/attempts/(?P<attemptid>\w+)/counters$',
   url(r'^jobs/(?P<jobid>\w+)/tasks/(?P<taskid>\w+)/attempts/(?P<attemptid>\w+)/counters$',
       'task_attempt_counters',name='task_attempt_counters'),
       'task_attempt_counters',name='task_attempt_counters'),
+  url(r'^jobs/(?P<jobid>\w+)/tasks/(?P<taskid>\w+)/attempts/(?P<attemptid>\w+)/logs$',
+      'single_task_attempt_logs',name='single_task_attempt_logs'),
   url(r'^jobs/(\w+)/tasks/(\w+)/attempts/(?P<attemptid>\w+)/kill$',
   url(r'^jobs/(\w+)/tasks/(\w+)/attempts/(?P<attemptid>\w+)/kill$',
       'kill_task_attempt',name='kill_task_attempt'),
       'kill_task_attempt',name='kill_task_attempt'),
   url(r'^clusterstatus$', 'clusterstatus',name='clusterstatus'),
   url(r'^clusterstatus$', 'clusterstatus',name='clusterstatus'),

+ 21 - 1
apps/jobbrowser/src/jobbrowser/views.py

@@ -234,6 +234,26 @@ def single_task_attempt(request, jobid, taskid, attemptid):
   except KeyError:
   except KeyError:
     raise KeyError(_("Cannot find attempt '%(id)s' in task") % dict(id=attemptid))
     raise KeyError(_("Cannot find attempt '%(id)s' in task") % dict(id=attemptid))
 
 
+  return render("attempt.mako", request,
+    {
+      "attempt":attempt,
+      "taskid":taskid,
+      "joblnk": job_link,
+      "task": task
+    })
+
+@check_job_permission
+def single_task_attempt_logs(request, jobid, taskid, attemptid):
+  """
+  We get here from /jobs/jobid/tasks/taskid/attempts/attemptid/logs
+  """
+  job_link = JobLinkage(request.jt, jobid)
+  task = job_link.get_task(taskid)
+  try:
+    attempt = task.get_attempt(attemptid)
+  except KeyError:
+    raise KeyError(_("Cannot find attempt '%(id)s' in task") % dict(id=attemptid))
+
   try:
   try:
     # Add a diagnostic log
     # Add a diagnostic log
     diagnostic_log = ", ".join(task.diagnosticMap[attempt.attemptId])
     diagnostic_log = ", ".join(task.diagnosticMap[attempt.attemptId])
@@ -244,7 +264,7 @@ def single_task_attempt(request, jobid, taskid, attemptid):
     # Four entries,
     # Four entries,
     # for diagnostic, stdout, stderr and syslog
     # for diagnostic, stdout, stderr and syslog
     logs = [ _("Failed to retrieve log. TaskTracker not found.") ] * 4
     logs = [ _("Failed to retrieve log. TaskTracker not found.") ] * 4
-  return render("attempt.mako", request,
+  return render("attempt_logs.mako", request,
     {
     {
       "attempt":attempt,
       "attempt":attempt,
       "taskid":taskid,
       "taskid":taskid,