chore: add new dashboard/alerts info events (#4214)

* chore: add new dashboard/alerts info events
This commit is contained in:
Vishal Sharma 2023-12-13 18:14:55 +05:30 committed by GitHub
parent c7b59d4405
commit c66c8c2823
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 142 additions and 15 deletions

View File

@ -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()) userEmail, err := auth.GetEmailFromJwt(r.Context())
if err == nil { if err == nil {
telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_PATH, data, userEmail) telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_PATH, data, userEmail)

View File

@ -43,6 +43,7 @@ import (
promModel "github.com/prometheus/common/model" promModel "github.com/prometheus/common/model"
"go.uber.org/zap" "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/logs"
"go.signoz.io/signoz/pkg/query-service/app/services" "go.signoz.io/signoz/pkg/query-service/app/services"
"go.signoz.io/signoz/pkg/query-service/auth" "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/interfaces"
"go.signoz.io/signoz/pkg/query-service/model" "go.signoz.io/signoz/pkg/query-service/model"
v3 "go.signoz.io/signoz/pkg/query-service/model/v3" 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/telemetry"
"go.signoz.io/signoz/pkg/query-service/utils" "go.signoz.io/signoz/pkg/query-service/utils"
) )
@ -3421,6 +3423,100 @@ func (r *ClickHouseReader) GetTagsInfoInLastHeartBeatInterval(ctx context.Contex
return &tagsInfo, nil 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) { func (r *ClickHouseReader) GetLogFields(ctx context.Context) (*model.GetFieldsResponse, *model.ApiError) {
// response will contain top level fields from the otel log model // response will contain top level fields from the otel log model
response := model.GetFieldsResponse{ response := model.GetFieldsResponse{

View File

@ -417,7 +417,7 @@ func (s *Server) analyticsMiddleware(next http.Handler) http.Handler {
} }
// if telemetry.GetInstance().IsSampled() { // if telemetry.GetInstance().IsSampled() {
if _, ok := telemetry.IgnoredPaths()[path]; !ok { if _, ok := telemetry.EnabledPaths()[path]; ok {
userEmail, err := auth.GetEmailFromJwt(r.Context()) userEmail, err := auth.GetEmailFromJwt(r.Context())
if err == nil { if err == nil {
telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_PATH, data, userEmail) telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_PATH, data, userEmail)

View File

@ -71,6 +71,8 @@ type Reader interface {
GetListResultV3(ctx context.Context, query string) ([]*v3.Row, error) GetListResultV3(ctx context.Context, query string) ([]*v3.Row, error)
LiveTailLogsV3(ctx context.Context, query string, timestampStart uint64, idStart string, client *v3.LogsLiveTailClient) 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) GetTotalSpans(ctx context.Context) (uint64, error)
GetSpansInLastHeartBeatInterval(ctx context.Context) (uint64, error) GetSpansInLastHeartBeatInterval(ctx context.Context) (uint64, error)
GetTimeSeriesInfo(ctx context.Context) (map[string]interface{}, error) GetTimeSeriesInfo(ctx context.Context) (map[string]interface{}, error)

View File

@ -615,6 +615,20 @@ type TagsInfo struct {
Env string `json:"env"` 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 { type TagTelemetryData struct {
ServiceName string `json:"serviceName" ch:"serviceName"` ServiceName string `json:"serviceName" ch:"serviceName"`
Env string `json:"env" ch:"env"` Env string `json:"env" ch:"env"`

View File

@ -14,7 +14,7 @@ import (
func TestThresholdRuleCombinations(t *testing.T) { func TestThresholdRuleCombinations(t *testing.T) {
postableRule := PostableRule{ postableRule := PostableRule{
Alert: "Tricky Condition Tests", Alert: "Tricky Condition Tests",
AlertType: "METRICS_BASED_ALERT", AlertType: "METRIC_BASED_ALERT",
RuleType: RuleTypeThreshold, RuleType: RuleTypeThreshold,
EvalWindow: Duration(5 * time.Minute), EvalWindow: Duration(5 * time.Minute),
Frequency: Duration(1 * time.Minute), Frequency: Duration(1 * time.Minute),

View File

@ -1,16 +1,11 @@
package telemetry package telemetry
func IgnoredPaths() map[string]struct{} { func EnabledPaths() map[string]struct{} {
ignoredPaths := map[string]struct{}{ enabledPaths := map[string]struct{}{
"/api/v1/tags": {}, "/api/v1/channels": {},
"/api/v1/version": {},
"/api/v1/query_range": {},
"/api/v2/metrics/query_range": {},
"/api/v1/health": {},
"/api/v1/featureFlags": {},
} }
return ignoredPaths return enabledPaths
} }
func ignoreEvents(event string, attributes map[string]interface{}) bool { func ignoreEvents(event string, attributes map[string]interface{}) bool {

View File

@ -38,6 +38,7 @@ const (
TELEMETRY_EVENT_LOGS_FILTERS = "Logs Filters" TELEMETRY_EVENT_LOGS_FILTERS = "Logs Filters"
TELEMETRY_EVENT_DISTRIBUTED = "Distributed" TELEMETRY_EVENT_DISTRIBUTED = "Distributed"
TELEMETRY_EVENT_QUERY_RANGE_V3 = "Query Range V3 Metadata" 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 = "Active User"
TELEMETRY_EVENT_ACTIVE_USER_PH = "Active User V2" TELEMETRY_EVENT_ACTIVE_USER_PH = "Active User V2"
TELEMETRY_EVENT_USER_INVITATION_SENT = "User Invitation Sent" TELEMETRY_EVENT_USER_INVITATION_SENT = "User Invitation Sent"
@ -53,6 +54,7 @@ var SAAS_EVENTS_LIST = map[string]struct{}{
TELEMETRY_EVENT_ENVIRONMENT: {}, TELEMETRY_EVENT_ENVIRONMENT: {},
TELEMETRY_EVENT_USER_INVITATION_SENT: {}, TELEMETRY_EVENT_USER_INVITATION_SENT: {},
TELEMETRY_EVENT_USER_INVITATION_ACCEPTED: {}, TELEMETRY_EVENT_USER_INVITATION_ACCEPTED: {},
TELEMETRY_EVENT_DASHBOARDS_ALERTS: {},
} }
const api_key = "4Gmoa4ixJAUHx2BpJxsjwA1bEfnwEeRz" const api_key = "4Gmoa4ixJAUHx2BpJxsjwA1bEfnwEeRz"
@ -61,9 +63,9 @@ const ph_api_key = "H-htDCae7CR3RV57gUzmol6IAKtm5IMCvbcm_fwnL-w"
const IP_NOT_FOUND_PLACEHOLDER = "NA" const IP_NOT_FOUND_PLACEHOLDER = "NA"
const DEFAULT_NUMBER_OF_SERVICES = 6 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 HEART_BEAT_DURATION = 30 * time.Second
// const ACTIVE_USER_DURATION = 30 * time.Second // const ACTIVE_USER_DURATION = 30 * time.Second
@ -241,9 +243,27 @@ func createTelemetry() {
} }
telemetry.SendEvent(TELEMETRY_EVENT_HEART_BEAT, data, "") 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()) getDistributedInfoInLastHeartBeatInterval, _ := telemetry.reader.GetDistributedInfoInLastHeartBeatInterval(context.Background())
telemetry.SendEvent(TELEMETRY_EVENT_DISTRIBUTED, getDistributedInfoInLastHeartBeatInterval, "") telemetry.SendEvent(TELEMETRY_EVENT_DISTRIBUTED, getDistributedInfoInLastHeartBeatInterval, "")
} }
} }
}() }()