mirror of
https://github.com/TeaOSLab/EdgeAdmin.git
synced 2025-11-10 09:20:25 +08:00
增加API方法调用耗时统计
This commit is contained in:
@@ -161,6 +161,10 @@ func (this *RPCClient) APINodeRPC() pb.APINodeServiceClient {
|
||||
return pb.NewAPINodeServiceClient(this.pickConn())
|
||||
}
|
||||
|
||||
func (this *RPCClient) APIMethodStatRPC() pb.APIMethodStatServiceClient {
|
||||
return pb.NewAPIMethodStatServiceClient(this.pickConn())
|
||||
}
|
||||
|
||||
func (this *RPCClient) UserNodeRPC() pb.UserNodeServiceClient {
|
||||
return pb.NewUserNodeServiceClient(this.pickConn())
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -26,11 +27,11 @@ func (this *IndexAction) RunGet(params struct{}) {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
count := countResp.Count
|
||||
page := this.NewPage(count)
|
||||
var count = countResp.Count
|
||||
var page = this.NewPage(count)
|
||||
this.Data["page"] = page.AsHTML()
|
||||
|
||||
nodeMaps := []maps.Map{}
|
||||
var nodeMaps = []maps.Map{}
|
||||
if count > 0 {
|
||||
nodesResp, err := this.RPC().APINodeRPC().ListEnabledAPINodes(this.AdminContext(), &pb.ListEnabledAPINodesRequest{
|
||||
Offset: page.Offset,
|
||||
@@ -106,5 +107,13 @@ func (this *IndexAction) RunGet(params struct{}) {
|
||||
}
|
||||
this.Data["nodes"] = nodeMaps
|
||||
|
||||
// 检查是否有调试数据
|
||||
countMethodStatsResp, err := this.RPC().APIMethodStatRPC().CountAPIMethodStatsWithDay(this.AdminContext(), &pb.CountAPIMethodStatsWithDayRequest{Day: timeutil.Format("Ymd")})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["hasMethodStats"] = countMethodStatsResp.Count > 0
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ func init() {
|
||||
Helper(settingutils.NewAdvancedHelper("apiNodes")).
|
||||
Prefix("/api").
|
||||
Get("", new(IndexAction)).
|
||||
Get("/methodStats", new(MethodStatsAction)).
|
||||
GetPost("/node/createPopup", new(node.CreatePopupAction)).
|
||||
Post("/delete", new(DeleteAction)).
|
||||
EndAll()
|
||||
|
||||
78
internal/web/actions/default/api/methodStats.go
Normal file
78
internal/web/actions/default/api/methodStats.go
Normal file
@@ -0,0 +1,78 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package api
|
||||
|
||||
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"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type MethodStatsAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *MethodStatsAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *MethodStatsAction) RunGet(params struct {
|
||||
Order string
|
||||
Method string
|
||||
Tag string
|
||||
}) {
|
||||
this.Data["order"] = params.Order
|
||||
this.Data["method"] = params.Method
|
||||
this.Data["tag"] = params.Tag
|
||||
|
||||
statsResp, err := this.RPC().APIMethodStatRPC().FindAPIMethodStatsWithDay(this.AdminContext(), &pb.FindAPIMethodStatsWithDayRequest{Day: timeutil.Format("Ymd")})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var pbStats = statsResp.ApiMethodStats
|
||||
|
||||
switch params.Order {
|
||||
case "method":
|
||||
sort.Slice(pbStats, func(i, j int) bool {
|
||||
return pbStats[i].Method < pbStats[j].Method
|
||||
})
|
||||
case "costMs.desc":
|
||||
sort.Slice(pbStats, func(i, j int) bool {
|
||||
return pbStats[i].CostMs > pbStats[j].CostMs
|
||||
})
|
||||
case "peekMs.desc":
|
||||
sort.Slice(pbStats, func(i, j int) bool {
|
||||
return pbStats[i].PeekMs > pbStats[j].PeekMs
|
||||
})
|
||||
case "calls.desc":
|
||||
sort.Slice(pbStats, func(i, j int) bool {
|
||||
return pbStats[i].CountCalls > pbStats[j].CountCalls
|
||||
})
|
||||
}
|
||||
|
||||
var statMaps = []maps.Map{}
|
||||
for _, stat := range pbStats {
|
||||
if len(params.Method) > 0 && !strings.Contains(strings.ToLower(stat.Method), strings.ToLower(params.Method)) {
|
||||
continue
|
||||
}
|
||||
if len(params.Tag) > 0 && !strings.Contains(strings.ToLower(stat.Tag), strings.ToLower(params.Tag)) {
|
||||
continue
|
||||
}
|
||||
|
||||
statMaps = append(statMaps, maps.Map{
|
||||
"id": stat.Id,
|
||||
"method": stat.Method,
|
||||
"tag": stat.Tag,
|
||||
"costMs": stat.CostMs,
|
||||
"peekMs": stat.PeekMs,
|
||||
"countCalls": stat.CountCalls,
|
||||
})
|
||||
}
|
||||
this.Data["stats"] = statMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
<first-menu>
|
||||
<a href="" class="item" @click.prevent="createNode()">[添加节点]</a>
|
||||
<a href="/api/methodStats" class="item" v-if="hasMethodStats">用时统计</a>
|
||||
</first-menu>
|
||||
|
||||
<p class="comment" v-if="nodes.length == 0">暂时还没有节点。</p>
|
||||
|
||||
46
web/views/@default/api/methodStats.html
Normal file
46
web/views/@default/api/methodStats.html
Normal file
@@ -0,0 +1,46 @@
|
||||
{$layout}
|
||||
|
||||
<div class="margin"></div>
|
||||
<form method="get" action="/api/methodStats" class="ui form">
|
||||
<div class="ui fields inline">
|
||||
<div class="ui field">
|
||||
<input type="text" name="method" v-model="method" placeholder="方法"/>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<input type="text" name="tag" v-model="tag" placeholder="标签"/>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<select class="ui dropdown auto-width" name="order" v-model="order">
|
||||
<option value="">[排序]</option>
|
||||
<option value="method">方法</option>
|
||||
<option value="costMs.desc">平均耗时</option>
|
||||
<option value="peekMs.desc">峰值耗时</option>
|
||||
<option value="calls.desc">调用次数</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<button class="ui button" type="submit">搜索</button>
|
||||
|
||||
<a href="/api/methodStats" v-if="method.length > 0 || tag.length > 0 || order.length > 0">清除条件</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<table class="ui table selectable celled">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>方法</th>
|
||||
<th>标签</th>
|
||||
<th>平均耗时</th>
|
||||
<th>峰值耗时</th>
|
||||
<td>调用次数</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="stat in stats">
|
||||
<td>{{stat.method}}</td>
|
||||
<td>{{stat.tag}}</td>
|
||||
<td>{{stat.costMs}}ms</td>
|
||||
<td>{{stat.peekMs}}ms</td>
|
||||
<td>{{stat.countCalls}}次</td>
|
||||
</tr>
|
||||
</table>
|
||||
Reference in New Issue
Block a user