diff --git a/ee/query-service/app/server.go b/ee/query-service/app/server.go index 02fecb50e9..6a16d673d0 100644 --- a/ee/query-service/app/server.go +++ b/ee/query-service/app/server.go @@ -424,7 +424,7 @@ func extractQueryRangeV3Data(path string, r *http.Request) (map[string]interface data["queryType"] = postData.CompositeQuery.QueryType data["panelType"] = postData.CompositeQuery.PanelType - signozLogsUsed, signozMetricsUsed = telemetry.GetInstance().CheckSigNozSignals(postData) + signozLogsUsed, signozMetricsUsed, _ = telemetry.GetInstance().CheckSigNozSignals(postData) } } diff --git a/pkg/query-service/app/clickhouseReader/reader.go b/pkg/query-service/app/clickhouseReader/reader.go index 0d122faf06..c415950b8c 100644 --- a/pkg/query-service/app/clickhouseReader/reader.go +++ b/pkg/query-service/app/clickhouseReader/reader.go @@ -3480,14 +3480,37 @@ func (r *ClickHouseReader) GetDashboardsInfo(ctx context.Context) (*model.Dashbo zap.S().Debug("Error in processing sql query: ", err) return &dashboardsInfo, err } + totalDashboardsWithPanelAndName := 0 for _, dashboard := range dashboardsData { + if isDashboardWithPanelAndName(dashboard.Data) { + totalDashboardsWithPanelAndName = totalDashboardsWithPanelAndName + 1 + } dashboardsInfo = countPanelsInDashboard(dashboard.Data) } dashboardsInfo.TotalDashboards = len(dashboardsData) - + dashboardsInfo.TotalDashboardsWithPanelAndName = totalDashboardsWithPanelAndName return &dashboardsInfo, nil } +func isDashboardWithPanelAndName(data map[string]interface{}) bool { + isDashboardName := false + isDashboardWithPanelAndName := false + if data != nil && data["title"] != nil && data["widgets"] != nil { + title, ok := data["title"].(string) + if ok && title != "Sample Title" { + isDashboardName = true + } + widgets, ok := data["widgets"].(interface{}) + if ok && isDashboardName { + data, ok := widgets.([]interface{}) + if ok && len(data) > 0 { + isDashboardWithPanelAndName = true + } + } + } + + return isDashboardWithPanelAndName +} func countPanelsInDashboard(data map[string]interface{}) model.DashboardsInfo { var logsPanelCount, tracesPanelCount, metricsPanelCount int // totalPanels := 0 diff --git a/pkg/query-service/app/http_handler.go b/pkg/query-service/app/http_handler.go index 7cfdc7b39f..1fff220f05 100644 --- a/pkg/query-service/app/http_handler.go +++ b/pkg/query-service/app/http_handler.go @@ -8,6 +8,7 @@ import ( "fmt" "io" "net/http" + "regexp" "strconv" "strings" "sync" @@ -3332,6 +3333,7 @@ func (aH *APIHandler) queryRangeV3(ctx context.Context, queryRangeParams *v3.Que applyMetricLimit(result, queryRangeParams) + sendQueryResultEvents(r, result, queryRangeParams) // only adding applyFunctions instead of postProcess since experssion are // are executed in clickhouse directly and we wanted to add support for timeshift if queryRangeParams.CompositeQuery.QueryType == v3.QueryTypeBuilder { @@ -3343,7 +3345,7 @@ func (aH *APIHandler) queryRangeV3(ctx context.Context, queryRangeParams *v3.Que } // This checks if the time for context to complete has exceeded. - // it adds flag to notify the user of incomplete respone + // it adds flag to notify the user of incomplete response select { case <-ctx.Done(): resp.ContextTimeout = true @@ -3355,6 +3357,50 @@ func (aH *APIHandler) queryRangeV3(ctx context.Context, queryRangeParams *v3.Que aH.Respond(w, resp) } +func sendQueryResultEvents(r *http.Request, result []*v3.Result, queryRangeParams *v3.QueryRangeParamsV3) { + referrer := r.Header.Get("Referer") + + dashboardMatched, err := regexp.MatchString(`/dashboard/[a-zA-Z0-9\-]+/(new|edit)(?:\?.*)?$`, referrer) + if err != nil { + zap.S().Errorf("error while matching the referrer: %v", err) + } + alertMatched, err := regexp.MatchString(`/alerts/(new|edit)(?:\?.*)?$`, referrer) + if err != nil { + zap.S().Errorf("error while matching the referrer: %v", err) + } + + if alertMatched || dashboardMatched { + + if len(result) > 0 && (len(result[0].Series) > 0 || len(result[0].List) > 0) { + + userEmail, err := auth.GetEmailFromJwt(r.Context()) + if err == nil { + signozLogsUsed, signozMetricsUsed, signozTracesUsed := telemetry.GetInstance().CheckSigNozSignals(queryRangeParams) + if signozLogsUsed || signozMetricsUsed || signozTracesUsed { + if dashboardMatched { + telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_SUCCESSFUL_DASHBOARD_PANEL_QUERY, map[string]interface{}{ + "queryType": queryRangeParams.CompositeQuery.QueryType, + "panelType": queryRangeParams.CompositeQuery.PanelType, + "tracesUsed": signozTracesUsed, + "logsUsed": signozLogsUsed, + "metricsUsed": signozMetricsUsed, + }, userEmail) + } + if alertMatched { + telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_SUCCESSFUL_ALERT_QUERY, map[string]interface{}{ + "queryType": queryRangeParams.CompositeQuery.QueryType, + "panelType": queryRangeParams.CompositeQuery.PanelType, + "tracesUsed": signozTracesUsed, + "logsUsed": signozLogsUsed, + "metricsUsed": signozMetricsUsed, + }, userEmail) + } + } + } + } + } +} + func (aH *APIHandler) QueryRangeV3(w http.ResponseWriter, r *http.Request) { queryRangeParams, apiErrorObj := ParseQueryRangeParams(r) @@ -3513,7 +3559,7 @@ func (aH *APIHandler) queryRangeV4(ctx context.Context, queryRangeParams *v3.Que RespondError(w, apiErrObj, errQuriesByName) return } - + sendQueryResultEvents(r, result, queryRangeParams) resp := v3.QueryRangeResponse{ Result: result, } diff --git a/pkg/query-service/app/server.go b/pkg/query-service/app/server.go index 9d934d0632..773f211c05 100644 --- a/pkg/query-service/app/server.go +++ b/pkg/query-service/app/server.go @@ -371,7 +371,7 @@ func extractQueryRangeV3Data(path string, r *http.Request) (map[string]interface data["queryType"] = postData.CompositeQuery.QueryType data["panelType"] = postData.CompositeQuery.PanelType - signozLogsUsed, signozMetricsUsed = telemetry.GetInstance().CheckSigNozSignals(postData) + signozLogsUsed, signozMetricsUsed, _ = telemetry.GetInstance().CheckSigNozSignals(postData) } } diff --git a/pkg/query-service/model/response.go b/pkg/query-service/model/response.go index ae99473720..0362ec6a35 100644 --- a/pkg/query-service/model/response.go +++ b/pkg/query-service/model/response.go @@ -624,10 +624,11 @@ type AlertsInfo struct { } type DashboardsInfo struct { - TotalDashboards int `json:"totalDashboards"` - LogsBasedPanels int `json:"logsBasedPanels"` - MetricBasedPanels int `json:"metricBasedPanels"` - TracesBasedPanels int `json:"tracesBasedPanels"` + TotalDashboards int `json:"totalDashboards"` + TotalDashboardsWithPanelAndName int `json:"totalDashboardsWithPanelAndName"` // dashboards with panel and name without sample title + LogsBasedPanels int `json:"logsBasedPanels"` + MetricBasedPanels int `json:"metricBasedPanels"` + TracesBasedPanels int `json:"tracesBasedPanels"` } type TagTelemetryData struct { diff --git a/pkg/query-service/telemetry/telemetry.go b/pkg/query-service/telemetry/telemetry.go index ea93d75a0a..ee346d229b 100644 --- a/pkg/query-service/telemetry/telemetry.go +++ b/pkg/query-service/telemetry/telemetry.go @@ -22,41 +22,45 @@ import ( ) const ( - TELEMETRY_EVENT_PATH = "API Call" - TELEMETRY_EVENT_USER = "User" - TELEMETRY_EVENT_INPRODUCT_FEEDBACK = "InProduct Feedback Submitted" - TELEMETRY_EVENT_NUMBER_OF_SERVICES = "Number of Services" - TELEMETRY_EVENT_NUMBER_OF_SERVICES_PH = "Number of Services V2" - TELEMETRY_EVENT_HEART_BEAT = "Heart Beat" - TELEMETRY_EVENT_ORG_SETTINGS = "Org Settings" - DEFAULT_SAMPLING = 0.1 - TELEMETRY_LICENSE_CHECK_FAILED = "License Check Failed" - TELEMETRY_LICENSE_UPDATED = "License Updated" - TELEMETRY_LICENSE_ACT_FAILED = "License Activation Failed" - TELEMETRY_EVENT_ENVIRONMENT = "Environment" - TELEMETRY_EVENT_LANGUAGE = "Language" - TELEMETRY_EVENT_SERVICE = "ServiceName" - 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" - TELEMETRY_EVENT_USER_INVITATION_ACCEPTED = "User Invitation Accepted" - DEFAULT_CLOUD_EMAIL = "admin@signoz.cloud" + TELEMETRY_EVENT_PATH = "API Call" + TELEMETRY_EVENT_USER = "User" + TELEMETRY_EVENT_INPRODUCT_FEEDBACK = "InProduct Feedback Submitted" + TELEMETRY_EVENT_NUMBER_OF_SERVICES = "Number of Services" + TELEMETRY_EVENT_NUMBER_OF_SERVICES_PH = "Number of Services V2" + TELEMETRY_EVENT_HEART_BEAT = "Heart Beat" + TELEMETRY_EVENT_ORG_SETTINGS = "Org Settings" + DEFAULT_SAMPLING = 0.1 + TELEMETRY_LICENSE_CHECK_FAILED = "License Check Failed" + TELEMETRY_LICENSE_UPDATED = "License Updated" + TELEMETRY_LICENSE_ACT_FAILED = "License Activation Failed" + TELEMETRY_EVENT_ENVIRONMENT = "Environment" + TELEMETRY_EVENT_LANGUAGE = "Language" + TELEMETRY_EVENT_SERVICE = "ServiceName" + 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" + TELEMETRY_EVENT_USER_INVITATION_ACCEPTED = "User Invitation Accepted" + TELEMETRY_EVENT_SUCCESSFUL_DASHBOARD_PANEL_QUERY = "Successful Dashboard Panel Query" + TELEMETRY_EVENT_SUCCESSFUL_ALERT_QUERY = "Successful Alert Query" + DEFAULT_CLOUD_EMAIL = "admin@signoz.cloud" ) var SAAS_EVENTS_LIST = map[string]struct{}{ - TELEMETRY_EVENT_NUMBER_OF_SERVICES: {}, - TELEMETRY_EVENT_ACTIVE_USER: {}, - TELEMETRY_EVENT_HEART_BEAT: {}, - TELEMETRY_EVENT_LANGUAGE: {}, - TELEMETRY_EVENT_SERVICE: {}, - TELEMETRY_EVENT_ENVIRONMENT: {}, - TELEMETRY_EVENT_USER_INVITATION_SENT: {}, - TELEMETRY_EVENT_USER_INVITATION_ACCEPTED: {}, - TELEMETRY_EVENT_DASHBOARDS_ALERTS: {}, + TELEMETRY_EVENT_NUMBER_OF_SERVICES: {}, + TELEMETRY_EVENT_ACTIVE_USER: {}, + TELEMETRY_EVENT_HEART_BEAT: {}, + TELEMETRY_EVENT_LANGUAGE: {}, + TELEMETRY_EVENT_SERVICE: {}, + TELEMETRY_EVENT_ENVIRONMENT: {}, + TELEMETRY_EVENT_USER_INVITATION_SENT: {}, + TELEMETRY_EVENT_USER_INVITATION_ACCEPTED: {}, + TELEMETRY_EVENT_DASHBOARDS_ALERTS: {}, + TELEMETRY_EVENT_SUCCESSFUL_DASHBOARD_PANEL_QUERY: {}, + TELEMETRY_EVENT_SUCCESSFUL_ALERT_QUERY: {}, } const api_key = "4Gmoa4ixJAUHx2BpJxsjwA1bEfnwEeRz" @@ -93,9 +97,10 @@ func (a *Telemetry) IsSampled() bool { } -func (telemetry *Telemetry) CheckSigNozSignals(postData *v3.QueryRangeParamsV3) (bool, bool) { +func (telemetry *Telemetry) CheckSigNozSignals(postData *v3.QueryRangeParamsV3) (bool, bool, bool) { signozLogsUsed := false signozMetricsUsed := false + signozTracesUsed := false if postData.CompositeQuery.QueryType == v3.QueryTypeBuilder { for _, query := range postData.CompositeQuery.BuilderQueries { @@ -105,6 +110,8 @@ func (telemetry *Telemetry) CheckSigNozSignals(postData *v3.QueryRangeParamsV3) !strings.Contains(query.AggregateAttribute.Key, "signoz_") && len(query.AggregateAttribute.Key) > 0 { signozMetricsUsed = true + } else if query.DataSource == v3.DataSourceTraces && len(query.Filters.Items) > 0 { + signozTracesUsed = true } } } else if postData.CompositeQuery.QueryType == v3.QueryTypePromQL { @@ -118,9 +125,15 @@ func (telemetry *Telemetry) CheckSigNozSignals(postData *v3.QueryRangeParamsV3) if strings.Contains(query.Query, "signoz_metrics") && len(query.Query) > 0 { signozMetricsUsed = true } + if strings.Contains(query.Query, "signoz_logs") && len(query.Query) > 0 { + signozLogsUsed = true + } + if strings.Contains(query.Query, "signoz_traces") && len(query.Query) > 0 { + signozTracesUsed = true + } } } - return signozLogsUsed, signozMetricsUsed + return signozLogsUsed, signozMetricsUsed, signozTracesUsed } func (telemetry *Telemetry) AddActiveTracesUser() {