mirror of
https://github.com/cuigh/swirl
synced 2024-12-30 15:53:24 +00:00
Support refreshing logs automatically
This commit is contained in:
parent
5746d9430a
commit
dee5cd38b7
@ -2681,6 +2681,49 @@ var Swirl;
|
||||
})(Stack = Swirl.Stack || (Swirl.Stack = {}));
|
||||
})(Swirl || (Swirl = {}));
|
||||
var Swirl;
|
||||
(function (Swirl) {
|
||||
var Task;
|
||||
(function (Task) {
|
||||
class LogsPage {
|
||||
constructor() {
|
||||
this.refreshInterval = 3000;
|
||||
this.$line = $("#txt-line");
|
||||
this.$timestamps = $("#cb-timestamps");
|
||||
this.$refresh = $("#cb-refresh");
|
||||
this.$stdout = $("#txt-stdout");
|
||||
this.$stderr = $("#txt-stderr");
|
||||
this.$refresh.change(e => {
|
||||
let elem = (e.target);
|
||||
if (elem.checked) {
|
||||
this.refreshData();
|
||||
}
|
||||
else if (this.timer > 0) {
|
||||
window.clearTimeout(this.timer);
|
||||
this.timer = 0;
|
||||
}
|
||||
});
|
||||
this.refreshData();
|
||||
}
|
||||
refreshData() {
|
||||
let args = {
|
||||
line: this.$line.val(),
|
||||
timestamps: this.$timestamps.prop("checked"),
|
||||
};
|
||||
$ajax.get('fetch_logs', args).json((r) => {
|
||||
this.$stdout.val(r.stdout);
|
||||
this.$stderr.val(r.stderr);
|
||||
this.$stdout.get(0).scrollTop = this.$stdout.get(0).scrollHeight;
|
||||
this.$stderr.get(0).scrollTop = this.$stderr.get(0).scrollHeight;
|
||||
});
|
||||
if (this.$refresh.prop("checked")) {
|
||||
this.timer = setTimeout(this.refreshData.bind(this), this.refreshInterval);
|
||||
}
|
||||
}
|
||||
}
|
||||
Task.LogsPage = LogsPage;
|
||||
})(Task = Swirl.Task || (Swirl.Task = {}));
|
||||
})(Swirl || (Swirl = {}));
|
||||
var Swirl;
|
||||
(function (Swirl) {
|
||||
var User;
|
||||
(function (User) {
|
||||
@ -2792,4 +2835,47 @@ var Swirl;
|
||||
Volume.NewPage = NewPage;
|
||||
})(Volume = Swirl.Volume || (Swirl.Volume = {}));
|
||||
})(Swirl || (Swirl = {}));
|
||||
var Swirl;
|
||||
(function (Swirl) {
|
||||
var Service;
|
||||
(function (Service) {
|
||||
class LogsPage {
|
||||
constructor() {
|
||||
this.refreshInterval = 3000;
|
||||
this.$line = $("#txt-line");
|
||||
this.$timestamps = $("#cb-timestamps");
|
||||
this.$refresh = $("#cb-refresh");
|
||||
this.$stdout = $("#txt-stdout");
|
||||
this.$stderr = $("#txt-stderr");
|
||||
this.$refresh.change(e => {
|
||||
let elem = (e.target);
|
||||
if (elem.checked) {
|
||||
this.refreshData();
|
||||
}
|
||||
else if (this.timer > 0) {
|
||||
window.clearTimeout(this.timer);
|
||||
this.timer = 0;
|
||||
}
|
||||
});
|
||||
this.refreshData();
|
||||
}
|
||||
refreshData() {
|
||||
let args = {
|
||||
line: this.$line.val(),
|
||||
timestamps: this.$timestamps.prop("checked"),
|
||||
};
|
||||
$ajax.get('fetch_logs', args).json((r) => {
|
||||
this.$stdout.val(r.stdout);
|
||||
this.$stderr.val(r.stderr);
|
||||
this.$stdout.get(0).scrollTop = this.$stdout.get(0).scrollHeight;
|
||||
this.$stderr.get(0).scrollTop = this.$stderr.get(0).scrollHeight;
|
||||
});
|
||||
if (this.$refresh.prop("checked")) {
|
||||
this.timer = setTimeout(this.refreshData.bind(this), this.refreshInterval);
|
||||
}
|
||||
}
|
||||
}
|
||||
Service.LogsPage = LogsPage;
|
||||
})(Service = Swirl.Service || (Swirl.Service = {}));
|
||||
})(Swirl || (Swirl = {}));
|
||||
//# sourceMappingURL=swirl.js.map
|
File diff suppressed because one or more lines are too long
53
assets/swirl/ts/task/logs.ts
Normal file
53
assets/swirl/ts/task/logs.ts
Normal file
@ -0,0 +1,53 @@
|
||||
///<reference path="../core/core.ts" />
|
||||
namespace Swirl.Task {
|
||||
export class LogsPage {
|
||||
private $stdout: JQuery;
|
||||
private $stderr: JQuery;
|
||||
private $line: JQuery;
|
||||
private $timestamps: JQuery;
|
||||
private $refresh: JQuery;
|
||||
private timer: number;
|
||||
private refreshInterval = 3000;
|
||||
|
||||
constructor() {
|
||||
this.$line = $("#txt-line");
|
||||
this.$timestamps = $("#cb-timestamps");
|
||||
this.$refresh = $("#cb-refresh");
|
||||
this.$stdout = $("#txt-stdout");
|
||||
this.$stderr = $("#txt-stderr");
|
||||
|
||||
this.$refresh.change(e => {
|
||||
let elem = <HTMLInputElement>(e.target);
|
||||
if (elem.checked) {
|
||||
this.refreshData();
|
||||
} else if (this.timer > 0) {
|
||||
window.clearTimeout(this.timer);
|
||||
this.timer = 0;
|
||||
}
|
||||
});
|
||||
|
||||
this.refreshData();
|
||||
// let ws = new WebSocket("ws://" + location.host + location.pathname + "_ws");
|
||||
// ws.onopen = e => console.log("open");
|
||||
// ws.onclose = e => console.log("close");
|
||||
// ws.onmessage = e => console.log("message: " + e.data);
|
||||
}
|
||||
|
||||
private refreshData() {
|
||||
let args: any = {
|
||||
line: this.$line.val(),
|
||||
timestamps: this.$timestamps.prop("checked"),
|
||||
};
|
||||
$ajax.get('fetch_logs', args).json((r: any) => {
|
||||
this.$stdout.val(r.stdout);
|
||||
this.$stderr.val(r.stderr);
|
||||
this.$stdout.get(0).scrollTop = this.$stdout.get(0).scrollHeight;
|
||||
this.$stderr.get(0).scrollTop = this.$stderr.get(0).scrollHeight;
|
||||
});
|
||||
|
||||
if (this.$refresh.prop("checked")) {
|
||||
this.timer = setTimeout(this.refreshData.bind(this), this.refreshInterval);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ package controller
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/cuigh/auxo/data"
|
||||
"github.com/cuigh/auxo/net/web"
|
||||
"github.com/cuigh/auxo/util/cast"
|
||||
"github.com/cuigh/swirl/biz/docker"
|
||||
@ -16,6 +17,7 @@ type ContainerController struct {
|
||||
Detail web.HandlerFunc `path:"/:id/detail" name:"container.detail" authorize:"!" desc:"container detail page"`
|
||||
Raw web.HandlerFunc `path:"/:id/raw" name:"container.raw" authorize:"!" desc:"container raw page"`
|
||||
Logs web.HandlerFunc `path:"/:id/logs" name:"container.logs" authorize:"!" desc:"container logs page"`
|
||||
FetchLogs web.HandlerFunc `path:"/:id/fetch_logs" name:"container.fetch_logs" authorize:"?" desc:"fetch container logs"`
|
||||
Delete web.HandlerFunc `path:"/delete" method:"post" name:"container.delete" authorize:"!" desc:"delete container"`
|
||||
}
|
||||
|
||||
@ -27,6 +29,7 @@ func Container() (c *ContainerController) {
|
||||
Raw: containerRaw,
|
||||
Logs: containerLogs,
|
||||
Delete: containerDelete,
|
||||
FetchLogs: containerFetchLogs,
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,16 +90,23 @@ func containerLogs(ctx web.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
m := newModel(ctx).Set("Container", container)
|
||||
return ctx.Render("container/logs", m)
|
||||
}
|
||||
|
||||
func containerFetchLogs(ctx web.Context) error {
|
||||
id := ctx.P("id")
|
||||
line := cast.ToInt(ctx.Q("line"), 500)
|
||||
timestamps := cast.ToBool(ctx.Q("timestamps"), false)
|
||||
stdout, stderr, err := docker.ContainerLogs(id, line, timestamps)
|
||||
if err != nil {
|
||||
return err
|
||||
return ajaxResult(ctx, err)
|
||||
}
|
||||
|
||||
m := newModel(ctx).Set("Container", container).Set("Line", line).Set("Timestamps", timestamps).
|
||||
Set("Stdout", stdout.String()).Set("Stderr", stderr.String())
|
||||
return ctx.Render("container/logs", m)
|
||||
return ctx.JSON(data.Map{
|
||||
"stdout": stdout.String(),
|
||||
"stderr": stderr.String(),
|
||||
})
|
||||
}
|
||||
|
||||
func containerDelete(ctx web.Context) error {
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cuigh/auxo/data"
|
||||
"github.com/cuigh/auxo/data/set"
|
||||
"github.com/cuigh/auxo/errors"
|
||||
"github.com/cuigh/auxo/net/web"
|
||||
@ -21,6 +22,7 @@ type ServiceController struct {
|
||||
Detail web.HandlerFunc `path:"/:name/detail" name:"service.detail" authorize:"!" perm:"read,service,name"`
|
||||
Raw web.HandlerFunc `path:"/:name/raw" name:"service.raw" authorize:"!" perm:"read,service,name"`
|
||||
Logs web.HandlerFunc `path:"/:name/logs" name:"service.logs" authorize:"!" perm:"read,service,name"`
|
||||
FetchLogs web.HandlerFunc `path:"/:name/fetch_logs" name:"service.fetch_logs" authorize:"?" desc:"fetch service logs"`
|
||||
Delete web.HandlerFunc `path:"/:name/delete" method:"post" name:"service.delete" authorize:"!" perm:"write,service,name"`
|
||||
Scale web.HandlerFunc `path:"/:name/scale" method:"post" name:"service.scale" authorize:"!" perm:"write,service,name"`
|
||||
Rollback web.HandlerFunc `path:"/:name/rollback" method:"post" name:"service.rollback" authorize:"!" perm:"write,service,name"`
|
||||
@ -41,6 +43,7 @@ func Service() (c *ServiceController) {
|
||||
Detail: serviceDetail,
|
||||
Raw: serviceRaw,
|
||||
Logs: serviceLogs,
|
||||
FetchLogs: serviceFetchLogs,
|
||||
Delete: serviceDelete,
|
||||
New: serviceNew,
|
||||
Create: serviceCreate,
|
||||
@ -121,17 +124,24 @@ func serviceRaw(ctx web.Context) error {
|
||||
}
|
||||
|
||||
func serviceLogs(ctx web.Context) error {
|
||||
name := ctx.P("name")
|
||||
m := newModel(ctx).Set("Service", name)
|
||||
return ctx.Render("service/logs", m)
|
||||
}
|
||||
|
||||
func serviceFetchLogs(ctx web.Context) error {
|
||||
name := ctx.P("name")
|
||||
line := cast.ToInt(ctx.Q("line"), 500)
|
||||
timestamps := cast.ToBool(ctx.Q("timestamps"), false)
|
||||
stdout, stderr, err := docker.ServiceLogs(name, line, timestamps)
|
||||
if err != nil {
|
||||
return err
|
||||
return ajaxResult(ctx, err)
|
||||
}
|
||||
|
||||
m := newModel(ctx).Set("Service", name).Set("Line", line).Set("Timestamps", timestamps).
|
||||
Set("Stdout", stdout.String()).Set("Stderr", stderr.String())
|
||||
return ctx.Render("service/logs", m)
|
||||
return ctx.JSON(data.Map{
|
||||
"stdout": stdout.String(),
|
||||
"stderr": stderr.String(),
|
||||
})
|
||||
}
|
||||
|
||||
func serviceDelete(ctx web.Context) error {
|
||||
|
@ -1,6 +1,7 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/cuigh/auxo/data"
|
||||
"github.com/cuigh/auxo/net/web"
|
||||
"github.com/cuigh/auxo/util/cast"
|
||||
"github.com/cuigh/swirl/biz/docker"
|
||||
@ -14,6 +15,7 @@ type TaskController struct {
|
||||
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"`
|
||||
FetchLogs web.HandlerFunc `path:"/:id/fetch_logs" name:"task.fetch_logs" authorize:"?" desc:"fetch task logs"`
|
||||
}
|
||||
|
||||
// Task creates an instance of TaskController
|
||||
@ -23,6 +25,7 @@ func Task() (c *TaskController) {
|
||||
Detail: taskDetail,
|
||||
Raw: taskRaw,
|
||||
Logs: taskLogs,
|
||||
FetchLogs: taskFetchLogs,
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,14 +85,21 @@ func taskLogs(ctx web.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
m := newModel(ctx).Set("Task", task)
|
||||
return ctx.Render("task/logs", m)
|
||||
}
|
||||
|
||||
func taskFetchLogs(ctx web.Context) error {
|
||||
id := ctx.P("id")
|
||||
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
|
||||
return ajaxResult(ctx, 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)
|
||||
return ctx.JSON(data.Map{
|
||||
"stdout": stdout.String(),
|
||||
"stderr": stderr.String(),
|
||||
})
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ type Dao struct {
|
||||
// New creates a Dao instance.
|
||||
func New(addr string) (*Dao, error) {
|
||||
if addr == "" {
|
||||
addr = "/data/bolt"
|
||||
addr = "/data/swirl"
|
||||
}
|
||||
|
||||
db, err := bolt.Open(filepath.Join(addr, "swirl.db"), 0600, nil)
|
||||
|
@ -1,5 +1,9 @@
|
||||
{{ extends "../_layouts/default" }}
|
||||
|
||||
{{ block script() }}
|
||||
<script>$(() => new Swirl.Task.LogsPage())</script>
|
||||
{{ end }}
|
||||
|
||||
{{ block body() }}
|
||||
<section class="hero is-info">
|
||||
<div class="hero-body">
|
||||
@ -43,7 +47,6 @@
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<nav class="level">
|
||||
<form>
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<div class="field has-addons">
|
||||
@ -51,25 +54,23 @@
|
||||
<a class="button is-static">Lines</a>
|
||||
</p>
|
||||
<p class="control">
|
||||
<input name="line" value="{{ .Line }}" class="input" placeholder="Max lines from tail">
|
||||
<input id="txt-line" name="line" value="500" 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}}>
|
||||
<input id="cb-timestamps" name="timestamps" value="true" type="checkbox" class="switch is-success is-rounded">
|
||||
<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>
|
||||
<input id="cb-refresh" name="refresh" value="true" type="checkbox" class="switch is-success is-rounded" checked>
|
||||
<label for="cb-refresh">Auto refresh</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</nav>
|
||||
|
||||
<div class="tabs is-boxed" data-target="tab-content">
|
||||
@ -85,12 +86,12 @@
|
||||
<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 }}
|
||||
<textarea id="txt-stdout" class="textarea code is-small" rows="30" readonly></textarea>
|
||||
</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 }}
|
||||
<textarea id="txt-stderr" class="textarea code is-small has-text-danger" rows="30" readonly></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,5 +1,9 @@
|
||||
{{ extends "_base" }}
|
||||
|
||||
{{ block script() }}
|
||||
<script>$(() => new Swirl.Task.LogsPage())</script>
|
||||
{{ end }}
|
||||
|
||||
{{ block body_content() }}
|
||||
<div class="container">
|
||||
<nav class="breadcrumb has-succeeds-separator is-small is-marginless" aria-label="breadcrumbs">
|
||||
@ -37,7 +41,6 @@
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<nav class="level">
|
||||
<form>
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<div class="field has-addons">
|
||||
@ -45,25 +48,23 @@
|
||||
<a class="button is-static">Lines</a>
|
||||
</p>
|
||||
<p class="control">
|
||||
<input name="line" value="{{ .Line }}" class="input" placeholder="Max lines from tail">
|
||||
<input id="txt-line" name="line" value="500" 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}}>
|
||||
<input id="cb-timestamps" name="timestamps" value="true" type="checkbox" class="switch is-success is-rounded">
|
||||
<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>
|
||||
<input id="cb-refresh" name="refresh" value="true" type="checkbox" class="switch is-success is-rounded" checked>
|
||||
<label for="cb-refresh">Auto refresh</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</nav>
|
||||
|
||||
<div class="tabs is-boxed" data-target="tab-content">
|
||||
@ -79,12 +80,12 @@
|
||||
<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 }}
|
||||
<textarea id="txt-stdout" class="textarea code is-small" rows="30" readonly></textarea>
|
||||
</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 }}
|
||||
<textarea id="txt-stderr" class="textarea code is-small has-text-danger" rows="30" readonly></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,5 +1,9 @@
|
||||
{{ extends "../_layouts/default" }}
|
||||
|
||||
{{ block script() }}
|
||||
<script>$(() => new Swirl.Task.LogsPage())</script>
|
||||
{{ end }}
|
||||
|
||||
{{ block body() }}
|
||||
<section class="hero is-info">
|
||||
<div class="hero-body">
|
||||
@ -43,7 +47,6 @@
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<nav class="level">
|
||||
<form>
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<div class="field has-addons">
|
||||
@ -51,25 +54,23 @@
|
||||
<a class="button is-static">Lines</a>
|
||||
</p>
|
||||
<p class="control">
|
||||
<input name="line" value="{{ .Line }}" class="input" placeholder="Max lines from tail">
|
||||
<input id="txt-line" name="line" value="500" 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}}>
|
||||
<input id="cb-timestamps" name="timestamps" value="true" type="checkbox" class="switch is-success is-rounded">
|
||||
<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>
|
||||
<input id="cb-refresh" name="refresh" value="true" type="checkbox" class="switch is-success is-rounded" checked>
|
||||
<label for="cb-refresh">Auto refresh</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</nav>
|
||||
|
||||
<div class="tabs is-boxed" data-target="tab-content">
|
||||
@ -85,12 +86,12 @@
|
||||
<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 }}
|
||||
<textarea id="txt-stdout" class="textarea code is-small" rows="30" readonly></textarea>
|
||||
</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 }}
|
||||
<textarea id="txt-stderr" class="textarea code is-small has-text-danger" rows="30" readonly></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user