mirror of
https://github.com/cuigh/swirl
synced 2024-12-28 14:51:57 +00:00
Add task logs page
This commit is contained in:
parent
f08ec4a142
commit
81081c8164
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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"},
|
||||
},
|
||||
},
|
||||
|
@ -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
103
views/task/logs.jet
Normal 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 }}
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user