@@ -25,4 +25,8 @@ type Interface interface {
|
||||
GetNamedMetricsOverTime(metrics []string, start, end time.Time, step time.Duration, opt QueryOption) []Metric
|
||||
GetMetadata(namespace string) []Metadata
|
||||
GetMetricLabelSet(expr string, start, end time.Time) []map[string]string
|
||||
|
||||
// meter
|
||||
GetNamedMeters(meters []string, time time.Time, opts []QueryOption) []Metric
|
||||
GetNamedMetersOverTime(metrics []string, start, end time.Time, step time.Duration, opts []QueryOption) []Metric
|
||||
}
|
||||
|
||||
@@ -445,3 +445,11 @@ func (m metricsServer) GetMetricLabelSet(expr string, start, end time.Time) []ma
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// meter
|
||||
func (m metricsServer) GetNamedMeters(meters []string, time time.Time, opts []monitoring.QueryOption) []monitoring.Metric {
|
||||
return nil
|
||||
}
|
||||
func (m metricsServer) GetNamedMetersOverTime(metrics []string, start, end time.Time, step time.Duration, opts []monitoring.QueryOption) []monitoring.Metric {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ import (
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/monitoring"
|
||||
)
|
||||
|
||||
const MeteringDefaultTimeout = 20 * time.Second
|
||||
|
||||
// prometheus implements monitoring interface backed by Prometheus
|
||||
type prometheus struct {
|
||||
client apiv1.API
|
||||
@@ -147,6 +149,108 @@ func (p prometheus) GetNamedMetricsOverTime(metrics []string, start, end time.Ti
|
||||
return res
|
||||
}
|
||||
|
||||
func (p prometheus) GetNamedMeters(meters []string, ts time.Time, opts []monitoring.QueryOption) []monitoring.Metric {
|
||||
var res []monitoring.Metric
|
||||
var wg sync.WaitGroup
|
||||
var mtx sync.Mutex
|
||||
|
||||
queryOptions := monitoring.NewQueryOptions()
|
||||
|
||||
for _, opt := range opts {
|
||||
opt.Apply(queryOptions)
|
||||
}
|
||||
|
||||
prometheusCtx, cancel := context.WithTimeout(context.Background(), MeteringDefaultTimeout)
|
||||
defer cancel()
|
||||
|
||||
for _, meter := range meters {
|
||||
|
||||
wg.Add(1)
|
||||
|
||||
go func(metric string) {
|
||||
parsedResp := monitoring.Metric{MetricName: metric}
|
||||
|
||||
begin := time.Now()
|
||||
value, _, err := p.client.Query(prometheusCtx, makeMeterExpr(metric, *queryOptions), ts)
|
||||
end := time.Now()
|
||||
timeElapsed := end.Unix() - begin.Unix()
|
||||
if timeElapsed > int64(MeteringDefaultTimeout.Seconds())/2 {
|
||||
klog.Warningf("long time query[cost %v seconds], expr: %v", timeElapsed, makeMeterExpr(metric, *queryOptions))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
parsedResp.Error = err.Error()
|
||||
} else {
|
||||
parsedResp.MetricData = parseQueryResp(value, nil)
|
||||
}
|
||||
|
||||
mtx.Lock()
|
||||
res = append(res, parsedResp)
|
||||
mtx.Unlock()
|
||||
|
||||
wg.Done()
|
||||
}(meter)
|
||||
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (p prometheus) GetNamedMetersOverTime(meters []string, start, end time.Time, step time.Duration, opts []monitoring.QueryOption) []monitoring.Metric {
|
||||
var res []monitoring.Metric
|
||||
var wg sync.WaitGroup
|
||||
var mtx sync.Mutex
|
||||
|
||||
queryOptions := monitoring.NewQueryOptions()
|
||||
|
||||
for _, opt := range opts {
|
||||
opt.Apply(queryOptions)
|
||||
}
|
||||
|
||||
timeRange := apiv1.Range{
|
||||
Start: start,
|
||||
End: end,
|
||||
Step: step,
|
||||
}
|
||||
|
||||
prometheusCtx, cancel := context.WithTimeout(context.Background(), MeteringDefaultTimeout)
|
||||
defer cancel()
|
||||
|
||||
for _, meter := range meters {
|
||||
|
||||
wg.Add(1)
|
||||
|
||||
go func(metric string) {
|
||||
parsedResp := monitoring.Metric{MetricName: metric}
|
||||
begin := time.Now()
|
||||
value, _, err := p.client.QueryRange(prometheusCtx, makeMeterExpr(metric, *queryOptions), timeRange)
|
||||
end := time.Now()
|
||||
timeElapsed := end.Unix() - begin.Unix()
|
||||
if timeElapsed > int64(MeteringDefaultTimeout.Seconds())/2 {
|
||||
klog.Warningf("long time query[cost %v seconds], expr: %v", timeElapsed, makeMeterExpr(metric, *queryOptions))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
parsedResp.Error = err.Error()
|
||||
} else {
|
||||
parsedResp.MetricData = parseQueryRangeResp(value, nil)
|
||||
}
|
||||
|
||||
mtx.Lock()
|
||||
res = append(res, parsedResp)
|
||||
mtx.Unlock()
|
||||
|
||||
wg.Done()
|
||||
}(meter)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (p prometheus) GetMetadata(namespace string) []monitoring.Metadata {
|
||||
var meta []monitoring.Metadata
|
||||
var matchTarget string
|
||||
|
||||
1304
pkg/simple/client/monitoring/prometheus/promql_meter.go
Normal file
1304
pkg/simple/client/monitoring/prometheus/promql_meter.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -16,6 +16,12 @@ limitations under the License.
|
||||
|
||||
package monitoring
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Level int
|
||||
|
||||
const (
|
||||
@@ -23,17 +29,39 @@ const (
|
||||
LevelNode
|
||||
LevelWorkspace
|
||||
LevelNamespace
|
||||
LevelApplication
|
||||
LevelWorkload
|
||||
LevelService
|
||||
LevelPod
|
||||
LevelContainer
|
||||
LevelPVC
|
||||
LevelComponent
|
||||
)
|
||||
|
||||
var MeteringLevelMap = map[string]int{
|
||||
"LevelCluster": LevelCluster,
|
||||
"LevelNode": LevelNode,
|
||||
"LevelWorkspace": LevelWorkspace,
|
||||
"LevelNamespace": LevelNamespace,
|
||||
"LevelApplication": LevelApplication,
|
||||
"LevelWorkload": LevelWorkload,
|
||||
"LevelService": LevelService,
|
||||
"LevelPod": LevelPod,
|
||||
"LevelContainer": LevelContainer,
|
||||
"LevelPVC": LevelPVC,
|
||||
"LevelComponent": LevelComponent,
|
||||
}
|
||||
|
||||
type QueryOption interface {
|
||||
Apply(*QueryOptions)
|
||||
}
|
||||
|
||||
type Meteroptions struct {
|
||||
Start time.Time
|
||||
End time.Time
|
||||
Step time.Duration
|
||||
}
|
||||
|
||||
type QueryOptions struct {
|
||||
Level Level
|
||||
|
||||
@@ -48,6 +76,10 @@ type QueryOptions struct {
|
||||
ContainerName string
|
||||
StorageClassName string
|
||||
PersistentVolumeClaimName string
|
||||
PVCFilter string
|
||||
ApplicationName string
|
||||
ServiceName string
|
||||
MeterOptions *Meteroptions
|
||||
}
|
||||
|
||||
func NewQueryOptions() *QueryOptions {
|
||||
@@ -61,31 +93,41 @@ func (_ ClusterOption) Apply(o *QueryOptions) {
|
||||
}
|
||||
|
||||
type NodeOption struct {
|
||||
ResourceFilter string
|
||||
NodeName string
|
||||
ResourceFilter string
|
||||
NodeName string
|
||||
PVCFilter string
|
||||
StorageClassName string
|
||||
}
|
||||
|
||||
func (no NodeOption) Apply(o *QueryOptions) {
|
||||
o.Level = LevelNode
|
||||
o.ResourceFilter = no.ResourceFilter
|
||||
o.NodeName = no.NodeName
|
||||
o.PVCFilter = no.PVCFilter
|
||||
o.StorageClassName = no.StorageClassName
|
||||
}
|
||||
|
||||
type WorkspaceOption struct {
|
||||
ResourceFilter string
|
||||
WorkspaceName string
|
||||
ResourceFilter string
|
||||
WorkspaceName string
|
||||
PVCFilter string
|
||||
StorageClassName string
|
||||
}
|
||||
|
||||
func (wo WorkspaceOption) Apply(o *QueryOptions) {
|
||||
o.Level = LevelWorkspace
|
||||
o.ResourceFilter = wo.ResourceFilter
|
||||
o.WorkspaceName = wo.WorkspaceName
|
||||
o.PVCFilter = wo.PVCFilter
|
||||
o.StorageClassName = wo.StorageClassName
|
||||
}
|
||||
|
||||
type NamespaceOption struct {
|
||||
ResourceFilter string
|
||||
WorkspaceName string
|
||||
NamespaceName string
|
||||
ResourceFilter string
|
||||
WorkspaceName string
|
||||
NamespaceName string
|
||||
PVCFilter string
|
||||
StorageClassName string
|
||||
}
|
||||
|
||||
func (no NamespaceOption) Apply(o *QueryOptions) {
|
||||
@@ -93,6 +135,41 @@ func (no NamespaceOption) Apply(o *QueryOptions) {
|
||||
o.ResourceFilter = no.ResourceFilter
|
||||
o.WorkspaceName = no.WorkspaceName
|
||||
o.NamespaceName = no.NamespaceName
|
||||
o.PVCFilter = no.PVCFilter
|
||||
o.StorageClassName = no.StorageClassName
|
||||
}
|
||||
|
||||
type ApplicationsOption struct {
|
||||
NamespaceName string
|
||||
Applications []string
|
||||
StorageClassName string
|
||||
}
|
||||
|
||||
func (aso ApplicationsOption) Apply(o *QueryOptions) {
|
||||
// nothing should be done
|
||||
return
|
||||
}
|
||||
|
||||
type ApplicationOption struct {
|
||||
NamespaceName string
|
||||
Application string
|
||||
ApplicationComponents []string
|
||||
StorageClassName string
|
||||
}
|
||||
|
||||
func (ao ApplicationOption) Apply(o *QueryOptions) {
|
||||
o.Level = LevelApplication
|
||||
o.NamespaceName = ao.NamespaceName
|
||||
o.ApplicationName = ao.Application
|
||||
o.StorageClassName = ao.StorageClassName
|
||||
|
||||
app_components := strings.Join(ao.ApplicationComponents[:], "|")
|
||||
|
||||
if len(app_components) > 0 {
|
||||
o.ResourceFilter = fmt.Sprintf(`namespace="%s", workload=~"%s"`, o.NamespaceName, app_components)
|
||||
} else {
|
||||
o.ResourceFilter = fmt.Sprintf(`namespace="%s", workload=~"%s"`, o.NamespaceName, ".*")
|
||||
}
|
||||
}
|
||||
|
||||
type WorkloadOption struct {
|
||||
@@ -108,6 +185,37 @@ func (wo WorkloadOption) Apply(o *QueryOptions) {
|
||||
o.WorkloadKind = wo.WorkloadKind
|
||||
}
|
||||
|
||||
type ServicesOption struct {
|
||||
NamespaceName string
|
||||
Services []string
|
||||
}
|
||||
|
||||
func (sso ServicesOption) Apply(o *QueryOptions) {
|
||||
// nothing should be done
|
||||
return
|
||||
}
|
||||
|
||||
type ServiceOption struct {
|
||||
ResourceFilter string
|
||||
NamespaceName string
|
||||
ServiceName string
|
||||
PodNames []string
|
||||
}
|
||||
|
||||
func (so ServiceOption) Apply(o *QueryOptions) {
|
||||
o.Level = LevelService
|
||||
o.NamespaceName = so.NamespaceName
|
||||
o.ServiceName = so.ServiceName
|
||||
|
||||
pod_names := strings.Join(so.PodNames, "|")
|
||||
|
||||
if len(pod_names) > 0 {
|
||||
o.ResourceFilter = fmt.Sprintf(`pod=~"%s", namespace="%s"`, pod_names, o.NamespaceName)
|
||||
} else {
|
||||
o.ResourceFilter = fmt.Sprintf(`pod=~"%s", namespace="%s"`, ".*", o.NamespaceName)
|
||||
}
|
||||
}
|
||||
|
||||
type PodOption struct {
|
||||
NamespacedResourcesFilter string
|
||||
ResourceFilter string
|
||||
@@ -157,6 +265,9 @@ func (po PVCOption) Apply(o *QueryOptions) {
|
||||
o.NamespaceName = po.NamespaceName
|
||||
o.StorageClassName = po.StorageClassName
|
||||
o.PersistentVolumeClaimName = po.PersistentVolumeClaimName
|
||||
|
||||
// for meter
|
||||
o.PVCFilter = po.PersistentVolumeClaimName
|
||||
}
|
||||
|
||||
type ComponentOption struct{}
|
||||
@@ -164,3 +275,17 @@ type ComponentOption struct{}
|
||||
func (_ ComponentOption) Apply(o *QueryOptions) {
|
||||
o.Level = LevelComponent
|
||||
}
|
||||
|
||||
type MeterOption struct {
|
||||
Start time.Time
|
||||
End time.Time
|
||||
Step time.Duration
|
||||
}
|
||||
|
||||
func (mo MeterOption) Apply(o *QueryOptions) {
|
||||
o.MeterOptions = &Meteroptions{
|
||||
Start: mo.Start,
|
||||
End: mo.End,
|
||||
Step: mo.Step,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/json-iterator/go"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -54,8 +55,32 @@ type MetricValue struct {
|
||||
// The type of Point is a float64 array with fixed length of 2.
|
||||
// So Point will always be initialized as [0, 0], rather than nil.
|
||||
// To allow empty Sample, we should declare Sample to type *Point
|
||||
Sample *Point `json:"value,omitempty" description:"time series, values of vector type"`
|
||||
Series []Point `json:"values,omitempty" description:"time series, values of matrix type"`
|
||||
Sample *Point `json:"value,omitempty" description:"time series, values of vector type"`
|
||||
Series []Point `json:"values,omitempty" description:"time series, values of matrix type"`
|
||||
ExportSample *ExportPoint `json:"exported_value,omitempty" description:"exported time series, values of vector type"`
|
||||
ExportedSeries []ExportPoint `json:"exported_values,omitempty" description:"exported time series, values of matrix type"`
|
||||
|
||||
MinValue float64 `json:"min_value" description:"minimum value from monitor points"`
|
||||
MaxValue float64 `json:"max_value" description:"maximum value from monitor points"`
|
||||
AvgValue float64 `json:"avg_value" description:"average value from monitor points"`
|
||||
SumValue float64 `json:"sum_value" description:"sum value from monitor points"`
|
||||
Fee float64 `json:"fee" description:"resource fee"`
|
||||
}
|
||||
|
||||
func (mv *MetricValue) TransferToExportedMetricValue() {
|
||||
|
||||
if mv.Sample != nil {
|
||||
sample := mv.Sample.transferToExported()
|
||||
mv.ExportSample = &sample
|
||||
mv.Sample = nil
|
||||
}
|
||||
|
||||
for _, item := range mv.Series {
|
||||
mv.ExportedSeries = append(mv.ExportedSeries, item.transferToExported())
|
||||
}
|
||||
mv.Series = nil
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (p Point) Timestamp() float64 {
|
||||
@@ -66,6 +91,10 @@ func (p Point) Value() float64 {
|
||||
return p[1]
|
||||
}
|
||||
|
||||
func (p Point) transferToExported() ExportPoint {
|
||||
return ExportPoint{p[0], p[1]}
|
||||
}
|
||||
|
||||
// MarshalJSON implements json.Marshaler. It will be called when writing JSON to HTTP response
|
||||
// Inspired by prometheus/client_golang
|
||||
func (p Point) MarshalJSON() ([]byte, error) {
|
||||
@@ -112,3 +141,27 @@ func (p *Point) UnmarshalJSON(b []byte) error {
|
||||
p[1] = valf
|
||||
return nil
|
||||
}
|
||||
|
||||
type ExportPoint [2]float64
|
||||
|
||||
func (p ExportPoint) Timestamp() string {
|
||||
return time.Unix(int64(p[0]), 0).Format("2006-01-02 03:04:05 PM")
|
||||
}
|
||||
|
||||
func (p ExportPoint) Value() float64 {
|
||||
return p[1]
|
||||
}
|
||||
|
||||
// MarshalJSON implements json.Marshaler. It will be called when writing JSON to HTTP response
|
||||
// Inspired by prometheus/client_golang
|
||||
func (p ExportPoint) MarshalJSON() ([]byte, error) {
|
||||
t, err := jsoniter.Marshal(p.Timestamp())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v, err := jsoniter.Marshal(strconv.FormatFloat(p.Value(), 'f', -1, 64))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []byte(fmt.Sprintf("[%s,%s]", t, v)), nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user