增加API方法调用耗时统计

This commit is contained in:
刘祥超
2022-01-19 16:53:38 +08:00
parent 79e52d1c6e
commit 65d19d92e9
6 changed files with 142 additions and 3 deletions

View File

@@ -161,6 +161,10 @@ func (this *RPCClient) APINodeRPC() pb.APINodeServiceClient {
return pb.NewAPINodeServiceClient(this.pickConn()) return pb.NewAPINodeServiceClient(this.pickConn())
} }
func (this *RPCClient) APIMethodStatRPC() pb.APIMethodStatServiceClient {
return pb.NewAPIMethodStatServiceClient(this.pickConn())
}
func (this *RPCClient) UserNodeRPC() pb.UserNodeServiceClient { func (this *RPCClient) UserNodeRPC() pb.UserNodeServiceClient {
return pb.NewUserNodeServiceClient(this.pickConn()) return pb.NewUserNodeServiceClient(this.pickConn())
} }

View File

@@ -9,6 +9,7 @@ import (
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs" "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/logs" "github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/maps" "github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
"time" "time"
) )
@@ -26,11 +27,11 @@ func (this *IndexAction) RunGet(params struct{}) {
this.ErrorPage(err) this.ErrorPage(err)
return return
} }
count := countResp.Count var count = countResp.Count
page := this.NewPage(count) var page = this.NewPage(count)
this.Data["page"] = page.AsHTML() this.Data["page"] = page.AsHTML()
nodeMaps := []maps.Map{} var nodeMaps = []maps.Map{}
if count > 0 { if count > 0 {
nodesResp, err := this.RPC().APINodeRPC().ListEnabledAPINodes(this.AdminContext(), &pb.ListEnabledAPINodesRequest{ nodesResp, err := this.RPC().APINodeRPC().ListEnabledAPINodes(this.AdminContext(), &pb.ListEnabledAPINodesRequest{
Offset: page.Offset, Offset: page.Offset,
@@ -106,5 +107,13 @@ func (this *IndexAction) RunGet(params struct{}) {
} }
this.Data["nodes"] = nodeMaps 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() this.Show()
} }

View File

@@ -16,6 +16,7 @@ func init() {
Helper(settingutils.NewAdvancedHelper("apiNodes")). Helper(settingutils.NewAdvancedHelper("apiNodes")).
Prefix("/api"). Prefix("/api").
Get("", new(IndexAction)). Get("", new(IndexAction)).
Get("/methodStats", new(MethodStatsAction)).
GetPost("/node/createPopup", new(node.CreatePopupAction)). GetPost("/node/createPopup", new(node.CreatePopupAction)).
Post("/delete", new(DeleteAction)). Post("/delete", new(DeleteAction)).
EndAll() EndAll()

View 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()
}

View File

@@ -2,6 +2,7 @@
<first-menu> <first-menu>
<a href="" class="item" @click.prevent="createNode()">[添加节点]</a> <a href="" class="item" @click.prevent="createNode()">[添加节点]</a>
<a href="/api/methodStats" class="item" v-if="hasMethodStats">用时统计</a>
</first-menu> </first-menu>
<p class="comment" v-if="nodes.length == 0">暂时还没有节点。</p> <p class="comment" v-if="nodes.length == 0">暂时还没有节点。</p>

View 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>
&nbsp;
<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>