Commit c618c05b authored by 李科's avatar 李科

feat: 告警总览

parent ea1e61a8
......@@ -108,6 +108,9 @@ require (
github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 // indirect
github.com/sirupsen/logrus v1.9.2 // indirect
github.com/syndtr/goleveldb v1.0.0 // indirect
github.com/tidwall/gjson v1.16.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.9 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
......
......@@ -647,6 +647,12 @@ github.com/tealeg/xlsx v1.0.5 h1:+f8oFmvY8Gw1iUXzPk+kz+4GpbDZPK1FhPiQRd+ypgE=
github.com/tealeg/xlsx v1.0.5/go.mod h1:btRS8dz54TDnvKNosuAqxrM1QgN1udgk9O34bDCnORM=
github.com/thoas/go-funk v0.9.3 h1:7+nAEx3kn5ZJcnDm2Bh23N2yOtweO14bi//dvRtgLpw=
github.com/thoas/go-funk v0.9.3/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q=
github.com/tidwall/gjson v1.16.0 h1:SyXa+dsSPpUlcwEDuKuEBJEz5vzTvOea+9rjyYodQFg=
github.com/tidwall/gjson v1.16.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
......
......@@ -21,24 +21,8 @@ type AlertArray struct {
TotalCount int `json:"total_count"` // 总预警数
}
type RiskLevelDistribution struct {
type AlertDistribution struct {
Name string `json:"name"` // 名称
//RiskLevel int `json:"risk_level"` // 风险等级,1:低风险,2:一般风险,3:较大风险,4:重大风险
//Percentage string `json:"percentage"` // 百分比
Value int `json:"value"`
}
type AlertStatusDistribution struct {
Name string `json:"name"` // 名称
//Status int `json:"status"` // 状态,1:已恢复 2:未恢复 3:已关闭
//Percentage string `json:"percentage"` // 百分比
Value int `json:"value"`
}
type AlertClassDistribution struct {
Name string `json:"name"` // 名称
//ClassId int `json:"class_id"` // 预警分类id
//Percentage string `json:"percentage"` // 百分比
Value int `json:"value"`
}
......
......@@ -62,3 +62,46 @@ type Hits struct {
MaxScore float64 `json:"max_score"`
Hits []SubHits `json:"hits"`
}
type AggAlertOverview struct {
Group struct {
DocCountErrorUpperBound int `json:"doc_count_error_upper_bound"`
SumOtherDocCount int `json:"sum_other_doc_count"`
Buckets []struct {
Key int `json:"key"`
DocCount int `json:"doc_count"`
Group struct {
DocCountErrorUpperBound int `json:"doc_count_error_upper_bound"`
SumOtherDocCount int `json:"sum_other_doc_count"`
Buckets []struct {
Key string `json:"key"`
DocCount int `json:"doc_count"`
UnresolvedCount struct {
DocCount int `json:"doc_count"`
} `json:"unresolved_count"`
} `json:"buckets"`
} `json:"group"`
} `json:"buckets"`
} `json:"group"`
}
type AlertDistributionGroup struct {
Group struct {
DocCountErrorUpperBound int `json:"doc_count_error_upper_bound"`
SumOtherDocCount int `json:"sum_other_doc_count"`
Buckets []struct {
Key int `json:"key"`
DocCount int `json:"doc_count"`
} `json:"buckets"`
} `json:"group"`
}
type DateHistogramGroup struct {
Group struct {
Buckets []struct {
KeyAsString string `json:"key_as_string"`
Key int64 `json:"key"`
DocCount int `json:"doc_count"`
} `json:"buckets"`
} `json:"group"`
}
......@@ -6,9 +6,9 @@ import (
type AlertOverviewItem struct {
AlertOverview []entity.AlertOverview `json:"alert_overview"`
RiskLevelDistribution []entity.RiskLevelDistribution `json:"risk_level_distribution"`
AlertStatusDistribution []entity.AlertStatusDistribution `json:"alert_status_distribution"`
AlertClassDistribution []entity.AlertClassDistribution `json:"alert_class_distribution"`
RiskLevelDistribution []entity.AlertDistribution `json:"risk_level_distribution"`
AlertStatusDistribution []entity.AlertDistribution `json:"alert_status_distribution"`
AlertClassDistribution []entity.AlertDistribution `json:"alert_class_distribution"`
AlertFrequencyDistribution entity.AlertFrequencyDistribution `json:"alert_frequency_distribution"`
}
......
......@@ -148,7 +148,7 @@ func RiskLeveText(code int) string {
case RiskLevelCritical:
return "重大风险"
default:
return ""
return "未知"
}
}
......@@ -159,6 +159,19 @@ const (
AlertClosed
)
func AlertStatusText(code int) string {
switch code {
case AlertRecovered:
return "已恢复"
case AlertNotRecovered:
return "未恢复"
case AlertClosed:
return "已关闭"
default:
return "未知"
}
}
// 是否处置(工单管理),1:已处置,2:未处置
const (
IsDisposedYes = 1
......@@ -172,7 +185,7 @@ func DisposedStatusText(code int) string {
case IsDisposedNo:
return "未处置"
default:
return "未知状态"
return "未知"
}
}
......
......@@ -155,6 +155,18 @@ func (a *AlertClassSvc) List(req request.ListAlertClass) (resp response.AlertCla
return
}
func (a *AlertClassSvc) AlertObjectList() (resp response.AlertClassList, err error) {
db, err := client.GetDbClient()
if err != nil {
return
}
session := db.NewSession()
defer session.Close()
session.Where("parent_id <> 0")
resp.TotalCount, err = session.OrderBy("sort_order").FindAndCount(&resp.List)
return
}
func (a *AlertClassSvc) Tree(req request.ListAlertClass) (resp []*response.AlertClassNode, err error) {
db, err := client.GetDbClient()
if err != nil {
......
package service
import (
"context"
"errors"
"fmt"
"github.com/jinzhu/copier"
json "github.com/json-iterator/go"
"github.com/opensearch-project/opensearch-go/opensearchapi"
"github.com/spf13/cast"
"github.com/tidwall/gjson"
"gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/entity"
"gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/vo/request"
"gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/vo/response"
"gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/client"
"gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/constant"
"gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/jsontime"
"io"
"net/http"
"strings"
"time"
"xorm.io/xorm"
)
......@@ -37,207 +49,465 @@ func (a *AlertOverviewSvc) Update(session *xorm.Session, req request.UpdateAlert
}
func (a *AlertOverviewSvc) Overview(req request.DetailAlertOverview) (resp response.AlertOverviewItem, err error) {
now := jsontime.Now()
alertOverviewList, _ := a.AlertOverview(req)
metricConfigSvc := MetricConfigSvc{User: a.User}
nameList, _ := metricConfigSvc.NameList()
for i := 0; i < len(alertOverviewList); i++ {
for j := 0; j < len(alertOverviewList[i].List); j++ {
for _, v := range nameList.List {
if v.Id == alertOverviewList[i].List[j].MetricName {
alertOverviewList[i].List[j].MetricName = v.MetricName
break
}
}
}
}
riskLevelDistributions, _ := a.RiskLevelDistribution(req)
alertStatusDistributions, _ := a.AlertStatusDistribution(req)
alertClassDistributions, _ := a.AlertClassDistribution(req)
alertFrequencyDistribution, _ := a.AlertFrequencyDistribution(req)
resp = response.AlertOverviewItem{
AlertOverview: []entity.AlertOverview{
{
RiskLevel: 4,
UnresolvedCount: 10,
TotalCount: 24,
List: []entity.AlertArray{
{
MetricName: "CPU使用率过高",
UnresolvedCount: 4,
TotalCount: 8,
},
{
MetricName: "内存不足",
UnresolvedCount: 1,
TotalCount: 2,
},
{
MetricName: "磁盘空间不足",
UnresolvedCount: 3,
TotalCount: 4,
},
{
MetricName: "服务中断",
UnresolvedCount: 1,
TotalCount: 4,
},
{
MetricName: "响应时间超时",
UnresolvedCount: 2,
TotalCount: 6,
},
},
CreatedBy: "admin",
CreatedAt: now,
UpdatedBy: "admin",
UpdatedAt: now,
},
{
RiskLevel: 3,
UnresolvedCount: 8,
TotalCount: 26,
List: []entity.AlertArray{
{
MetricName: "CPU使用率过高",
UnresolvedCount: 4,
TotalCount: 12,
},
{
MetricName: "内存不足",
UnresolvedCount: 1,
TotalCount: 10,
},
{
MetricName: "磁盘空间不足",
UnresolvedCount: 3,
TotalCount: 8,
},
{
MetricName: "服务中断",
UnresolvedCount: 1,
TotalCount: 4,
},
{
MetricName: "响应时间超时",
UnresolvedCount: 2,
TotalCount: 6,
},
},
CreatedBy: "admin",
CreatedAt: now,
UpdatedBy: "admin",
UpdatedAt: now,
},
{
RiskLevel: 2,
UnresolvedCount: 13,
TotalCount: 50,
List: []entity.AlertArray{
{
MetricName: "CPU使用率过高",
UnresolvedCount: 4,
TotalCount: 12,
},
{
MetricName: "内存不足",
UnresolvedCount: 1,
TotalCount: 10,
},
{
MetricName: "磁盘空间不足",
UnresolvedCount: 3,
TotalCount: 8,
},
{
MetricName: "服务中断",
UnresolvedCount: 1,
TotalCount: 4,
},
{
MetricName: "响应时间超时",
UnresolvedCount: 2,
TotalCount: 6,
},
},
CreatedBy: "admin",
CreatedAt: now,
UpdatedBy: "admin",
UpdatedAt: now,
},
{
RiskLevel: 1,
UnresolvedCount: 8,
TotalCount: 20,
List: []entity.AlertArray{
{
MetricName: "CPU使用率过高",
UnresolvedCount: 4,
TotalCount: 12,
},
{
MetricName: "内存不足",
UnresolvedCount: 1,
TotalCount: 10,
},
{
MetricName: "磁盘空间不足",
UnresolvedCount: 3,
TotalCount: 8,
},
{
MetricName: "服务中断",
UnresolvedCount: 1,
TotalCount: 4,
},
{
MetricName: "响应时间超时",
UnresolvedCount: 2,
TotalCount: 6,
},
AlertOverview: alertOverviewList,
RiskLevelDistribution: riskLevelDistributions,
AlertStatusDistribution: alertStatusDistributions,
AlertClassDistribution: alertClassDistributions,
AlertFrequencyDistribution: alertFrequencyDistribution,
}
return
}
func (a *AlertOverviewSvc) AlertOverview(req request.DetailAlertOverview) (resp []entity.AlertOverview, err error) {
var (
sources response.AggAlertOverview
)
cli, err := client.GetOpenSearch()
if err != nil {
return
}
if req.StartTime == "" {
req.StartTime = time.Now().Format("2006-01-02")
}
if req.EndTime == "" {
req.EndTime = time.Now().Add(time.Hour * 24).Format("2006-01-02")
}
content := strings.NewReader(fmt.Sprintf(`{
"size": 0,
"query": {
"range": {
"created_at": {
"gte": "%s",
"lte": "%s"
}
}
},
CreatedBy: "admin",
"aggs": {
"group": {
"terms": {
"field": "risk_level",
"size": 10
},
"aggs": {
"group": {
"terms": {
"field": "metric_config_id",
"size": 10
},
"aggs": {
"unresolved_count": {
"filter": {
"term": {
"status": 2
}
}
}
}
}
}
}
}
}`, req.StartTime, req.EndTime))
res := opensearchapi.SearchRequest{
Index: []string{OpenSearchIndex},
Body: content,
}
do, err := res.Do(context.Background(), cli)
if err != nil {
return
}
defer do.Body.Close()
if do.StatusCode < http.StatusOK && do.StatusCode > http.StatusIMUsed {
err = errors.New(do.String())
return
}
body, err := io.ReadAll(do.Body)
if err != nil {
return
}
result := gjson.GetBytes(body, "aggregations")
err = json.Unmarshal([]byte(result.String()), &sources)
if err != nil {
return
}
now := jsontime.Now()
for _, v := range sources.Group.Buckets {
alertOverview := entity.AlertOverview{
RiskLevel: v.Key,
UnresolvedCount: 0,
TotalCount: v.DocCount,
List: nil,
CreatedBy: a.User.SystemAccount,
CreatedAt: now,
UpdatedBy: "admin",
UpdatedBy: a.User.SystemAccount,
UpdatedAt: now,
}
var unresolvedCount int
var alertArray []entity.AlertArray
for _, bucket := range v.Group.Buckets {
alertArray = append(alertArray, entity.AlertArray{
MetricName: bucket.Key,
UnresolvedCount: bucket.UnresolvedCount.DocCount,
TotalCount: bucket.DocCount,
})
unresolvedCount += bucket.UnresolvedCount.DocCount
}
alertOverview.UnresolvedCount = unresolvedCount
alertOverview.List = alertArray
resp = append(resp, alertOverview)
}
return
}
func (a *AlertOverviewSvc) RiskLevelDistribution(req request.DetailAlertOverview) (resp []entity.AlertDistribution, err error) {
var (
sources response.AlertDistributionGroup
)
cli, err := client.GetOpenSearch()
if err != nil {
return
}
if req.StartTime == "" {
req.StartTime = time.Now().Format("2006-01-02")
}
if req.EndTime == "" {
req.EndTime = time.Now().Add(time.Hour * 24).Format("2006-01-02")
}
content := strings.NewReader(fmt.Sprintf(`{
"size": 0,
"query": {
"range": {
"created_at": {
"gte": "%s",
"lte": "%s"
}
}
},
"aggs": {
"group": {
"terms": {
"field": "risk_level",
"size": 10
}
}
}
}`, req.StartTime, req.EndTime))
res := opensearchapi.SearchRequest{
Index: []string{OpenSearchIndex},
Body: content,
}
do, err := res.Do(context.Background(), cli)
if err != nil {
return
}
defer do.Body.Close()
if do.StatusCode < http.StatusOK && do.StatusCode > http.StatusIMUsed {
err = errors.New(do.String())
return
}
body, err := io.ReadAll(do.Body)
if err != nil {
return
}
result := gjson.GetBytes(body, "aggregations")
err = json.Unmarshal([]byte(result.String()), &sources)
if err != nil {
return
}
var alertDistributions []entity.AlertDistribution
riskLevels := []int{constant.RiskLevelLow, constant.RiskLevelModerate, constant.RiskLevelHigh, constant.RiskLevelCritical}
for _, level := range riskLevels {
riskLevelDistribution := entity.AlertDistribution{Name: constant.RiskLeveText(level)}
for _, bucket := range sources.Group.Buckets {
if level == bucket.Key {
riskLevelDistribution.Value = bucket.DocCount
break
}
}
alertDistributions = append(alertDistributions, riskLevelDistribution)
}
resp = alertDistributions
return
}
func (a *AlertOverviewSvc) AlertStatusDistribution(req request.DetailAlertOverview) (resp []entity.AlertDistribution, err error) {
var (
sources response.AlertDistributionGroup
)
cli, err := client.GetOpenSearch()
if err != nil {
return
}
if req.StartTime == "" {
req.StartTime = time.Now().Format("2006-01-02")
}
if req.EndTime == "" {
req.EndTime = time.Now().Add(time.Hour * 24).Format("2006-01-02")
}
content := strings.NewReader(fmt.Sprintf(`{
"size": 0,
"query": {
"range": {
"created_at": {
"gte": "%s",
"lte": "%s"
}
}
},
RiskLevelDistribution: []entity.RiskLevelDistribution{
{
Name: "重大风险",
Value: 1,
},
{
Name: "较大风险",
Value: 2,
},
{
Name: "一般风险",
Value: 3,
},
{
Name: "低风险",
Value: 4,
},
},
AlertStatusDistribution: []entity.AlertStatusDistribution{
{
Name: "未恢复",
Value: 4,
},
{
Name: "已恢复",
Value: 6,
},
},
AlertClassDistribution: []entity.AlertClassDistribution{
{
Name: "容器集群",
Value: 1,
},
{
Name: "容器节点",
Value: 2,
},
{
Name: "容器组",
Value: 3,
},
{
Name: "网关",
Value: 4,
"aggs": {
"group": {
"terms": {
"field": "status",
"size": 10
}
}
}
}`, req.StartTime, req.EndTime))
res := opensearchapi.SearchRequest{
Index: []string{OpenSearchIndex},
Body: content,
}
do, err := res.Do(context.Background(), cli)
if err != nil {
return
}
defer do.Body.Close()
if do.StatusCode < http.StatusOK && do.StatusCode > http.StatusIMUsed {
err = errors.New(do.String())
return
}
body, err := io.ReadAll(do.Body)
if err != nil {
return
}
result := gjson.GetBytes(body, "aggregations")
err = json.Unmarshal([]byte(result.String()), &sources)
if err != nil {
return
}
var alertDistributions []entity.AlertDistribution
alertStatusList := []int{constant.AlertRecovered, constant.AlertNotRecovered, constant.AlertClosed}
for _, status := range alertStatusList {
alertStatusDistribution := entity.AlertDistribution{Name: constant.AlertStatusText(status)}
for _, bucket := range sources.Group.Buckets {
if status == bucket.Key {
alertStatusDistribution.Value = bucket.DocCount
break
}
}
alertDistributions = append(alertDistributions, alertStatusDistribution)
}
resp = alertDistributions
return
}
func (a *AlertOverviewSvc) AlertClassDistribution(req request.DetailAlertOverview) (resp []entity.AlertDistribution, err error) {
var (
sources response.AlertDistributionGroup
)
cli, err := client.GetOpenSearch()
if err != nil {
return
}
if req.StartTime == "" {
req.StartTime = time.Now().Format("2006-01-02")
}
if req.EndTime == "" {
req.EndTime = time.Now().Add(time.Hour * 24).Format("2006-01-02")
}
content := strings.NewReader(fmt.Sprintf(`{
"size": 0,
"query": {
"range": {
"created_at": {
"gte": "%s",
"lte": "%s"
}
}
},
"aggs": {
"group": {
"terms": {
"field": "class_id",
"size": 10
}
}
}
}`, req.StartTime, req.EndTime))
res := opensearchapi.SearchRequest{
Index: []string{OpenSearchIndex},
Body: content,
}
do, err := res.Do(context.Background(), cli)
if err != nil {
return
}
defer do.Body.Close()
if do.StatusCode < http.StatusOK && do.StatusCode > http.StatusIMUsed {
err = errors.New(do.String())
return
}
body, err := io.ReadAll(do.Body)
if err != nil {
return
}
result := gjson.GetBytes(body, "aggregations")
err = json.Unmarshal([]byte(result.String()), &sources)
if err != nil {
return
}
alertClassSvc := AlertClassSvc{User: a.User}
alertObjectList, err := alertClassSvc.AlertObjectList()
var alertDistributions []entity.AlertDistribution
for _, bucket := range sources.Group.Buckets {
alertDistribution := entity.AlertDistribution{Value: bucket.DocCount}
for _, object := range alertObjectList.List {
if bucket.Key == object.ClassId {
alertDistribution.Name = object.ClassName
}
}
alertDistributions = append(alertDistributions, alertDistribution)
}
resp = alertDistributions
return
}
func (a *AlertOverviewSvc) AlertFrequencyDistribution(req request.DetailAlertOverview) (resp entity.AlertFrequencyDistribution, err error) {
var (
sources response.DateHistogramGroup
)
cli, err := client.GetOpenSearch()
if err != nil {
return
}
if req.StartTime == "" {
req.StartTime = time.Now().Format("2006-01-02")
}
if req.EndTime == "" {
req.EndTime = time.Now().Add(time.Hour * 24).Format("2006-01-02")
}
/*
"gte": "now/d",
"lt": "now+1d/d"
*/
/*
"gte": "2023-07-17",
"lte": "2023-07-18"
*/
content := strings.NewReader(`{
"size": 0,
"query": {
"range": {
"created_at": {
"gte": "now/d",
"lt": "now+1d/d"
}
}
},
AlertFrequencyDistribution: entity.AlertFrequencyDistribution{
"aggs": {
"group": {
"date_histogram": {
"field": "created_at",
"interval": "3h",
"format": "HH",
"time_zone": "+00:00"
}
}
}
}`)
res := opensearchapi.SearchRequest{
Index: []string{OpenSearchIndex},
Body: content,
}
do, err := res.Do(context.Background(), cli)
if err != nil {
return
}
defer do.Body.Close()
if do.StatusCode < http.StatusOK && do.StatusCode > http.StatusIMUsed {
err = errors.New(do.String())
return
}
body, err := io.ReadAll(do.Body)
if err != nil {
return
}
result := gjson.GetBytes(body, "aggregations")
err = json.Unmarshal([]byte(result.String()), &sources)
if err != nil {
return
}
alertFrequencyDistribution := entity.AlertFrequencyDistribution{
XAxis: []string{"0-3时", "3-6时", "6-9时", "9-12时", "12-15时", "15-18时", "18-21时", "21-24时"},
Data: []int{12, 20, 11, 50, 60, 30, 16, 6},
},
Data: []int{0, 0, 0, 0, 0, 0, 0, 0},
}
for i := 0; i < len(alertFrequencyDistribution.XAxis); i++ {
for _, bucket := range sources.Group.Buckets {
if cast.ToInt(bucket.KeyAsString) == i*3 {
alertFrequencyDistribution.Data[i] = bucket.DocCount
break
}
}
}
resp = alertFrequencyDistribution
return
}
func (a *AlertOverviewSvc) List(req request.ListAlertOverview) (resp response.AlertOverviewList, err error) {
......
......@@ -93,6 +93,17 @@ func (m *MetricConfigSvc) List(req request.ListMetricConfig) (resp response.Metr
return
}
func (m *MetricConfigSvc) NameList() (resp response.MetricConfigList, err error) {
db, err := client.GetDbClient()
if err != nil {
return
}
session := db.NewSession()
defer session.Close()
resp.TotalCount, err = session.Select("id,metric_name").FindAndCount(&resp.List)
return
}
func (m *MetricConfigSvc) Delete(ids []string) (err error) {
db, err := client.GetDbClient()
if err != nil {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment