diff --git a/ee/query-service/app/server.go b/ee/query-service/app/server.go index bed3855f17d5..699894e691a5 100644 --- a/ee/query-service/app/server.go +++ b/ee/query-service/app/server.go @@ -480,7 +480,7 @@ func (s *Server) analyticsMiddleware(next http.Handler) http.Handler { } } - if _, ok := telemetry.IgnoredPaths()[path]; !ok { + if _, ok := telemetry.EnabledPaths()[path]; ok { userEmail, err := auth.GetEmailFromJwt(r.Context()) if err == nil { telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_PATH, data, userEmail) diff --git a/pkg/query-service/app/clickhouseReader/reader.go b/pkg/query-service/app/clickhouseReader/reader.go index 8b42c9265a98..c8f150cd8579 100644 --- a/pkg/query-service/app/clickhouseReader/reader.go +++ b/pkg/query-service/app/clickhouseReader/reader.go @@ -43,6 +43,7 @@ import ( promModel "github.com/prometheus/common/model" "go.uber.org/zap" + "go.signoz.io/signoz/pkg/query-service/app/dashboards" "go.signoz.io/signoz/pkg/query-service/app/logs" "go.signoz.io/signoz/pkg/query-service/app/services" "go.signoz.io/signoz/pkg/query-service/auth" @@ -51,6 +52,7 @@ import ( "go.signoz.io/signoz/pkg/query-service/interfaces" "go.signoz.io/signoz/pkg/query-service/model" v3 "go.signoz.io/signoz/pkg/query-service/model/v3" + "go.signoz.io/signoz/pkg/query-service/rules" "go.signoz.io/signoz/pkg/query-service/telemetry" "go.signoz.io/signoz/pkg/query-service/utils" ) @@ -3421,6 +3423,100 @@ func (r *ClickHouseReader) GetTagsInfoInLastHeartBeatInterval(ctx context.Contex return &tagsInfo, nil } +// GetDashboardsInfo returns analytics data for dashboards +func (r *ClickHouseReader) GetDashboardsInfo(ctx context.Context) (*model.DashboardsInfo, error) { + dashboardsInfo := model.DashboardsInfo{} + // fetch dashboards from dashboard db + query := "SELECT data FROM dashboards" + var dashboardsData []dashboards.Dashboard + err := r.localDB.Select(&dashboardsData, query) + if err != nil { + zap.S().Debug("Error in processing sql query: ", err) + return &dashboardsInfo, err + } + for _, dashboard := range dashboardsData { + dashboardsInfo = countPanelsInDashboard(dashboard.Data) + } + dashboardsInfo.TotalDashboards = len(dashboardsData) + + return &dashboardsInfo, nil +} + +func countPanelsInDashboard(data map[string]interface{}) model.DashboardsInfo { + var logsPanelCount, tracesPanelCount, metricsPanelCount int + // totalPanels := 0 + if data != nil && data["widgets"] != nil { + widgets, ok := data["widgets"].(interface{}) + if ok { + data, ok := widgets.([]interface{}) + if ok { + for _, widget := range data { + sData, ok := widget.(map[string]interface{}) + if ok && sData["query"] != nil { + // totalPanels++ + query, ok := sData["query"].(interface{}).(map[string]interface{}) + if ok && query["queryType"] == "builder" && query["builder"] != nil { + builderData, ok := query["builder"].(interface{}).(map[string]interface{}) + if ok && builderData["queryData"] != nil { + builderQueryData, ok := builderData["queryData"].([]interface{}) + if ok { + for _, queryData := range builderQueryData { + data, ok := queryData.(map[string]interface{}) + if ok { + if data["dataSource"] == "traces" { + tracesPanelCount++ + } else if data["dataSource"] == "metrics" { + metricsPanelCount++ + } else if data["dataSource"] == "logs" { + logsPanelCount++ + } + } + } + } + } + } + } + } + } + } + } + return model.DashboardsInfo{ + LogsBasedPanels: logsPanelCount, + TracesBasedPanels: tracesPanelCount, + MetricBasedPanels: metricsPanelCount, + } +} + +func (r *ClickHouseReader) GetAlertsInfo(ctx context.Context) (*model.AlertsInfo, error) { + alertsInfo := model.AlertsInfo{} + // fetch alerts from rules db + query := "SELECT data FROM rules" + var alertsData []string + err := r.localDB.Select(&alertsData, query) + if err != nil { + zap.S().Debug("Error in processing sql query: ", err) + return &alertsInfo, err + } + for _, alert := range alertsData { + var rule rules.GettableRule + err = json.Unmarshal([]byte(alert), &rule) + if err != nil { + zap.S().Errorf("msg:", "invalid rule data", "\t err:", err) + continue + } + if rule.AlertType == "LOGS_BASED_ALERT" { + alertsInfo.LogsBasedAlerts = alertsInfo.LogsBasedAlerts + 1 + } else if rule.AlertType == "METRIC_BASED_ALERT" { + alertsInfo.MetricBasedAlerts = alertsInfo.MetricBasedAlerts + 1 + } else if rule.AlertType == "TRACES_BASED_ALERT" { + alertsInfo.TracesBasedAlerts = alertsInfo.TracesBasedAlerts + 1 + } + alertsInfo.TotalAlerts = alertsInfo.TotalAlerts + 1 + } + + return &alertsInfo, nil +} + func (r *ClickHouseReader) GetLogFields(ctx context.Context) (*model.GetFieldsResponse, *model.ApiError) { // response will contain top level fields from the otel log model response := model.GetFieldsResponse{ diff --git a/pkg/query-service/app/server.go b/pkg/query-service/app/server.go index e4e43f164857..f7fa328b9f01 100644 --- a/pkg/query-service/app/server.go +++ b/pkg/query-service/app/server.go @@ -417,7 +417,7 @@ func (s *Server) analyticsMiddleware(next http.Handler) http.Handler { } // if telemetry.GetInstance().IsSampled() { - if _, ok := telemetry.IgnoredPaths()[path]; !ok { + if _, ok := telemetry.EnabledPaths()[path]; ok { userEmail, err := auth.GetEmailFromJwt(r.Context()) if err == nil { telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_PATH, data, userEmail) diff --git a/pkg/query-service/interfaces/interface.go b/pkg/query-service/interfaces/interface.go index b25888f607e9..e2b2b49481f4 100644 --- a/pkg/query-service/interfaces/interface.go +++ b/pkg/query-service/interfaces/interface.go @@ -71,6 +71,8 @@ type Reader interface { GetListResultV3(ctx context.Context, query string) ([]*v3.Row, error) LiveTailLogsV3(ctx context.Context, query string, timestampStart uint64, idStart string, client *v3.LogsLiveTailClient) + GetDashboardsInfo(ctx context.Context) (*model.DashboardsInfo, error) + GetAlertsInfo(ctx context.Context) (*model.AlertsInfo, error) GetTotalSpans(ctx context.Context) (uint64, error) GetSpansInLastHeartBeatInterval(ctx context.Context) (uint64, error) GetTimeSeriesInfo(ctx context.Context) (map[string]interface{}, error) diff --git a/pkg/query-service/model/response.go b/pkg/query-service/model/response.go index 9bb1d985c054..6d5e65d7329c 100644 --- a/pkg/query-service/model/response.go +++ b/pkg/query-service/model/response.go @@ -615,6 +615,20 @@ type TagsInfo struct { Env string `json:"env"` } +type AlertsInfo struct { + TotalAlerts int `json:"totalAlerts"` + LogsBasedAlerts int `json:"logsBasedAlerts"` + MetricBasedAlerts int `json:"metricBasedAlerts"` + TracesBasedAlerts int `json:"tracesBasedAlerts"` +} + +type DashboardsInfo struct { + TotalDashboards int `json:"totalDashboards"` + LogsBasedPanels int `json:"logsBasedPanels"` + MetricBasedPanels int `json:"metricBasedPanels"` + TracesBasedPanels int `json:"tracesBasedPanels"` +} + type TagTelemetryData struct { ServiceName string `json:"serviceName" ch:"serviceName"` Env string `json:"env" ch:"env"` diff --git a/pkg/query-service/rules/thresholdRule_test.go b/pkg/query-service/rules/thresholdRule_test.go index 27ad6611f53e..031a19b70a36 100644 --- a/pkg/query-service/rules/thresholdRule_test.go +++ b/pkg/query-service/rules/thresholdRule_test.go @@ -14,7 +14,7 @@ import ( func TestThresholdRuleCombinations(t *testing.T) { postableRule := PostableRule{ Alert: "Tricky Condition Tests", - AlertType: "METRICS_BASED_ALERT", + AlertType: "METRIC_BASED_ALERT", RuleType: RuleTypeThreshold, EvalWindow: Duration(5 * time.Minute), Frequency: Duration(1 * time.Minute), diff --git a/pkg/query-service/telemetry/ignored.go b/pkg/query-service/telemetry/ignored.go index 29c06fe1acb4..c0a739e9eea7 100644 --- a/pkg/query-service/telemetry/ignored.go +++ b/pkg/query-service/telemetry/ignored.go @@ -1,16 +1,11 @@ package telemetry -func IgnoredPaths() map[string]struct{} { - ignoredPaths := map[string]struct{}{ - "/api/v1/tags": {}, - "/api/v1/version": {}, - "/api/v1/query_range": {}, - "/api/v2/metrics/query_range": {}, - "/api/v1/health": {}, - "/api/v1/featureFlags": {}, +func EnabledPaths() map[string]struct{} { + enabledPaths := map[string]struct{}{ + "/api/v1/channels": {}, } - return ignoredPaths + return enabledPaths } func ignoreEvents(event string, attributes map[string]interface{}) bool { diff --git a/pkg/query-service/telemetry/telemetry.go b/pkg/query-service/telemetry/telemetry.go index 24a2dbc4e2da..206e200f5cd8 100644 --- a/pkg/query-service/telemetry/telemetry.go +++ b/pkg/query-service/telemetry/telemetry.go @@ -38,6 +38,7 @@ const ( TELEMETRY_EVENT_LOGS_FILTERS = "Logs Filters" TELEMETRY_EVENT_DISTRIBUTED = "Distributed" TELEMETRY_EVENT_QUERY_RANGE_V3 = "Query Range V3 Metadata" + TELEMETRY_EVENT_DASHBOARDS_ALERTS = "Dashboards/Alerts Info" TELEMETRY_EVENT_ACTIVE_USER = "Active User" TELEMETRY_EVENT_ACTIVE_USER_PH = "Active User V2" TELEMETRY_EVENT_USER_INVITATION_SENT = "User Invitation Sent" @@ -53,6 +54,7 @@ var SAAS_EVENTS_LIST = map[string]struct{}{ TELEMETRY_EVENT_ENVIRONMENT: {}, TELEMETRY_EVENT_USER_INVITATION_SENT: {}, TELEMETRY_EVENT_USER_INVITATION_ACCEPTED: {}, + TELEMETRY_EVENT_DASHBOARDS_ALERTS: {}, } const api_key = "4Gmoa4ixJAUHx2BpJxsjwA1bEfnwEeRz" @@ -61,9 +63,9 @@ const ph_api_key = "H-htDCae7CR3RV57gUzmol6IAKtm5IMCvbcm_fwnL-w" const IP_NOT_FOUND_PLACEHOLDER = "NA" const DEFAULT_NUMBER_OF_SERVICES = 6 -const HEART_BEAT_DURATION = 6 * time.Hour +const HEART_BEAT_DURATION = 12 * time.Hour -const ACTIVE_USER_DURATION = 30 * time.Minute +const ACTIVE_USER_DURATION = 6 * time.Hour // const HEART_BEAT_DURATION = 30 * time.Second // const ACTIVE_USER_DURATION = 30 * time.Second @@ -241,9 +243,27 @@ func createTelemetry() { } telemetry.SendEvent(TELEMETRY_EVENT_HEART_BEAT, data, "") + alertsInfo, err := telemetry.reader.GetAlertsInfo(context.Background()) + dashboardsInfo, err := telemetry.reader.GetDashboardsInfo(context.Background()) + + if err == nil { + dashboardsAlertsData := map[string]interface{}{ + "totalDashboards": dashboardsInfo.TotalDashboards, + "logsBasedPanels": dashboardsInfo.LogsBasedPanels, + "metricBasedPanels": dashboardsInfo.MetricBasedPanels, + "tracesBasedPanels": dashboardsInfo.TracesBasedPanels, + "totalAlerts": alertsInfo.TotalAlerts, + "logsBasedAlerts": alertsInfo.LogsBasedAlerts, + "metricBasedAlerts": alertsInfo.MetricBasedAlerts, + "tracesBasedAlerts": alertsInfo.TracesBasedAlerts, + } + telemetry.SendEvent(TELEMETRY_EVENT_DASHBOARDS_ALERTS, dashboardsAlertsData, "") + } else { + telemetry.SendEvent(TELEMETRY_EVENT_DASHBOARDS_ALERTS, map[string]interface{}{"error": err.Error()}, "") + } + getDistributedInfoInLastHeartBeatInterval, _ := telemetry.reader.GetDistributedInfoInLastHeartBeatInterval(context.Background()) telemetry.SendEvent(TELEMETRY_EVENT_DISTRIBUTED, getDistributedInfoInLastHeartBeatInterval, "") - } } }()