mirror of
https://github.com/cuigh/swirl
synced 2024-12-31 16:23:13 +00:00
Add service logs page
This commit is contained in:
parent
605706b235
commit
6ce9d3358e
File diff suppressed because one or more lines are too long
@ -7,6 +7,13 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/cuigh/swirl/misc"
|
"github.com/cuigh/swirl/misc"
|
||||||
"github.com/cuigh/swirl/model"
|
"github.com/cuigh/swirl/model"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
@ -513,3 +520,40 @@ func ServiceRemove(name string) error {
|
|||||||
return cli.ServiceRemove(ctx, name)
|
return cli.ServiceRemove(ctx, name)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ServiceLogs returns the logs generated by a service.
|
||||||
|
func ServiceLogs(name string, line int, timestamps bool) (stdout, stderr *bytes.Buffer, err error) {
|
||||||
|
err = mgr.Do(func(ctx context.Context, cli *client.Client) (err error) {
|
||||||
|
var (
|
||||||
|
rc io.ReadCloser
|
||||||
|
buf []byte
|
||||||
|
)
|
||||||
|
opts := types.ContainerLogsOptions{
|
||||||
|
ShowStdout: true,
|
||||||
|
ShowStderr: true,
|
||||||
|
Tail: strconv.Itoa(line),
|
||||||
|
Timestamps: timestamps,
|
||||||
|
//Since: (time.Hour * 24).String()
|
||||||
|
}
|
||||||
|
if rc, err = cli.ServiceLogs(ctx, name, opts); err == nil {
|
||||||
|
defer rc.Close()
|
||||||
|
|
||||||
|
stdout = &bytes.Buffer{}
|
||||||
|
stderr = &bytes.Buffer{}
|
||||||
|
scanner := bufio.NewScanner(rc)
|
||||||
|
for scanner.Scan() {
|
||||||
|
buf = scanner.Bytes()
|
||||||
|
if buf[0] == 1 {
|
||||||
|
stdout.Write(buf[8:])
|
||||||
|
stdout.WriteByte('\n')
|
||||||
|
} else if buf[0] == 2 {
|
||||||
|
stdout.Write(buf[8:])
|
||||||
|
stdout.WriteByte('\n')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = scanner.Err()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
@ -19,6 +19,7 @@ type ServiceController struct {
|
|||||||
List web.HandlerFunc `path:"/" name:"service.list" authorize:"!" desc:"service list page"`
|
List web.HandlerFunc `path:"/" name:"service.list" authorize:"!" desc:"service list page"`
|
||||||
Detail web.HandlerFunc `path:"/:name/detail" name:"service.detail" authorize:"!" desc:"service detail page"`
|
Detail web.HandlerFunc `path:"/:name/detail" name:"service.detail" authorize:"!" desc:"service detail page"`
|
||||||
Raw web.HandlerFunc `path:"/:name/raw" name:"service.raw" authorize:"!" desc:"service raw page"`
|
Raw web.HandlerFunc `path:"/:name/raw" name:"service.raw" authorize:"!" desc:"service raw page"`
|
||||||
|
Logs web.HandlerFunc `path:"/:name/logs" name:"service.logs" authorize:"!" desc:"service logs page"`
|
||||||
Delete web.HandlerFunc `path:"/delete" method:"post" name:"service.delete" authorize:"!" desc:"delete service"`
|
Delete web.HandlerFunc `path:"/delete" method:"post" name:"service.delete" authorize:"!" desc:"delete service"`
|
||||||
Scale web.HandlerFunc `path:"/scale" method:"post" name:"service.scale" authorize:"!" desc:"scale service"`
|
Scale web.HandlerFunc `path:"/scale" method:"post" name:"service.scale" authorize:"!" desc:"scale service"`
|
||||||
New web.HandlerFunc `path:"/new" name:"service.new" authorize:"!" desc:"new service page"`
|
New web.HandlerFunc `path:"/new" name:"service.new" authorize:"!" desc:"new service page"`
|
||||||
@ -86,6 +87,20 @@ func Service() (c *ServiceController) {
|
|||||||
return ctx.Render("service/raw", m)
|
return ctx.Render("service/raw", m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.Logs = func(ctx web.Context) error {
|
||||||
|
name := ctx.P("name")
|
||||||
|
line := cast.ToIntD(ctx.Q("line"), 500)
|
||||||
|
timestamps := cast.ToBoolD(ctx.Q("timestamps"), false)
|
||||||
|
stdout, stderr, err := docker.ServiceLogs(name, line, timestamps)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
m := newModel(ctx).Add("Service", name).Add("Line", line).Add("Timestamps", timestamps).
|
||||||
|
Add("Stdout", stdout.String()).Add("Stderr", stderr.String())
|
||||||
|
return ctx.Render("service/logs", m)
|
||||||
|
}
|
||||||
|
|
||||||
c.Delete = func(ctx web.Context) error {
|
c.Delete = func(ctx web.Context) error {
|
||||||
names := strings.Split(ctx.F("names"), ",")
|
names := strings.Split(ctx.F("names"), ",")
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
|
@ -9,7 +9,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
// Version is the version of Swirl
|
// Version is the version of Swirl
|
||||||
Version = "0.5.1"
|
Version = "0.5.2"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -50,6 +50,7 @@ var Perms = []PermGroup{
|
|||||||
{Key: "service.new", Text: "View new"},
|
{Key: "service.new", Text: "View new"},
|
||||||
{Key: "service.detail", Text: "View detail"},
|
{Key: "service.detail", Text: "View detail"},
|
||||||
{Key: "service.raw", Text: "View raw"},
|
{Key: "service.raw", Text: "View raw"},
|
||||||
|
{Key: "service.logs", Text: "View logs"},
|
||||||
{Key: "service.edit", Text: "View edit"},
|
{Key: "service.edit", Text: "View edit"},
|
||||||
{Key: "service.create", Text: "Create"},
|
{Key: "service.create", Text: "Create"},
|
||||||
{Key: "service.delete", Text: "Delete"},
|
{Key: "service.delete", Text: "Delete"},
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
<div class="navbar-brand">
|
<div class="navbar-brand">
|
||||||
<a class="navbar-item is-tab is-active" href="/service/{{.Service.Spec.Name}}/detail">Detail</a>
|
<a class="navbar-item is-tab is-active" href="/service/{{.Service.Spec.Name}}/detail">Detail</a>
|
||||||
<a class="navbar-item is-tab" href="/service/{{.Service.Spec.Name}}/raw">Raw</a>
|
<a class="navbar-item is-tab" href="/service/{{.Service.Spec.Name}}/raw">Raw</a>
|
||||||
|
<a class="navbar-item is-tab" href="/service/{{.Service.Spec.Name}}/logs">Logs</a>
|
||||||
<a class="navbar-item is-tab" href="/service/{{.Service.Spec.Name}}/edit">Edit</a>
|
<a class="navbar-item is-tab" href="/service/{{.Service.Spec.Name}}/edit">Edit</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -91,6 +91,7 @@
|
|||||||
<div class="navbar-brand">
|
<div class="navbar-brand">
|
||||||
<a class="navbar-item is-tab" href="/service/{{.Service.Name}}/detail">Detail</a>
|
<a class="navbar-item is-tab" href="/service/{{.Service.Name}}/detail">Detail</a>
|
||||||
<a class="navbar-item is-tab" href="/service/{{.Service.Name}}/raw">Raw</a>
|
<a class="navbar-item is-tab" href="/service/{{.Service.Name}}/raw">Raw</a>
|
||||||
|
<a class="navbar-item is-tab" href="/service/{{.Service.Name}}/logs">Logs</a>
|
||||||
<a class="navbar-item is-tab is-active" href="/service/{{.Service.Name}}/edit">Edit</a>
|
<a class="navbar-item is-tab is-active" href="/service/{{.Service.Name}}/edit">Edit</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
100
views/service/logs.jet
Normal file
100
views/service/logs.jet
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
{{ 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">
|
||||||
|
SERVICE
|
||||||
|
</h1>
|
||||||
|
<h2 class="subtitle is-5">
|
||||||
|
Services are the definitions of tasks to run on a swarm.
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<nav class="breadcrumb has-succeeds-separator is-small is-marginless" aria-label="breadcrumbs">
|
||||||
|
<ul>
|
||||||
|
<li><a href="/">Dashboard</a></li>
|
||||||
|
<li><a href="/service/">Services</a></li>
|
||||||
|
<li class="is-active"><a>Logs</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section class="hero is-small is-light">
|
||||||
|
<div class="hero-body">
|
||||||
|
<div class="container">
|
||||||
|
<h2 class="title is-2">
|
||||||
|
{{ .Service }}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<nav class="navbar has-shadow">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand">
|
||||||
|
<a class="navbar-item is-tab" href="/service/{{.Service}}/detail">Detail</a>
|
||||||
|
<a class="navbar-item is-tab" href="/service/{{.Service}}/raw">Raw</a>
|
||||||
|
<a class="navbar-item is-tab is-active" href="/service/{{.Service}}/logs">Logs</a>
|
||||||
|
<a class="navbar-item is-tab" href="/service/{{.Service}}/edit">Edit</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">Search</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="tabs is-toggle is-fullwidth is-marginless" 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">
|
||||||
|
<pre class="is-marginless" style="max-height: 600px; padding: 0.75em">{{ .Stdout }}</pre>
|
||||||
|
<pre class="is-marginless" style="max-height: 600px; padding: 0.75em; display: none">{{ .Stderr }}</pre>
|
||||||
|
</div>
|
||||||
|
<a href="/service/" class="button is-primary">
|
||||||
|
<span class="icon"><i class="fa fa-reply"></i></span>
|
||||||
|
<span>Return</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{{ end }}
|
@ -48,6 +48,7 @@
|
|||||||
<div class="navbar-brand">
|
<div class="navbar-brand">
|
||||||
<a class="navbar-item is-tab" href="/service/{{.Service}}/detail">Detail</a>
|
<a class="navbar-item is-tab" href="/service/{{.Service}}/detail">Detail</a>
|
||||||
<a class="navbar-item is-tab is-active" href="/service/{{.Service}}/raw">Raw</a>
|
<a class="navbar-item is-tab is-active" href="/service/{{.Service}}/raw">Raw</a>
|
||||||
|
<a class="navbar-item is-tab" href="/service/{{.Service}}/logs">Logs</a>
|
||||||
<a class="navbar-item is-tab" href="/service/{{.Service}}/edit">Edit</a>
|
<a class="navbar-item is-tab" href="/service/{{.Service}}/edit">Edit</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user