mirror of
https://github.com/TeaOSLab/EdgeAdmin.git
synced 2025-11-06 14:50:25 +08:00
增加全局的边缘节点日志
This commit is contained in:
@@ -22,7 +22,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RPC客户端
|
// RPCClient RPC客户端
|
||||||
type RPCClient struct {
|
type RPCClient struct {
|
||||||
apiConfig *configs.APIConfig
|
apiConfig *configs.APIConfig
|
||||||
conns []*grpc.ClientConn
|
conns []*grpc.ClientConn
|
||||||
@@ -30,7 +30,7 @@ type RPCClient struct {
|
|||||||
locker sync.Mutex
|
locker sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构造新的RPC客户端
|
// NewRPCClient 构造新的RPC客户端
|
||||||
func NewRPCClient(apiConfig *configs.APIConfig) (*RPCClient, error) {
|
func NewRPCClient(apiConfig *configs.APIConfig) (*RPCClient, error) {
|
||||||
if apiConfig == nil {
|
if apiConfig == nil {
|
||||||
return nil, errors.New("api config should not be nil")
|
return nil, errors.New("api config should not be nil")
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ func TestRPC_Dial_HTTP(t *testing.T) {
|
|||||||
RPC: struct {
|
RPC: struct {
|
||||||
Endpoints []string `yaml:"endpoints"`
|
Endpoints []string `yaml:"endpoints"`
|
||||||
}{
|
}{
|
||||||
Endpoints: []string{"127.0.0.1:8003"},
|
Endpoints: []string{"http://127.0.0.1:8004"},
|
||||||
},
|
},
|
||||||
NodeId: "a7e55782dab39bce0901058a1e14a0e6",
|
NodeId: "a7e55782dab39bce0901058a1e14a0e6",
|
||||||
Secret: "lvyPobI3BszkJopz5nPTocOs0OLkEJ7y",
|
Secret: "lvyPobI3BszkJopz5nPTocOs0OLkEJ7y",
|
||||||
@@ -58,7 +58,7 @@ func TestRPC_Dial_HTTP_2(t *testing.T) {
|
|||||||
RPC: struct {
|
RPC: struct {
|
||||||
Endpoints []string `yaml:"endpoints"`
|
Endpoints []string `yaml:"endpoints"`
|
||||||
}{
|
}{
|
||||||
Endpoints: []string{"http://127.0.0.1:8003"},
|
Endpoints: []string{"https://127.0.0.1:8003"},
|
||||||
},
|
},
|
||||||
NodeId: "a7e55782dab39bce0901058a1e14a0e6",
|
NodeId: "a7e55782dab39bce0901058a1e14a0e6",
|
||||||
Secret: "lvyPobI3BszkJopz5nPTocOs0OLkEJ7y",
|
Secret: "lvyPobI3BszkJopz5nPTocOs0OLkEJ7y",
|
||||||
|
|||||||
86
internal/web/actions/default/clusters/logs/index.go
Normal file
86
internal/web/actions/default/clusters/logs/index.go
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
package logs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type IndexAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *IndexAction) Init() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *IndexAction) RunGet(params struct {
|
||||||
|
DayFrom string
|
||||||
|
DayTo string
|
||||||
|
Keyword string
|
||||||
|
Level string
|
||||||
|
}) {
|
||||||
|
this.Data["dayFrom"] = params.DayFrom
|
||||||
|
this.Data["dayTo"] = params.DayTo
|
||||||
|
this.Data["keyword"] = params.Keyword
|
||||||
|
this.Data["level"] = params.Level
|
||||||
|
|
||||||
|
countResp, err := this.RPC().NodeLogRPC().CountNodeLogs(this.AdminContext(), &pb.CountNodeLogsRequest{
|
||||||
|
NodeId: 0,
|
||||||
|
Role: "node",
|
||||||
|
DayFrom: params.DayFrom,
|
||||||
|
DayTo: params.DayTo,
|
||||||
|
Keyword: params.Keyword,
|
||||||
|
Level: params.Level,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
count := countResp.Count
|
||||||
|
page := this.NewPage(count)
|
||||||
|
this.Data["page"] = page.AsHTML()
|
||||||
|
|
||||||
|
logsResp, err := this.RPC().NodeLogRPC().ListNodeLogs(this.AdminContext(), &pb.ListNodeLogsRequest{
|
||||||
|
NodeId: 0,
|
||||||
|
Role: "node",
|
||||||
|
DayFrom: params.DayFrom,
|
||||||
|
DayTo: params.DayTo,
|
||||||
|
Keyword: params.Keyword,
|
||||||
|
Level: params.Level,
|
||||||
|
Offset: page.Offset,
|
||||||
|
Size: page.Size,
|
||||||
|
})
|
||||||
|
|
||||||
|
logs := []maps.Map{}
|
||||||
|
for _, log := range logsResp.NodeLogs {
|
||||||
|
// 节点信息
|
||||||
|
nodeResp, err := this.RPC().NodeRPC().FindEnabledNode(this.AdminContext(), &pb.FindEnabledNodeRequest{NodeId: log.NodeId})
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
node := nodeResp.Node
|
||||||
|
if node == nil || node.NodeCluster == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
logs = append(logs, maps.Map{
|
||||||
|
"tag": log.Tag,
|
||||||
|
"description": log.Description,
|
||||||
|
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", log.CreatedAt),
|
||||||
|
"level": log.Level,
|
||||||
|
"isToday": timeutil.FormatTime("Y-m-d", log.CreatedAt) == timeutil.Format("Y-m-d"),
|
||||||
|
"node": maps.Map{
|
||||||
|
"id": node.Id,
|
||||||
|
"cluster": maps.Map{
|
||||||
|
"id": node.NodeCluster.Id,
|
||||||
|
"name": node.NodeCluster.Name,
|
||||||
|
},
|
||||||
|
"name": node.Name,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.Data["logs"] = logs
|
||||||
|
|
||||||
|
this.Show()
|
||||||
|
}
|
||||||
19
internal/web/actions/default/clusters/logs/init.go
Normal file
19
internal/web/actions/default/clusters/logs/init.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package logs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||||
|
"github.com/iwind/TeaGo"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||||
|
server.
|
||||||
|
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNode)).
|
||||||
|
Data("teaMenu", "clusters").
|
||||||
|
Data("teaSubMenu", "log").
|
||||||
|
Prefix("/clusters/logs").
|
||||||
|
Get("", new(IndexAction)).
|
||||||
|
EndAll()
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -181,6 +181,11 @@ func (this *userMustAuth) modules(adminId int64) []maps.Map {
|
|||||||
"subtitle": "集群列表",
|
"subtitle": "集群列表",
|
||||||
"icon": "cloud",
|
"icon": "cloud",
|
||||||
"subItems": []maps.Map{
|
"subItems": []maps.Map{
|
||||||
|
{
|
||||||
|
"name": "节点日志",
|
||||||
|
"url": "/clusters/logs",
|
||||||
|
"code": "log",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "SSH认证",
|
"name": "SSH认证",
|
||||||
"url": "/clusters/grants",
|
"url": "/clusters/grants",
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster"
|
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster"
|
||||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings"
|
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings"
|
||||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants"
|
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants"
|
||||||
|
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/logs"
|
||||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/regions"
|
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/regions"
|
||||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/regions/items"
|
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/regions/items"
|
||||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/tasks"
|
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/tasks"
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ Vue.component("origin-list-box", {
|
|||||||
teaweb.popup("/servers/server/settings/origins/addPopup?originType=primary&" + this.vParams, {
|
teaweb.popup("/servers/server/settings/origins/addPopup?originType=primary&" + this.vParams, {
|
||||||
height: "24em",
|
height: "24em",
|
||||||
callback: function (resp) {
|
callback: function (resp) {
|
||||||
window.location.reload()
|
teaweb.success("保存成功", function () {
|
||||||
|
window.location.reload()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -19,7 +21,9 @@ Vue.component("origin-list-box", {
|
|||||||
teaweb.popup("/servers/server/settings/origins/addPopup?originType=backup&" + this.vParams, {
|
teaweb.popup("/servers/server/settings/origins/addPopup?originType=backup&" + this.vParams, {
|
||||||
height: "24em",
|
height: "24em",
|
||||||
callback: function (resp) {
|
callback: function (resp) {
|
||||||
window.location.reload()
|
teaweb.success("保存成功", function () {
|
||||||
|
window.location.reload()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -27,7 +31,9 @@ Vue.component("origin-list-box", {
|
|||||||
teaweb.popup("/servers/server/settings/origins/updatePopup?originType=" + originType + "&" + this.vParams + "&originId=" + originId, {
|
teaweb.popup("/servers/server/settings/origins/updatePopup?originType=" + originType + "&" + this.vParams + "&originId=" + originId, {
|
||||||
height: "24em",
|
height: "24em",
|
||||||
callback: function (resp) {
|
callback: function (resp) {
|
||||||
window.location.reload()
|
teaweb.success("保存成功", function () {
|
||||||
|
window.location.reload()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -37,7 +43,9 @@ Vue.component("origin-list-box", {
|
|||||||
Tea.action("/servers/server/settings/origins/delete?" + that.vParams + "&originId=" + originId + "&originType=" + originType)
|
Tea.action("/servers/server/settings/origins/delete?" + that.vParams + "&originId=" + originId + "&originType=" + originType)
|
||||||
.post()
|
.post()
|
||||||
.success(function () {
|
.success(function () {
|
||||||
window.location.reload()
|
teaweb.success("保存成功", function () {
|
||||||
|
window.location.reload()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,19 @@
|
|||||||
{$layout}
|
{$layout}
|
||||||
|
{$template "node_menu"}
|
||||||
|
|
||||||
{$template "node_menu"}
|
<p class="comment" v-if="logs.length == 0">暂时还没有日志。</p>
|
||||||
|
|
||||||
<p class="comment" v-if="logs.length == 0">暂时还没有日志。</p>
|
<table class="ui table selectable" v-if="logs.length > 0">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
|
||||||
<table class="ui table selectable" v-if="logs.length > 0">
|
</tr>
|
||||||
<thead>
|
</thead>
|
||||||
<tr>
|
<tr v-for="log in logs">
|
||||||
|
<td>
|
||||||
|
<pre class="log-box"><span :class="{red:log.level == 'error', yellow:log.level == 'warning'}"><span v-if="!log.isToday">[{{log.createdTime}}]</span><strong v-if="log.isToday">[{{log.createdTime}}]</strong>[{{log.tag}}]{{log.description}}</span></pre>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
</tr>
|
<div class="page" v-html="page"></div>
|
||||||
</thead>
|
|
||||||
<tr v-for="log in logs">
|
|
||||||
<td>
|
|
||||||
<pre class="log-box"><span :class="{red:log.level == 'error', yellow:log.level == 'warning'}"><span v-if="!log.isToday">[{{log.createdTime}}]</span><strong v-if="log.isToday">[{{log.createdTime}}]</strong>[{{log.tag}}]{{log.description}}</span></pre>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<div class="page" v-html="page"></div>
|
|
||||||
5
web/views/@default/clusters/logs/index.css
Normal file
5
web/views/@default/clusters/logs/index.css
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
pre.log-box {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
/*# sourceMappingURL=index.css.map */
|
||||||
1
web/views/@default/clusters/logs/index.css.map
Normal file
1
web/views/@default/clusters/logs/index.css.map
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["index.less"],"names":[],"mappings":"AAAA,GAAG;EACF,SAAA;EACA,UAAA","file":"index.css"}
|
||||||
62
web/views/@default/clusters/logs/index.html
Normal file
62
web/views/@default/clusters/logs/index.html
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
{$layout}
|
||||||
|
|
||||||
|
{$var "header"}
|
||||||
|
<!-- datepicker -->
|
||||||
|
<script type="text/javascript" src="/js/moment.min.js"></script>
|
||||||
|
<script type="text/javascript" src="/js/pikaday.js"></script>
|
||||||
|
<link rel="stylesheet" href="/js/pikaday.css"/>
|
||||||
|
<link rel="stylesheet" href="/js/pikaday.theme.css"/>
|
||||||
|
<link rel="stylesheet" href="/js/pikaday.triangle.css"/>
|
||||||
|
{$end}
|
||||||
|
|
||||||
|
|
||||||
|
<div class="margin"></div>
|
||||||
|
|
||||||
|
<form method="get" action="/clusters/logs" class="ui form" autocomplete="off">
|
||||||
|
<div class="ui fields inline">
|
||||||
|
<div class="ui field">
|
||||||
|
<input type="text" name="dayFrom" placeholder="开始日期" v-model="dayFrom" value="" style="width:8em" id="day-from-picker"/>
|
||||||
|
</div>
|
||||||
|
<div class="ui field">
|
||||||
|
<input type="text" name="dayTo" placeholder="结束日期" v-model="dayTo" value="" style="width:8em" id="day-to-picker"/>
|
||||||
|
</div>
|
||||||
|
<div class="ui field">
|
||||||
|
<select class="ui dropdown" name="level" v-model="level">
|
||||||
|
<option value="">[级别]</option>
|
||||||
|
<option value="error">错误</option>
|
||||||
|
<option value="warn">警告</option>
|
||||||
|
<option value="info">信息</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="ui field">
|
||||||
|
<input type="text" name="keyword" style="width:10em" v-model="keyword" placeholder="关键词"/>
|
||||||
|
</div>
|
||||||
|
<div class="ui field">
|
||||||
|
<button type="submit" class="ui button">查询</button>
|
||||||
|
</div>
|
||||||
|
<div class="ui field" v-if="dayFrom.length > 0 || dayTo.length > 0 || keyword.length > 0 || level.length > 0">
|
||||||
|
<a href="/clusters/logs">[清除条件]</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<p class="comment" v-if="logs.length == 0">暂时还没有日志。</p>
|
||||||
|
|
||||||
|
<table class="ui table selectable celled" v-if="logs.length > 0">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>集群</th>
|
||||||
|
<th>节点</th>
|
||||||
|
<th>信息</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tr v-for="log in logs">
|
||||||
|
<td nowrap=""><link-icon :href="'/clusters/cluster?clusterId=' + log.node.cluster.id">{{log.node.cluster.name}}</link-icon></td>
|
||||||
|
<td nowrap=""><link-icon :href="'/clusters/cluster/node?clusterId=' + log.node.cluster.id + '&nodeId=' + log.node.id">{{log.node.name}}</link-icon></td>
|
||||||
|
<td>
|
||||||
|
<pre class="log-box"><span :class="{red:log.level == 'error', yellow:log.level == 'warning'}"><span v-if="!log.isToday">[{{log.createdTime}}]</span><strong v-if="log.isToday">[{{log.createdTime}}]</strong>[{{log.tag}}]{{log.description}}</span></pre>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="page" v-html="page"></div>
|
||||||
6
web/views/@default/clusters/logs/index.js
Normal file
6
web/views/@default/clusters/logs/index.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
Tea.context(function () {
|
||||||
|
this.$delay(function () {
|
||||||
|
teaweb.datepicker("day-from-picker")
|
||||||
|
teaweb.datepicker("day-to-picker")
|
||||||
|
})
|
||||||
|
})
|
||||||
4
web/views/@default/clusters/logs/index.less
Normal file
4
web/views/@default/clusters/logs/index.less
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
pre.log-box {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user