diff --git a/ee/query-service/app/server.go b/ee/query-service/app/server.go index c742eef01b..dfdff14939 100644 --- a/ee/query-service/app/server.go +++ b/ee/query-service/app/server.go @@ -445,7 +445,7 @@ func extractQueryRangeV3Data(path string, r *http.Request) (map[string]interface data["tracesUsed"] = signozTracesUsed userEmail, err := baseauth.GetEmailFromJwt(r.Context()) if err == nil { - telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_QUERY_RANGE_API, data, userEmail) + telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_QUERY_RANGE_API, data, userEmail, true, false) } } return data, true @@ -488,7 +488,7 @@ func (s *Server) analyticsMiddleware(next http.Handler) http.Handler { if _, ok := telemetry.EnabledPaths()[path]; ok { userEmail, err := baseauth.GetEmailFromJwt(r.Context()) if err == nil { - telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_PATH, data, userEmail) + telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_PATH, data, userEmail, true, false) } } diff --git a/ee/query-service/license/manager.go b/ee/query-service/license/manager.go index 56cb685fec..d348b6d216 100644 --- a/ee/query-service/license/manager.go +++ b/ee/query-service/license/manager.go @@ -204,7 +204,7 @@ func (lm *Manager) Validate(ctx context.Context) (reterr error) { zap.L().Error("License validation completed with error", zap.Error(reterr)) atomic.AddUint64(&lm.failedAttempts, 1) telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_LICENSE_CHECK_FAILED, - map[string]interface{}{"err": reterr.Error()}, "") + map[string]interface{}{"err": reterr.Error()}, "", true, false) } else { zap.L().Info("License validation completed with no errors") } @@ -263,7 +263,7 @@ func (lm *Manager) Activate(ctx context.Context, key string) (licenseResponse *m userEmail, err := auth.GetEmailFromJwt(ctx) if err == nil { telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_LICENSE_ACT_FAILED, - map[string]interface{}{"err": errResponse.Err.Error()}, userEmail) + map[string]interface{}{"err": errResponse.Err.Error()}, userEmail, true, false) } } }() diff --git a/pkg/query-service/app/clickhouseReader/reader.go b/pkg/query-service/app/clickhouseReader/reader.go index a1b12d9415..1f5b2c2eb5 100644 --- a/pkg/query-service/app/clickhouseReader/reader.go +++ b/pkg/query-service/app/clickhouseReader/reader.go @@ -3802,7 +3802,7 @@ func (r *ClickHouseReader) GetLogs(ctx context.Context, params *model.LogsFilter if lenFilters != 0 { userEmail, err := auth.GetEmailFromJwt(ctx) if err == nil { - telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_LOGS_FILTERS, data, userEmail) + telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_LOGS_FILTERS, data, userEmail, true, false) } } @@ -3844,7 +3844,7 @@ func (r *ClickHouseReader) TailLogs(ctx context.Context, client *model.LogsTailC if lenFilters != 0 { userEmail, err := auth.GetEmailFromJwt(ctx) if err == nil { - telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_LOGS_FILTERS, data, userEmail) + telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_LOGS_FILTERS, data, userEmail, true, false) } } @@ -3936,7 +3936,7 @@ func (r *ClickHouseReader) AggregateLogs(ctx context.Context, params *model.Logs if lenFilters != 0 { userEmail, err := auth.GetEmailFromJwt(ctx) if err == nil { - telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_LOGS_FILTERS, data, userEmail) + telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_LOGS_FILTERS, data, userEmail, true, false) } } diff --git a/pkg/query-service/app/http_handler.go b/pkg/query-service/app/http_handler.go index c025345cef..aab6cb3393 100644 --- a/pkg/query-service/app/http_handler.go +++ b/pkg/query-service/app/http_handler.go @@ -401,6 +401,8 @@ func (aH *APIHandler) RegisterRoutes(router *mux.Router, am *AuthMiddleware) { router.HandleFunc("/api/v1/explorer/views/{viewId}", am.EditAccess(aH.deleteSavedView)).Methods(http.MethodDelete) router.HandleFunc("/api/v1/feedback", am.OpenAccess(aH.submitFeedback)).Methods(http.MethodPost) + router.HandleFunc("/api/v1/events", am.ViewAccess(aH.registerEvent)).Methods(http.MethodPost) + // router.HandleFunc("/api/v1/get_percentiles", aH.getApplicationPercentiles).Methods(http.MethodGet) router.HandleFunc("/api/v1/services", am.ViewAccess(aH.getServices)).Methods(http.MethodPost) router.HandleFunc("/api/v1/services/list", am.ViewAccess(aH.getServicesList)).Methods(http.MethodGet) @@ -1502,7 +1504,22 @@ func (aH *APIHandler) submitFeedback(w http.ResponseWriter, r *http.Request) { } userEmail, err := auth.GetEmailFromJwt(r.Context()) if err == nil { - telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_INPRODUCT_FEEDBACK, data, userEmail) + telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_INPRODUCT_FEEDBACK, data, userEmail, true, false) + } +} + +func (aH *APIHandler) registerEvent(w http.ResponseWriter, r *http.Request) { + + request, err := parseRegisterEventRequest(r) + if aH.HandleError(w, err, http.StatusBadRequest) { + return + } + userEmail, err := auth.GetEmailFromJwt(r.Context()) + if err == nil { + telemetry.GetInstance().SendEvent(request.EventName, request.Attributes, userEmail, true, true) + aH.WriteJSON(w, r, map[string]string{"data": "Event Processed Successfully"}) + } else { + RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: err}, nil) } } @@ -1585,7 +1602,7 @@ func (aH *APIHandler) getServices(w http.ResponseWriter, r *http.Request) { } userEmail, err := auth.GetEmailFromJwt(r.Context()) if err == nil { - telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_NUMBER_OF_SERVICES, data, userEmail) + telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_NUMBER_OF_SERVICES, data, userEmail, true, false) } if (data["number"] != 0) && (data["number"] != telemetry.DEFAULT_NUMBER_OF_SERVICES) { @@ -2310,7 +2327,7 @@ func (aH *APIHandler) editOrg(w http.ResponseWriter, r *http.Request) { "organizationName": req.Name, } userEmail, err := auth.GetEmailFromJwt(r.Context()) - telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_ORG_SETTINGS, data, userEmail) + telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_ORG_SETTINGS, data, userEmail, true, false) aH.WriteJSON(w, r, map[string]string{"data": "org updated successfully"}) } @@ -3525,7 +3542,7 @@ func sendQueryResultEvents(r *http.Request, result []*v3.Result, queryRangeParam "metricsUsed": signozMetricsUsed, "dashboardId": dashboardID, "widgetId": widgetID, - }, userEmail) + }, userEmail, true, false) } if alertMatched { var alertID string @@ -3547,7 +3564,7 @@ func sendQueryResultEvents(r *http.Request, result []*v3.Result, queryRangeParam "logsUsed": signozLogsUsed, "metricsUsed": signozMetricsUsed, "alertId": alertID, - }, userEmail) + }, userEmail, true, false) } } } diff --git a/pkg/query-service/app/parser.go b/pkg/query-service/app/parser.go index 9a9f388ab5..670f5eff25 100644 --- a/pkg/query-service/app/parser.go +++ b/pkg/query-service/app/parser.go @@ -66,6 +66,19 @@ func parseGetTopOperationsRequest(r *http.Request) (*model.GetTopOperationsParam return postData, nil } +func parseRegisterEventRequest(r *http.Request) (*model.RegisterEventParams, error) { + var postData *model.RegisterEventParams + err := json.NewDecoder(r.Body).Decode(&postData) + if err != nil { + return nil, err + } + if postData.EventName == "" { + return nil, errors.New("eventName param missing in query") + } + + return postData, nil +} + func parseMetricsTime(s string) (time.Time, error) { if t, err := strconv.ParseFloat(s, 64); err == nil { s, ns := math.Modf(t) diff --git a/pkg/query-service/app/server.go b/pkg/query-service/app/server.go index 549e74e976..cb34b048ea 100644 --- a/pkg/query-service/app/server.go +++ b/pkg/query-service/app/server.go @@ -452,7 +452,7 @@ func extractQueryRangeV3Data(path string, r *http.Request) (map[string]interface data["tracesUsed"] = signozTracesUsed userEmail, err := auth.GetEmailFromJwt(r.Context()) if err == nil { - telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_QUERY_RANGE_API, data, userEmail) + telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_QUERY_RANGE_API, data, userEmail, true, false) } } return data, true @@ -496,7 +496,7 @@ func (s *Server) analyticsMiddleware(next http.Handler) http.Handler { if _, ok := telemetry.EnabledPaths()[path]; ok { userEmail, err := auth.GetEmailFromJwt(r.Context()) if err == nil { - telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_PATH, data, userEmail) + telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_PATH, data, userEmail, true, false) } } // } diff --git a/pkg/query-service/auth/auth.go b/pkg/query-service/auth/auth.go index 0a90c8c730..f0d220df81 100644 --- a/pkg/query-service/auth/auth.go +++ b/pkg/query-service/auth/auth.go @@ -89,7 +89,7 @@ func Invite(ctx context.Context, req *model.InviteRequest) (*model.InviteRespons telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_USER_INVITATION_SENT, map[string]interface{}{ "invited user email": req.Email, - }, au.Email) + }, au.Email, true, false) // send email if SMTP is enabled if os.Getenv("SMTP_ENABLED") == "true" && req.FrontendBaseUrl != "" { @@ -404,7 +404,7 @@ func RegisterInvitedUser(ctx context.Context, req *RegisterRequest, nopassword b } telemetry.GetInstance().IdentifyUser(user) - telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_USER_INVITATION_ACCEPTED, nil, req.Email) + telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_USER_INVITATION_ACCEPTED, nil, req.Email, true, false) return user, nil } diff --git a/pkg/query-service/dao/sqlite/rbac.go b/pkg/query-service/dao/sqlite/rbac.go index c28a2b675c..aba9beb065 100644 --- a/pkg/query-service/dao/sqlite/rbac.go +++ b/pkg/query-service/dao/sqlite/rbac.go @@ -203,7 +203,7 @@ func (mds *ModelDaoSqlite) CreateUser(ctx context.Context, } telemetry.GetInstance().IdentifyUser(user) - telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_USER, data, user.Email) + telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_USER, data, user.Email, true, false) return user, nil } diff --git a/pkg/query-service/model/queryParams.go b/pkg/query-service/model/queryParams.go index 754de3eae0..11020a0abf 100644 --- a/pkg/query-service/model/queryParams.go +++ b/pkg/query-service/model/queryParams.go @@ -164,6 +164,11 @@ type GetTopOperationsParams struct { Limit int `json:"limit"` } +type RegisterEventParams struct { + EventName string `json:"eventName"` + Attributes map[string]interface{} `json:"attributes"` +} + type GetUsageParams struct { StartTime string EndTime string diff --git a/pkg/query-service/telemetry/telemetry.go b/pkg/query-service/telemetry/telemetry.go index 4c23cbd092..03b21855ba 100644 --- a/pkg/query-service/telemetry/telemetry.go +++ b/pkg/query-service/telemetry/telemetry.go @@ -197,7 +197,7 @@ func createTelemetry() { data := map[string]interface{}{} telemetry.SetTelemetryEnabled(constants.IsTelemetryEnabled()) - telemetry.SendEvent(TELEMETRY_EVENT_HEART_BEAT, data, "") + telemetry.SendEvent(TELEMETRY_EVENT_HEART_BEAT, data, "", true, false) ticker := time.NewTicker(HEART_BEAT_DURATION) activeUserTicker := time.NewTicker(ACTIVE_USER_DURATION) @@ -231,7 +231,12 @@ func createTelemetry() { if (telemetry.activeUser["traces"] != 0) || (telemetry.activeUser["metrics"] != 0) || (telemetry.activeUser["logs"] != 0) { telemetry.activeUser["any"] = 1 } - telemetry.SendEvent(TELEMETRY_EVENT_ACTIVE_USER, map[string]interface{}{"traces": telemetry.activeUser["traces"], "metrics": telemetry.activeUser["metrics"], "logs": telemetry.activeUser["logs"], "any": telemetry.activeUser["any"]}, "") + telemetry.SendEvent(TELEMETRY_EVENT_ACTIVE_USER, map[string]interface{}{ + "traces": telemetry.activeUser["traces"], + "metrics": telemetry.activeUser["metrics"], + "logs": telemetry.activeUser["logs"], + "any": telemetry.activeUser["any"]}, + "", true, false) telemetry.activeUser = map[string]int8{"traces": 0, "metrics": 0, "logs": 0, "any": 0} case <-ticker.C: @@ -239,15 +244,15 @@ func createTelemetry() { tagsInfo, _ := telemetry.reader.GetTagsInfoInLastHeartBeatInterval(context.Background(), HEART_BEAT_DURATION) if len(tagsInfo.Env) != 0 { - telemetry.SendEvent(TELEMETRY_EVENT_ENVIRONMENT, map[string]interface{}{"value": tagsInfo.Env}, "") + telemetry.SendEvent(TELEMETRY_EVENT_ENVIRONMENT, map[string]interface{}{"value": tagsInfo.Env}, "", true, false) } for language, _ := range tagsInfo.Languages { - telemetry.SendEvent(TELEMETRY_EVENT_LANGUAGE, map[string]interface{}{"language": language}, "") + telemetry.SendEvent(TELEMETRY_EVENT_LANGUAGE, map[string]interface{}{"language": language}, "", true, false) } for service, _ := range tagsInfo.Services { - telemetry.SendEvent(TELEMETRY_EVENT_SERVICE, map[string]interface{}{"serviceName": service}, "") + telemetry.SendEvent(TELEMETRY_EVENT_SERVICE, map[string]interface{}{"serviceName": service}, "", true, false) } totalSpans, _ := telemetry.reader.GetTotalSpans(context.Background()) @@ -280,7 +285,7 @@ func createTelemetry() { for key, value := range tsInfo { data[key] = value } - telemetry.SendEvent(TELEMETRY_EVENT_HEART_BEAT, data, "") + telemetry.SendEvent(TELEMETRY_EVENT_HEART_BEAT, data, "", true, false) alertsInfo, err := telemetry.reader.GetAlertsInfo(context.Background()) if err == nil { @@ -307,18 +312,18 @@ func createTelemetry() { } // send event only if there are dashboards or alerts or channels if dashboardsInfo.TotalDashboards > 0 || alertsInfo.TotalAlerts > 0 || len(*channels) > 0 || savedViewsInfo.TotalSavedViews > 0 { - telemetry.SendEvent(TELEMETRY_EVENT_DASHBOARDS_ALERTS, dashboardsAlertsData, "") + telemetry.SendEvent(TELEMETRY_EVENT_DASHBOARDS_ALERTS, dashboardsAlertsData, "", true, false) } } } } } if err != nil { - telemetry.SendEvent(TELEMETRY_EVENT_DASHBOARDS_ALERTS, map[string]interface{}{"error": err.Error()}, "") + telemetry.SendEvent(TELEMETRY_EVENT_DASHBOARDS_ALERTS, map[string]interface{}{"error": err.Error()}, "", true, false) } getDistributedInfoInLastHeartBeatInterval, _ := telemetry.reader.GetDistributedInfoInLastHeartBeatInterval(context.Background()) - telemetry.SendEvent(TELEMETRY_EVENT_DISTRIBUTED, getDistributedInfoInLastHeartBeatInterval, "") + telemetry.SendEvent(TELEMETRY_EVENT_DISTRIBUTED, getDistributedInfoInLastHeartBeatInterval, "", true, false) } } }() @@ -426,7 +431,7 @@ func (a *Telemetry) checkEvents(event string) bool { return sendEvent } -func (a *Telemetry) SendEvent(event string, data map[string]interface{}, userEmail string, opts ...bool) { +func (a *Telemetry) SendEvent(event string, data map[string]interface{}, userEmail string, rateLimitFlag bool, viaEventsAPI bool) { // ignore telemetry for default user if userEmail == DEFAULT_CLOUD_EMAIL || a.GetUserEmail() == DEFAULT_CLOUD_EMAIL { @@ -436,10 +441,6 @@ func (a *Telemetry) SendEvent(event string, data map[string]interface{}, userEma if userEmail != "" { a.SetUserEmail(userEmail) } - rateLimitFlag := true - if len(opts) > 0 { - rateLimitFlag = opts[0] - } if !a.isTelemetryEnabled() { return @@ -485,7 +486,7 @@ func (a *Telemetry) SendEvent(event string, data map[string]interface{}, userEma // check if event is part of SAAS_EVENTS_LIST _, isSaaSEvent := SAAS_EVENTS_LIST[event] - if a.saasOperator != nil && a.GetUserEmail() != "" && isSaaSEvent { + if a.saasOperator != nil && a.GetUserEmail() != "" && (isSaaSEvent || viaEventsAPI) { a.saasOperator.Enqueue(analytics.Track{ Event: event, UserId: a.GetUserEmail(),