Skip to content

Commit

Permalink
Merge pull request #319 from ShawnJeffersonWang/master
Browse files Browse the repository at this point in the history
feat: add metrics detail list for dubbo instance
  • Loading branch information
chickenlj authored Sep 8, 2024
2 parents 42e4841 + d4f8205 commit 9729cc7
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 0 deletions.
15 changes: 15 additions & 0 deletions pkg/admin/handler/observability.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package handler
import (
"github.com/apache/dubbo-kubernetes/pkg/admin/constants"
"github.com/apache/dubbo-kubernetes/pkg/admin/model"
"github.com/apache/dubbo-kubernetes/pkg/admin/service"
core_runtime "github.com/apache/dubbo-kubernetes/pkg/core/runtime"
"github.com/gin-gonic/gin"
"net/http"
Expand Down Expand Up @@ -92,3 +93,17 @@ func GetPrometheus(rt core_runtime.Runtime) gin.HandlerFunc {
c.JSON(http.StatusOK, model.NewSuccessResp(resp))
}
}

func GetMetricsList(rt core_runtime.Runtime) gin.HandlerFunc {
return func(c *gin.Context) {
req := &model.MetricsReq{}
if err := c.ShouldBindQuery(req); err != nil {
}
resp, err := service.GetInstanceMetrics(rt, req)
if err != nil {
c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error()))
return
}
c.JSON(http.StatusOK, model.NewSuccessResp(resp))
}
}
29 changes: 29 additions & 0 deletions pkg/admin/model/observability.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,32 @@ func (req *ServiceDashboardReq) GetKeyVariable() string {
type DashboardResp struct {
BaseURL string `json:"baseURL"`
}

// Metric represents a single metric with its name, labels, and value.
type Metric struct {
Name string `json:"name"`
Labels map[string]string `json:"labels"`
Value float64 `json:"value"`
}

type MetricsReq struct {
InstanceName string `form:"instanceName"`
}

type MetricsResp struct {
InstanceName string
Metrics []Metric
}

type MetricsCategory int

const (
RT MetricsCategory = iota
QPS
REQUESTS
APPLICATION
CONFIGCENTER
REGISTRY
METADATA
THREAD_POOL
)
1 change: 1 addition & 0 deletions pkg/admin/server/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ func initRouter(r *gin.Engine, rt core_runtime.Runtime) {
}
instance.GET("/metric-dashboard", handler.GetMetricDashBoard(rt, handler.InstanceDimension))
instance.GET("/instance-dashboard", handler.GetTraceDashBoard(rt, handler.InstanceDimension))
instance.GET("/metrics-list", handler.GetMetricsList(rt))
}

{
Expand Down
106 changes: 106 additions & 0 deletions pkg/admin/service/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@
package service

import (
"fmt"
"io"
"net/http"
"strconv"
"strings"
)

import (
Expand Down Expand Up @@ -79,3 +83,105 @@ func GetInstanceDetail(rt core_runtime.Runtime, req *model.InstanceDetailReq) ([
}
return resp, nil
}

func GetInstanceMetrics(rt core_runtime.Runtime, req *model.MetricsReq) ([]*model.MetricsResp, error) {
manager := rt.ResourceManager()
dataplaneList := &mesh.DataplaneResourceList{}
if err := manager.List(rt.AppContext(), dataplaneList, store.ListByNameContains(req.InstanceName)); err != nil {
return nil, err
}
instMap := make(map[string]*model.InstanceDetail)
resp := make([]*model.MetricsResp, 0)
for _, dataplane := range dataplaneList.Items {
instName := dataplane.Meta.GetName()
var instanceDetail *model.InstanceDetail
if detail, ok := instMap[instName]; ok {
instanceDetail = detail
} else {
instanceDetail = model.NewInstanceDetail()
}
instanceDetail.Merge(dataplane)
metrics, err := fetchMetricsData(dataplane.GetIP(), 22222)
if err != nil {
continue
}
metricsResp := &model.MetricsResp{
InstanceName: instName,
Metrics: metrics,
}
resp = append(resp, metricsResp)
}
return resp, nil
}

func fetchMetricsData(ip string, port int) ([]model.Metric, error) {
url := fmt.Sprintf("http://%s:%d/metrics", ip, port)
response, err := http.Get(url)
if err != nil {
return nil, err
}
defer response.Body.Close()

body, err := io.ReadAll(response.Body)
if err != nil {
return nil, err
}
metrics, err := parsePrometheusData(string(body))
if err != nil {
return nil, err
}
return metrics, nil
}

// parsePrometheusData parses Prometheus text format data and converts it to a slice of Metrics.
func parsePrometheusData(data string) ([]model.Metric, error) {
var metrics []model.Metric
lines := strings.Split(data, "\n")

for _, line := range lines {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "#") {
continue
}

parts := strings.Split(line, " ")
if len(parts) != 2 {
continue
}

metricPart := parts[0]
valuePart := parts[1]

// Extract the metric name and labels
nameAndLabels := strings.SplitN(metricPart, "{", 2)
if len(nameAndLabels) != 2 {
continue
}

name := nameAndLabels[0]
labelsPart := strings.TrimSuffix(nameAndLabels[1], "}")

labels := make(map[string]string)
for _, label := range strings.Split(labelsPart, ",") {
if label == "" {
continue
}
labelParts := strings.SplitN(label, "=", 2)
if len(labelParts) == 2 {
labels[labelParts[0]] = strings.Trim(labelParts[1], `"`)
}
}

// Parse the value
var value float64
fmt.Sscanf(valuePart, "%f", &value)

metrics = append(metrics, model.Metric{
Name: name,
Labels: labels,
Value: value,
})
}

return metrics, nil
}

0 comments on commit 9729cc7

Please sign in to comment.