</ul>
</div>
{% end %}
+
+ {# Footer with some useful buttons #}
+ <footer class="card-footer">
+ {% if job.has_log() %}
+ <a href="/jobs/{{ job.uuid }}/log" class="card-footer-item">
+ {{ _("Download Log") }}
+ </a>
+ {% end %}
+ </footer>
</div>
</div>
{% end %}
(r"/queue", jobs.QueueHandler),
# Jobs
+ (r"/jobs/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/log", jobs.LogHandler),
(r"/job/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/abort", jobs.JobAbortHandler),
(r"/job/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/buildroot", jobs.JobBuildrootHandler),
self.render("queue.html", queue=self.backend.jobqueue)
+class LogHandler(base.BaseHandler):
+ async def get(self, uuid):
+ job = self.backend.jobs.get_by_uuid(uuid)
+ if not job:
+ raise tornado.web.HTTPError("Could not find job %s" % uuid)
+
+ tail = self.get_argument_int("tail", None)
+
+ # Should we tail the log, or stream the entire file?
+ try:
+ log = await job.tail_log(tail) if tail else await job.open_log()
+
+ # Send 404 if there is no log file
+ except FileNotFoundError as e:
+ raise tornado.web.HTTPError(404, "Could not find log for %s" % job) from e
+
+ # Set Content-Type header
+ self.set_header("Content-Type", "text/plain")
+
+ # Stream the entire log
+ for line in log:
+ self.write(line)
+
+
class JobBuildrootHandler(base.BaseHandler):
def get(self, uuid):
job = self.backend.jobs.get_by_uuid(uuid)