Add task logs page

This commit is contained in:
cuigh 2018-02-23 11:06:45 +08:00
parent f08ec4a142
commit 81081c8164
6 changed files with 157 additions and 5 deletions

View File

@ -1,10 +1,12 @@
package docker
import (
"bytes"
"context"
"io"
"math"
"sort"
"strconv"
"github.com/cuigh/swirl/misc"
"github.com/cuigh/swirl/model"
@ -12,6 +14,7 @@ import (
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/client"
"github.com/docker/docker/pkg/stdcopy"
)
// TaskList return all running tasks of a service or a node.
@ -88,11 +91,31 @@ func TaskInspect(id string) (task swarm.Task, raw []byte, err error) {
// TaskLogs returns the logs generated by a task in an io.ReadCloser.
// It's up to the caller to close the stream.
func TaskLogs(id string) (rc io.ReadCloser, err error) {
err = mgr.Do(func(ctx context.Context, cli *client.Client) (err error) {
opts := types.ContainerLogsOptions{}
rc, err = cli.TaskLogs(ctx, id, opts)
func TaskLogs(id string, line int, timestamps bool) (stdout, stderr *bytes.Buffer, err error) {
var (
ctx context.Context
cli *client.Client
rc io.ReadCloser
)
ctx, cli, err = mgr.Client()
if err != nil {
return
})
}
opts := types.ContainerLogsOptions{
ShowStdout: true,
ShowStderr: true,
Tail: strconv.Itoa(line),
Timestamps: timestamps,
//Since: (time.Hour * 24).String()
}
if rc, err = cli.TaskLogs(ctx, id, opts); err == nil {
defer rc.Close()
stdout = &bytes.Buffer{}
stderr = &bytes.Buffer{}
_, err = stdcopy.StdCopy(stdout, stderr, rc)
}
return
}

View File

@ -2,6 +2,7 @@ package controller
import (
"github.com/cuigh/auxo/net/web"
"github.com/cuigh/auxo/util/cast"
"github.com/cuigh/swirl/biz/docker"
"github.com/cuigh/swirl/misc"
"github.com/cuigh/swirl/model"
@ -12,6 +13,7 @@ type TaskController struct {
List web.HandlerFunc `path:"/" name:"task.list" authorize:"!" desc:"task list page"`
Detail web.HandlerFunc `path:"/:id/detail" name:"task.detail" authorize:"!" desc:"task detail page"`
Raw web.HandlerFunc `path:"/:id/raw" name:"task.raw" authorize:"!" desc:"task raw page"`
Logs web.HandlerFunc `path:"/:id/logs" name:"task.logs" authorize:"!" desc:"task logs page"`
}
// Task creates an instance of TaskController
@ -20,6 +22,7 @@ func Task() (c *TaskController) {
List: taskList,
Detail: taskDetail,
Raw: taskRaw,
Logs: taskLogs,
}
}
@ -71,3 +74,22 @@ func taskRaw(ctx web.Context) error {
m := newModel(ctx).Set("Task", task).Set("Raw", j)
return ctx.Render("task/raw", m)
}
func taskLogs(ctx web.Context) error {
id := ctx.P("id")
task, _, err := docker.TaskInspect(id)
if err != nil {
return err
}
line := cast.ToInt(ctx.Q("line"), 500)
timestamps := cast.ToBool(ctx.Q("timestamps"), false)
stdout, stderr, err := docker.TaskLogs(id, line, timestamps)
if err != nil {
return err
}
m := newModel(ctx).Set("Task", task).Set("Line", line).Set("Timestamps", timestamps).
Set("Stdout", stdout.String()).Set("Stderr", stderr.String())
return ctx.Render("task/logs", m)
}

View File

@ -91,6 +91,7 @@ var Perms = []PermGroup{
{Key: "task.list", Text: "View list"},
{Key: "task.detail", Text: "View detail"},
{Key: "task.raw", Text: "View raw"},
{Key: "task.logs", Text: "View logs"},
},
},
{
@ -108,6 +109,7 @@ var Perms = []PermGroup{
{Key: "container.list", Text: "View list"},
{Key: "container.detail", Text: "View detail"},
{Key: "container.raw", Text: "View raw"},
{Key: "container.logs", Text: "View logs"},
{Key: "container.delete", Text: "Delete"},
},
},

View File

@ -24,6 +24,7 @@
<div class="navbar-brand">
<a class="navbar-item is-tab is-active" href="/task/{{.Task.ID}}/detail">{{ i18n("menu.detail") }}</a>
<a class="navbar-item is-tab " href="/task/{{.Task.ID}}/raw">{{ i18n("menu.raw") }}</a>
<a class="navbar-item is-tab" href="/task/{{.Task.ID}}/logs">{{ i18n("menu.log") }}</a>
</div>
</div>
</nav>

103
views/task/logs.jet Normal file
View File

@ -0,0 +1,103 @@
{{ extends "../_layouts/default" }}
{{ block body() }}
<section class="hero is-info">
<div class="hero-body">
<div class="container has-text-centered">
<h1 class="title is-2 is-uppercase">{{ i18n("container.title") }}</h1>
<h2 class="subtitle is-5">{{ i18n("container.description") }}</h2>
</div>
</div>
</section>
<div class="container">
<nav class="breadcrumb has-succeeds-separator is-small is-marginless" aria-label="breadcrumbs">
<ul>
<li><a href="/">{{ i18n("menu.dashboard") }}</a></li>
<li><a href="/task/">{{ i18n("menu.task") }}</a></li>
<li class="is-active"><a>{{ i18n("menu.log") }}</a></li>
</ul>
</nav>
</div>
<section class="hero is-small is-light">
<div class="hero-body">
<div class="container">
<h2 class="title is-2">
{{ .Task.Name ? .Task.Name : .Task.ID }}
</h2>
</div>
</div>
</section>
<nav class="navbar has-shadow">
<div class="container">
<div class="navbar-brand">
<a class="navbar-item is-tab" href="/task/{{.Task.ID}}/detail">{{ i18n("menu.detail") }}</a>
<a class="navbar-item is-tab " href="/task/{{.Task.ID}}/raw">{{ i18n("menu.raw") }}</a>
<a class="navbar-item is-tab is-active" href="/task/{{.Task.ID}}/logs">{{ i18n("menu.log") }}</a>
</div>
</div>
</nav>
<section class="section">
<div class="container">
<nav class="level">
<form>
<div class="level-left">
<div class="level-item">
<div class="field has-addons">
<p class="control">
<a class="button is-static">Lines</a>
</p>
<p class="control">
<input name="line" value="{{ .Line }}" class="input" placeholder="Max lines from tail">
</p>
</div>
</div>
<div class="level-item">
<div class="field">
<input id="cb-timestamps" name="timestamps" value="true" type="checkbox" class="switch is-success is-rounded"{{if .Timestamps}} checked{{end}}>
<label for="cb-timestamps">Add timestamps</label>
</div>
</div>
<div class="level-item">
<div class="field">
<p class="control">
<button class="button is-primary">{{ i18n("button.search") }}</button>
</p>
</div>
</div>
</div>
</form>
</nav>
<div class="tabs is-boxed" data-target="tab-content">
<ul>
<li class="is-active">
<a><span>Stdout</span></a>
</li>
<li>
<a><span>Stderr</span></a>
</li>
</ul>
</div>
<div id="tab-content" class="content">
<div class="field">
<div class="control">
{{ if .Stdout }}<textarea class="textarea code is-small" rows="30" readonly>{{ .Stdout }}</textarea>{{ end }}
</div>
</div>
<div class="field" style="display: none">
<div class="control">
{{ if .Stderr }}<textarea class="textarea code is-small" rows="30" readonly>{{ .Stderr }}</textarea>{{ end }}
</div>
</div>
</div>
<a href="/task/" class="button is-primary">
<span class="icon"><i class="fas fa-reply"></i></span>
<span>{{ i18n("button.return") }}</span>
</a>
</div>
</section>
{{ end }}

View File

@ -33,6 +33,7 @@
<div class="navbar-brand">
<a class="navbar-item is-tab" href="/task/{{.Task.ID}}/detail">{{ i18n("menu.detail") }}</a>
<a class="navbar-item is-tab is-active" href="/task/{{.Task.ID}}/raw">{{ i18n("menu.raw") }}</a>
<a class="navbar-item is-tab" href="/task/{{.Task.ID}}/logs">{{ i18n("menu.log") }}</a>
</div>
</div>
</nav>