diff --git a/ee/query-service/app/api/auth.go b/ee/query-service/app/api/auth.go index a469b99e33..9ec99a4cc1 100644 --- a/ee/query-service/app/api/auth.go +++ b/ee/query-service/app/api/auth.go @@ -74,7 +74,7 @@ func (ah *APIHandler) registerUser(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() requestBody, err := io.ReadAll(r.Body) if err != nil { - zap.S().Errorf("received no input in api\n", err) + zap.L().Error("received no input in api", zap.Error(err)) RespondError(w, model.BadRequest(err), nil) return } @@ -82,7 +82,7 @@ func (ah *APIHandler) registerUser(w http.ResponseWriter, r *http.Request) { err = json.Unmarshal(requestBody, &req) if err != nil { - zap.S().Errorf("received invalid user registration request", zap.Error(err)) + zap.L().Error("received invalid user registration request", zap.Error(err)) RespondError(w, model.BadRequest(fmt.Errorf("failed to register user")), nil) return } @@ -90,13 +90,13 @@ func (ah *APIHandler) registerUser(w http.ResponseWriter, r *http.Request) { // get invite object invite, err := baseauth.ValidateInvite(ctx, req) if err != nil { - zap.S().Errorf("failed to validate invite token", err) + zap.L().Error("failed to validate invite token", zap.Error(err)) RespondError(w, model.BadRequest(err), nil) return } if invite == nil { - zap.S().Errorf("failed to validate invite token: it is either empty or invalid", err) + zap.L().Error("failed to validate invite token: it is either empty or invalid", zap.Error(err)) RespondError(w, model.BadRequest(basemodel.ErrSignupFailed{}), nil) return } @@ -104,7 +104,7 @@ func (ah *APIHandler) registerUser(w http.ResponseWriter, r *http.Request) { // get auth domain from email domain domain, apierr := ah.AppDao().GetDomainByEmail(ctx, invite.Email) if apierr != nil { - zap.S().Errorf("failed to get domain from email", apierr) + zap.L().Error("failed to get domain from email", zap.Error(apierr)) RespondError(w, model.InternalError(basemodel.ErrSignupFailed{}), nil) } @@ -205,24 +205,24 @@ func (ah *APIHandler) receiveGoogleAuth(w http.ResponseWriter, r *http.Request) ctx := context.Background() if !ah.CheckFeature(model.SSO) { - zap.S().Errorf("[receiveGoogleAuth] sso requested but feature unavailable %s in org domain %s", model.SSO) + zap.L().Error("[receiveGoogleAuth] sso requested but feature unavailable in org domain") http.Redirect(w, r, fmt.Sprintf("%s?ssoerror=%s", redirectUri, "feature unavailable, please upgrade your billing plan to access this feature"), http.StatusMovedPermanently) return } q := r.URL.Query() if errType := q.Get("error"); errType != "" { - zap.S().Errorf("[receiveGoogleAuth] failed to login with google auth", q.Get("error_description")) + zap.L().Error("[receiveGoogleAuth] failed to login with google auth", zap.String("error", errType), zap.String("error_description", q.Get("error_description"))) http.Redirect(w, r, fmt.Sprintf("%s?ssoerror=%s", redirectUri, "failed to login through SSO "), http.StatusMovedPermanently) return } relayState := q.Get("state") - zap.S().Debug("[receiveGoogleAuth] relay state received", zap.String("state", relayState)) + zap.L().Debug("[receiveGoogleAuth] relay state received", zap.String("state", relayState)) parsedState, err := url.Parse(relayState) if err != nil || relayState == "" { - zap.S().Errorf("[receiveGoogleAuth] failed to process response - invalid response from IDP", err, r) + zap.L().Error("[receiveGoogleAuth] failed to process response - invalid response from IDP", zap.Error(err), zap.Any("request", r)) handleSsoError(w, r, redirectUri) return } @@ -244,14 +244,14 @@ func (ah *APIHandler) receiveGoogleAuth(w http.ResponseWriter, r *http.Request) identity, err := callbackHandler.HandleCallback(r) if err != nil { - zap.S().Errorf("[receiveGoogleAuth] failed to process HandleCallback ", domain.String(), zap.Error(err)) + zap.L().Error("[receiveGoogleAuth] failed to process HandleCallback ", zap.String("domain", domain.String()), zap.Error(err)) handleSsoError(w, r, redirectUri) return } nextPage, err := ah.AppDao().PrepareSsoRedirect(ctx, redirectUri, identity.Email) if err != nil { - zap.S().Errorf("[receiveGoogleAuth] failed to generate redirect URI after successful login ", domain.String(), zap.Error(err)) + zap.L().Error("[receiveGoogleAuth] failed to generate redirect URI after successful login ", zap.String("domain", domain.String()), zap.Error(err)) handleSsoError(w, r, redirectUri) return } @@ -266,14 +266,14 @@ func (ah *APIHandler) receiveSAML(w http.ResponseWriter, r *http.Request) { ctx := context.Background() if !ah.CheckFeature(model.SSO) { - zap.S().Errorf("[receiveSAML] sso requested but feature unavailable %s in org domain %s", model.SSO) + zap.L().Error("[receiveSAML] sso requested but feature unavailable in org domain") http.Redirect(w, r, fmt.Sprintf("%s?ssoerror=%s", redirectUri, "feature unavailable, please upgrade your billing plan to access this feature"), http.StatusMovedPermanently) return } err := r.ParseForm() if err != nil { - zap.S().Errorf("[receiveSAML] failed to process response - invalid response from IDP", err, r) + zap.L().Error("[receiveSAML] failed to process response - invalid response from IDP", zap.Error(err), zap.Any("request", r)) handleSsoError(w, r, redirectUri) return } @@ -281,11 +281,11 @@ func (ah *APIHandler) receiveSAML(w http.ResponseWriter, r *http.Request) { // the relay state is sent when a login request is submitted to // Idp. relayState := r.FormValue("RelayState") - zap.S().Debug("[receiveML] relay state", zap.String("relayState", relayState)) + zap.L().Debug("[receiveML] relay state", zap.String("relayState", relayState)) parsedState, err := url.Parse(relayState) if err != nil || relayState == "" { - zap.S().Errorf("[receiveSAML] failed to process response - invalid response from IDP", err, r) + zap.L().Error("[receiveSAML] failed to process response - invalid response from IDP", zap.Error(err), zap.Any("request", r)) handleSsoError(w, r, redirectUri) return } @@ -302,34 +302,34 @@ func (ah *APIHandler) receiveSAML(w http.ResponseWriter, r *http.Request) { sp, err := domain.PrepareSamlRequest(parsedState) if err != nil { - zap.S().Errorf("[receiveSAML] failed to prepare saml request for domain (%s): %v", domain.String(), err) + zap.L().Error("[receiveSAML] failed to prepare saml request for domain", zap.String("domain", domain.String()), zap.Error(err)) handleSsoError(w, r, redirectUri) return } assertionInfo, err := sp.RetrieveAssertionInfo(r.FormValue("SAMLResponse")) if err != nil { - zap.S().Errorf("[receiveSAML] failed to retrieve assertion info from saml response for organization (%s): %v", domain.String(), err) + zap.L().Error("[receiveSAML] failed to retrieve assertion info from saml response", zap.String("domain", domain.String()), zap.Error(err)) handleSsoError(w, r, redirectUri) return } if assertionInfo.WarningInfo.InvalidTime { - zap.S().Errorf("[receiveSAML] expired saml response for organization (%s): %v", domain.String(), err) + zap.L().Error("[receiveSAML] expired saml response", zap.String("domain", domain.String()), zap.Error(err)) handleSsoError(w, r, redirectUri) return } email := assertionInfo.NameID if email == "" { - zap.S().Errorf("[receiveSAML] invalid email in the SSO response (%s)", domain.String()) + zap.L().Error("[receiveSAML] invalid email in the SSO response", zap.String("domain", domain.String())) handleSsoError(w, r, redirectUri) return } nextPage, err := ah.AppDao().PrepareSsoRedirect(ctx, redirectUri, email) if err != nil { - zap.S().Errorf("[receiveSAML] failed to generate redirect URI after successful login ", domain.String(), zap.Error(err)) + zap.L().Error("[receiveSAML] failed to generate redirect URI after successful login ", zap.String("domain", domain.String()), zap.Error(err)) handleSsoError(w, r, redirectUri) return } diff --git a/ee/query-service/app/api/license.go b/ee/query-service/app/api/license.go index 5c397020b1..51cfddefb1 100644 --- a/ee/query-service/app/api/license.go +++ b/ee/query-service/app/api/license.go @@ -191,7 +191,7 @@ func (ah *APIHandler) listLicensesV2(w http.ResponseWriter, r *http.Request) { url := fmt.Sprintf("%s/trial?licenseKey=%s", constants.LicenseSignozIo, currentActiveLicenseKey) req, err := http.NewRequest("GET", url, nil) if err != nil { - zap.S().Error("Error while creating request for trial details", err) + zap.L().Error("Error while creating request for trial details", zap.Error(err)) // If there is an error in fetching trial details, we will still return the license details // to avoid blocking the UI ah.Respond(w, resp) @@ -200,7 +200,7 @@ func (ah *APIHandler) listLicensesV2(w http.ResponseWriter, r *http.Request) { req.Header.Add("X-SigNoz-SecretKey", constants.LicenseAPIKey) trialResp, err := hClient.Do(req) if err != nil { - zap.S().Error("Error while fetching trial details", err) + zap.L().Error("Error while fetching trial details", zap.Error(err)) // If there is an error in fetching trial details, we will still return the license details // to avoid incorrectly blocking the UI ah.Respond(w, resp) @@ -211,7 +211,7 @@ func (ah *APIHandler) listLicensesV2(w http.ResponseWriter, r *http.Request) { trialRespBody, err := io.ReadAll(trialResp.Body) if err != nil || trialResp.StatusCode != http.StatusOK { - zap.S().Error("Error while fetching trial details", err) + zap.L().Error("Error while fetching trial details", zap.Error(err)) // If there is an error in fetching trial details, we will still return the license details // to avoid incorrectly blocking the UI ah.Respond(w, resp) @@ -222,7 +222,7 @@ func (ah *APIHandler) listLicensesV2(w http.ResponseWriter, r *http.Request) { var trialRespData model.SubscriptionServerResp if err := json.Unmarshal(trialRespBody, &trialRespData); err != nil { - zap.S().Error("Error while decoding trial details", err) + zap.L().Error("Error while decoding trial details", zap.Error(err)) // If there is an error in fetching trial details, we will still return the license details // to avoid incorrectly blocking the UI ah.Respond(w, resp) diff --git a/ee/query-service/app/api/metrics.go b/ee/query-service/app/api/metrics.go index 81af7035b7..7c0e320f45 100644 --- a/ee/query-service/app/api/metrics.go +++ b/ee/query-service/app/api/metrics.go @@ -18,14 +18,14 @@ import ( func (ah *APIHandler) queryRangeMetricsV2(w http.ResponseWriter, r *http.Request) { if !ah.CheckFeature(basemodel.CustomMetricsFunction) { - zap.S().Info("CustomMetricsFunction feature is not enabled in this plan") + zap.L().Info("CustomMetricsFunction feature is not enabled in this plan") ah.APIHandler.QueryRangeMetricsV2(w, r) return } metricsQueryRangeParams, apiErrorObj := parser.ParseMetricQueryRangeParams(r) if apiErrorObj != nil { - zap.S().Errorf(apiErrorObj.Err.Error()) + zap.L().Error("Error in parsing metric query params", zap.Error(apiErrorObj.Err)) RespondError(w, apiErrorObj, nil) return } diff --git a/ee/query-service/app/api/pat.go b/ee/query-service/app/api/pat.go index ea43f47fb0..3ff8be74a2 100644 --- a/ee/query-service/app/api/pat.go +++ b/ee/query-service/app/api/pat.go @@ -43,8 +43,8 @@ func (ah *APIHandler) createPAT(w http.ResponseWriter, r *http.Request) { return } pat := model.PAT{ - Name: req.Name, - Role: req.Role, + Name: req.Name, + Role: req.Role, ExpiresAt: req.ExpiresInDays, } err = validatePATRequest(pat) @@ -65,7 +65,7 @@ func (ah *APIHandler) createPAT(w http.ResponseWriter, r *http.Request) { pat.ExpiresAt = time.Now().Unix() + (pat.ExpiresAt * 24 * 60 * 60) } - zap.S().Debugf("Got Create PAT request: %+v", pat) + zap.L().Info("Got Create PAT request", zap.Any("pat", pat)) var apierr basemodel.BaseApiError if pat, apierr = ah.AppDao().CreatePAT(ctx, pat); apierr != nil { RespondError(w, apierr, nil) @@ -115,7 +115,7 @@ func (ah *APIHandler) updatePAT(w http.ResponseWriter, r *http.Request) { req.UpdatedByUserID = user.Id id := mux.Vars(r)["id"] req.UpdatedAt = time.Now().Unix() - zap.S().Debugf("Got Update PAT request: %+v", req) + zap.L().Info("Got Update PAT request", zap.Any("pat", req)) var apierr basemodel.BaseApiError if apierr = ah.AppDao().UpdatePAT(ctx, req, id); apierr != nil { RespondError(w, apierr, nil) @@ -135,7 +135,7 @@ func (ah *APIHandler) getPATs(w http.ResponseWriter, r *http.Request) { }, nil) return } - zap.S().Infof("Get PATs for user: %+v", user.Id) + zap.L().Info("Get PATs for user", zap.String("user_id", user.Id)) pats, apierr := ah.AppDao().ListPATs(ctx) if apierr != nil { RespondError(w, apierr, nil) @@ -156,7 +156,7 @@ func (ah *APIHandler) revokePAT(w http.ResponseWriter, r *http.Request) { return } - zap.S().Debugf("Revoke PAT with id: %+v", id) + zap.L().Info("Revoke PAT with id", zap.String("id", id)) if apierr := ah.AppDao().RevokePAT(ctx, id, user.Id); apierr != nil { RespondError(w, apierr, nil) return diff --git a/ee/query-service/app/api/traces.go b/ee/query-service/app/api/traces.go index 22d66f7a82..ee18b2f50b 100644 --- a/ee/query-service/app/api/traces.go +++ b/ee/query-service/app/api/traces.go @@ -15,7 +15,7 @@ import ( func (ah *APIHandler) searchTraces(w http.ResponseWriter, r *http.Request) { if !ah.CheckFeature(basemodel.SmartTraceDetail) { - zap.S().Info("SmartTraceDetail feature is not enabled in this plan") + zap.L().Info("SmartTraceDetail feature is not enabled in this plan") ah.APIHandler.SearchTraces(w, r) return } @@ -26,7 +26,7 @@ func (ah *APIHandler) searchTraces(w http.ResponseWriter, r *http.Request) { } spanLimit, err := strconv.Atoi(constants.SpanLimitStr) if err != nil { - zap.S().Error("Error during strconv.Atoi() on SPAN_LIMIT env variable: ", err) + zap.L().Error("Error during strconv.Atoi() on SPAN_LIMIT env variable", zap.Error(err)) return } result, err := ah.opts.DataConnector.SearchTraces(r.Context(), traceId, spanId, levelUpInt, levelDownInt, spanLimit, db.SmartTraceAlgorithm) diff --git a/ee/query-service/app/db/metrics.go b/ee/query-service/app/db/metrics.go index 3bafc6a638..c7b41b17f5 100644 --- a/ee/query-service/app/db/metrics.go +++ b/ee/query-service/app/db/metrics.go @@ -22,7 +22,7 @@ import ( func (r *ClickhouseReader) GetMetricResultEE(ctx context.Context, query string) ([]*basemodel.Series, string, error) { defer utils.Elapsed("GetMetricResult")() - zap.S().Infof("Executing metric result query: %s", query) + zap.L().Info("Executing metric result query: ", zap.String("query", query)) var hash string // If getSubTreeSpans function is used in the clickhouse query @@ -38,9 +38,8 @@ func (r *ClickhouseReader) GetMetricResultEE(ctx context.Context, query string) } rows, err := r.conn.Query(ctx, query) - zap.S().Debug(query) if err != nil { - zap.S().Debug("Error in processing query: ", err) + zap.L().Error("Error in processing query", zap.Error(err)) return nil, "", fmt.Errorf("error in processing query") } @@ -117,7 +116,7 @@ func (r *ClickhouseReader) GetMetricResultEE(ctx context.Context, query string) groupAttributes[colName] = fmt.Sprintf("%v", reflect.ValueOf(v).Elem().Int()) } default: - zap.S().Errorf("invalid var found in metric builder query result", v, colName) + zap.L().Error("invalid var found in metric builder query result", zap.Any("var", v), zap.String("colName", colName)) } } sort.Strings(groupBy) @@ -140,7 +139,7 @@ func (r *ClickhouseReader) GetMetricResultEE(ctx context.Context, query string) } // err = r.conn.Exec(ctx, "DROP TEMPORARY TABLE IF EXISTS getSubTreeSpans"+hash) // if err != nil { - // zap.S().Error("Error in dropping temporary table: ", err) + // zap.L().Error("Error in dropping temporary table: ", err) // return nil, err // } if hash == "" { @@ -152,7 +151,7 @@ func (r *ClickhouseReader) GetMetricResultEE(ctx context.Context, query string) func (r *ClickhouseReader) getSubTreeSpansCustomFunction(ctx context.Context, query string, hash string) (string, string, error) { - zap.S().Debugf("Executing getSubTreeSpans function") + zap.L().Debug("Executing getSubTreeSpans function") // str1 := `select fromUnixTimestamp64Milli(intDiv( toUnixTimestamp64Milli ( timestamp ), 100) * 100) AS interval, toFloat64(count()) as count from (select timestamp, spanId, parentSpanId, durationNano from getSubTreeSpans(select * from signoz_traces.signoz_index_v2 where serviceName='frontend' and name='/driver.DriverService/FindNearest' and traceID='00000000000000004b0a863cb5ed7681') where name='FindDriverIDs' group by interval order by interval asc;` @@ -162,28 +161,28 @@ func (r *ClickhouseReader) getSubTreeSpansCustomFunction(ctx context.Context, qu err := r.conn.Exec(ctx, "DROP TABLE IF EXISTS getSubTreeSpans"+hash) if err != nil { - zap.S().Error("Error in dropping temporary table: ", err) + zap.L().Error("Error in dropping temporary table", zap.Error(err)) return query, hash, err } // Create temporary table to store the getSubTreeSpans() results - zap.S().Debugf("Creating temporary table getSubTreeSpans%s", hash) + zap.L().Debug("Creating temporary table getSubTreeSpans", zap.String("hash", hash)) err = r.conn.Exec(ctx, "CREATE TABLE IF NOT EXISTS "+"getSubTreeSpans"+hash+" (timestamp DateTime64(9) CODEC(DoubleDelta, LZ4), traceID FixedString(32) CODEC(ZSTD(1)), spanID String CODEC(ZSTD(1)), parentSpanID String CODEC(ZSTD(1)), rootSpanID String CODEC(ZSTD(1)), serviceName LowCardinality(String) CODEC(ZSTD(1)), name LowCardinality(String) CODEC(ZSTD(1)), rootName LowCardinality(String) CODEC(ZSTD(1)), durationNano UInt64 CODEC(T64, ZSTD(1)), kind Int8 CODEC(T64, ZSTD(1)), tagMap Map(LowCardinality(String), String) CODEC(ZSTD(1)), events Array(String) CODEC(ZSTD(2))) ENGINE = MergeTree() ORDER BY (timestamp)") if err != nil { - zap.S().Error("Error in creating temporary table: ", err) + zap.L().Error("Error in creating temporary table", zap.Error(err)) return query, hash, err } var getSpansSubQueryDBResponses []model.GetSpansSubQueryDBResponse getSpansSubQuery := subtreeInput // Execute the subTree query - zap.S().Debugf("Executing subTree query: %s", getSpansSubQuery) + zap.L().Debug("Executing subTree query", zap.String("query", getSpansSubQuery)) err = r.conn.Select(ctx, &getSpansSubQueryDBResponses, getSpansSubQuery) - // zap.S().Info(getSpansSubQuery) + // zap.L().Info(getSpansSubQuery) if err != nil { - zap.S().Debug("Error in processing sql query: ", err) + zap.L().Error("Error in processing sql query", zap.Error(err)) return query, hash, fmt.Errorf("Error in processing sql query") } @@ -196,16 +195,16 @@ func (r *ClickhouseReader) getSubTreeSpansCustomFunction(ctx context.Context, qu if len(getSpansSubQueryDBResponses) == 0 { return query, hash, fmt.Errorf("No spans found for the given query") } - zap.S().Debugf("Executing query to fetch all the spans from the same TraceID: %s", modelQuery) + zap.L().Debug("Executing query to fetch all the spans from the same TraceID: ", zap.String("modelQuery", modelQuery)) err = r.conn.Select(ctx, &searchScanResponses, modelQuery, getSpansSubQueryDBResponses[0].TraceID) if err != nil { - zap.S().Debug("Error in processing sql query: ", err) + zap.L().Error("Error in processing sql query", zap.Error(err)) return query, hash, fmt.Errorf("Error in processing sql query") } // Process model to fetch the spans - zap.S().Debugf("Processing model to fetch the spans") + zap.L().Debug("Processing model to fetch the spans") searchSpanResponses := []basemodel.SearchSpanResponseItem{} for _, item := range searchScanResponses { var jsonItem basemodel.SearchSpanResponseItem @@ -218,17 +217,17 @@ func (r *ClickhouseReader) getSubTreeSpansCustomFunction(ctx context.Context, qu } // Build the subtree and store all the subtree spans in temporary table getSubTreeSpans+hash // Use map to store pointer to the spans to avoid duplicates and save memory - zap.S().Debugf("Building the subtree to store all the subtree spans in temporary table getSubTreeSpans%s", hash) + zap.L().Debug("Building the subtree to store all the subtree spans in temporary table getSubTreeSpans", zap.String("hash", hash)) treeSearchResponse, err := getSubTreeAlgorithm(searchSpanResponses, getSpansSubQueryDBResponses) if err != nil { - zap.S().Error("Error in getSubTreeAlgorithm function: ", err) + zap.L().Error("Error in getSubTreeAlgorithm function", zap.Error(err)) return query, hash, err } - zap.S().Debugf("Preparing batch to store subtree spans in temporary table getSubTreeSpans%s", hash) + zap.L().Debug("Preparing batch to store subtree spans in temporary table getSubTreeSpans", zap.String("hash", hash)) statement, err := r.conn.PrepareBatch(context.Background(), fmt.Sprintf("INSERT INTO getSubTreeSpans"+hash)) if err != nil { - zap.S().Error("Error in preparing batch statement: ", err) + zap.L().Error("Error in preparing batch statement", zap.Error(err)) return query, hash, err } for _, span := range treeSearchResponse { @@ -251,14 +250,14 @@ func (r *ClickhouseReader) getSubTreeSpansCustomFunction(ctx context.Context, qu span.Events, ) if err != nil { - zap.S().Debug("Error in processing sql query: ", err) + zap.L().Error("Error in processing sql query", zap.Error(err)) return query, hash, err } } - zap.S().Debugf("Inserting the subtree spans in temporary table getSubTreeSpans%s", hash) + zap.L().Debug("Inserting the subtree spans in temporary table getSubTreeSpans", zap.String("hash", hash)) err = statement.Send() if err != nil { - zap.S().Error("Error in sending statement: ", err) + zap.L().Error("Error in sending statement", zap.Error(err)) return query, hash, err } return query, hash, nil @@ -323,7 +322,7 @@ func getSubTreeAlgorithm(payload []basemodel.SearchSpanResponseItem, getSpansSub spans = append(spans, span) } - zap.S().Debug("Building Tree") + zap.L().Debug("Building Tree") roots, err := buildSpanTrees(&spans) if err != nil { return nil, err @@ -333,7 +332,7 @@ func getSubTreeAlgorithm(payload []basemodel.SearchSpanResponseItem, getSpansSub // For each root, get the subtree spans for _, getSpansSubQueryDBResponse := range getSpansSubQueryDBResponses { targetSpan := &model.SpanForTraceDetails{} - // zap.S().Debug("Building tree for span id: " + getSpansSubQueryDBResponse.SpanID + " " + strconv.Itoa(i+1) + " of " + strconv.Itoa(len(getSpansSubQueryDBResponses))) + // zap.L().Debug("Building tree for span id: " + getSpansSubQueryDBResponse.SpanID + " " + strconv.Itoa(i+1) + " of " + strconv.Itoa(len(getSpansSubQueryDBResponses))) // Search target span object in the tree for _, root := range roots { targetSpan, err = breadthFirstSearch(root, getSpansSubQueryDBResponse.SpanID) @@ -341,7 +340,7 @@ func getSubTreeAlgorithm(payload []basemodel.SearchSpanResponseItem, getSpansSub break } if err != nil { - zap.S().Error("Error during BreadthFirstSearch(): ", err) + zap.L().Error("Error during BreadthFirstSearch()", zap.Error(err)) return nil, err } } diff --git a/ee/query-service/app/db/trace.go b/ee/query-service/app/db/trace.go index 529a9a93fd..c6fe9045cf 100644 --- a/ee/query-service/app/db/trace.go +++ b/ee/query-service/app/db/trace.go @@ -49,7 +49,7 @@ func SmartTraceAlgorithm(payload []basemodel.SearchSpanResponseItem, targetSpanI break } if err != nil { - zap.S().Error("Error during BreadthFirstSearch(): ", err) + zap.L().Error("Error during BreadthFirstSearch()", zap.Error(err)) return nil, err } } @@ -186,7 +186,7 @@ func buildSpanTrees(spansPtr *[]*model.SpanForTraceDetails) ([]*model.SpanForTra // If the parent span is not found, add current span to list of roots if parent == nil { - // zap.S().Debug("Parent Span not found parent_id: ", span.ParentID) + // zap.L().Debug("Parent Span not found parent_id: ", span.ParentID) roots = append(roots, span) span.ParentID = "" continue diff --git a/ee/query-service/app/server.go b/ee/query-service/app/server.go index 469632ac7f..c742eef01b 100644 --- a/ee/query-service/app/server.go +++ b/ee/query-service/app/server.go @@ -134,7 +134,7 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) { var reader interfaces.DataConnector storage := os.Getenv("STORAGE") if storage == "clickhouse" { - zap.S().Info("Using ClickHouse as datastore ...") + zap.L().Info("Using ClickHouse as datastore ...") qb := db.NewDataConnector( localDB, serverOptions.PromConfigPath, @@ -419,30 +419,33 @@ func extractQueryRangeV3Data(path string, r *http.Request) (map[string]interface signozMetricsUsed := false signozLogsUsed := false - dataSources := []string{} + signozTracesUsed := false if postData != nil { if postData.CompositeQuery != nil { data["queryType"] = postData.CompositeQuery.QueryType data["panelType"] = postData.CompositeQuery.PanelType - signozLogsUsed, signozMetricsUsed, _ = telemetry.GetInstance().CheckSigNozSignals(postData) + signozLogsUsed, signozMetricsUsed, signozTracesUsed = telemetry.GetInstance().CheckSigNozSignals(postData) } } - if signozMetricsUsed || signozLogsUsed { + if signozMetricsUsed || signozLogsUsed || signozTracesUsed { if signozMetricsUsed { - dataSources = append(dataSources, "metrics") telemetry.GetInstance().AddActiveMetricsUser() } if signozLogsUsed { - dataSources = append(dataSources, "logs") telemetry.GetInstance().AddActiveLogsUser() } - data["dataSources"] = dataSources + if signozTracesUsed { + telemetry.GetInstance().AddActiveTracesUser() + } + data["metricsUsed"] = signozMetricsUsed + data["logsUsed"] = signozLogsUsed + data["tracesUsed"] = signozTracesUsed userEmail, err := baseauth.GetEmailFromJwt(r.Context()) if err == nil { - telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_QUERY_RANGE_V3, data, userEmail, true) + telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_QUERY_RANGE_API, data, userEmail) } } return data, true @@ -522,7 +525,7 @@ func (s *Server) initListeners() error { return err } - zap.S().Info(fmt.Sprintf("Query server started listening on %s...", s.serverOptions.HTTPHostPort)) + zap.L().Info(fmt.Sprintf("Query server started listening on %s...", s.serverOptions.HTTPHostPort)) // listen on private port to support internal services privateHostPort := s.serverOptions.PrivateHostPort @@ -535,7 +538,7 @@ func (s *Server) initListeners() error { if err != nil { return err } - zap.S().Info(fmt.Sprintf("Query server started listening on private port %s...", s.serverOptions.PrivateHostPort)) + zap.L().Info(fmt.Sprintf("Query server started listening on private port %s...", s.serverOptions.PrivateHostPort)) return nil } @@ -547,7 +550,7 @@ func (s *Server) Start() error { if !s.serverOptions.DisableRules { s.ruleManager.Start() } else { - zap.S().Info("msg: Rules disabled as rules.disable is set to TRUE") + zap.L().Info("msg: Rules disabled as rules.disable is set to TRUE") } err := s.initListeners() @@ -561,23 +564,23 @@ func (s *Server) Start() error { } go func() { - zap.S().Info("Starting HTTP server", zap.Int("port", httpPort), zap.String("addr", s.serverOptions.HTTPHostPort)) + zap.L().Info("Starting HTTP server", zap.Int("port", httpPort), zap.String("addr", s.serverOptions.HTTPHostPort)) switch err := s.httpServer.Serve(s.httpConn); err { case nil, http.ErrServerClosed, cmux.ErrListenerClosed: // normal exit, nothing to do default: - zap.S().Error("Could not start HTTP server", zap.Error(err)) + zap.L().Error("Could not start HTTP server", zap.Error(err)) } s.unavailableChannel <- healthcheck.Unavailable }() go func() { - zap.S().Info("Starting pprof server", zap.String("addr", baseconst.DebugHttpPort)) + zap.L().Info("Starting pprof server", zap.String("addr", baseconst.DebugHttpPort)) err = http.ListenAndServe(baseconst.DebugHttpPort, nil) if err != nil { - zap.S().Error("Could not start pprof server", zap.Error(err)) + zap.L().Error("Could not start pprof server", zap.Error(err)) } }() @@ -587,14 +590,14 @@ func (s *Server) Start() error { } go func() { - zap.S().Info("Starting Private HTTP server", zap.Int("port", privatePort), zap.String("addr", s.serverOptions.PrivateHostPort)) + zap.L().Info("Starting Private HTTP server", zap.Int("port", privatePort), zap.String("addr", s.serverOptions.PrivateHostPort)) switch err := s.privateHTTP.Serve(s.privateConn); err { case nil, http.ErrServerClosed, cmux.ErrListenerClosed: // normal exit, nothing to do - zap.S().Info("private http server closed") + zap.L().Info("private http server closed") default: - zap.S().Error("Could not start private HTTP server", zap.Error(err)) + zap.L().Error("Could not start private HTTP server", zap.Error(err)) } s.unavailableChannel <- healthcheck.Unavailable @@ -602,10 +605,10 @@ func (s *Server) Start() error { }() go func() { - zap.S().Info("Starting OpAmp Websocket server", zap.String("addr", baseconst.OpAmpWsEndpoint)) + zap.L().Info("Starting OpAmp Websocket server", zap.String("addr", baseconst.OpAmpWsEndpoint)) err := s.opampServer.Start(baseconst.OpAmpWsEndpoint) if err != nil { - zap.S().Info("opamp ws server failed to start", err) + zap.L().Error("opamp ws server failed to start", zap.Error(err)) s.unavailableChannel <- healthcheck.Unavailable } }() @@ -681,7 +684,7 @@ func makeRulesManager( return nil, fmt.Errorf("rule manager error: %v", err) } - zap.S().Info("rules manager is ready") + zap.L().Info("rules manager is ready") return manager, nil } diff --git a/ee/query-service/auth/auth.go b/ee/query-service/auth/auth.go index 8c06384549..d45d050cca 100644 --- a/ee/query-service/auth/auth.go +++ b/ee/query-service/auth/auth.go @@ -17,25 +17,25 @@ import ( func GetUserFromRequest(r *http.Request, apiHandler *api.APIHandler) (*basemodel.UserPayload, error) { patToken := r.Header.Get("SIGNOZ-API-KEY") if len(patToken) > 0 { - zap.S().Debugf("Received a non-zero length PAT token") + zap.L().Debug("Received a non-zero length PAT token") ctx := context.Background() dao := apiHandler.AppDao() pat, err := dao.GetPAT(ctx, patToken) if err == nil && pat != nil { - zap.S().Debugf("Found valid PAT: %+v", pat) + zap.L().Debug("Found valid PAT: ", zap.Any("pat", pat)) if pat.ExpiresAt < time.Now().Unix() && pat.ExpiresAt != 0 { - zap.S().Debugf("PAT has expired: %+v", pat) + zap.L().Info("PAT has expired: ", zap.Any("pat", pat)) return nil, fmt.Errorf("PAT has expired") } group, apiErr := dao.GetGroupByName(ctx, pat.Role) if apiErr != nil { - zap.S().Debugf("Error while getting group for PAT: %+v", apiErr) + zap.L().Error("Error while getting group for PAT: ", zap.Any("apiErr", apiErr)) return nil, apiErr } user, err := dao.GetUser(ctx, pat.UserID) if err != nil { - zap.S().Debugf("Error while getting user for PAT: %+v", err) + zap.L().Error("Error while getting user for PAT: ", zap.Error(err)) return nil, err } telemetry.GetInstance().SetPatTokenUser() @@ -48,7 +48,7 @@ func GetUserFromRequest(r *http.Request, apiHandler *api.APIHandler) (*basemodel }, nil } if err != nil { - zap.S().Debugf("Error while getting user for PAT: %+v", err) + zap.L().Error("Error while getting user for PAT: ", zap.Error(err)) return nil, err } } diff --git a/ee/query-service/dao/sqlite/auth.go b/ee/query-service/dao/sqlite/auth.go index 664323eaaf..4418b04cbf 100644 --- a/ee/query-service/dao/sqlite/auth.go +++ b/ee/query-service/dao/sqlite/auth.go @@ -22,19 +22,19 @@ func (m *modelDao) createUserForSAMLRequest(ctx context.Context, email string) ( domain, apierr := m.GetDomainByEmail(ctx, email) if apierr != nil { - zap.S().Errorf("failed to get domain from email", apierr) + zap.L().Error("failed to get domain from email", zap.Error(apierr)) return nil, model.InternalErrorStr("failed to get domain from email") } hash, err := baseauth.PasswordHash(utils.GeneratePassowrd()) if err != nil { - zap.S().Errorf("failed to generate password hash when registering a user via SSO redirect", zap.Error(err)) + zap.L().Error("failed to generate password hash when registering a user via SSO redirect", zap.Error(err)) return nil, model.InternalErrorStr("failed to generate password hash") } group, apiErr := m.GetGroupByName(ctx, baseconst.ViewerGroup) if apiErr != nil { - zap.S().Debugf("GetGroupByName failed, err: %v\n", apiErr.Err) + zap.L().Error("GetGroupByName failed", zap.Error(apiErr)) return nil, apiErr } @@ -51,7 +51,7 @@ func (m *modelDao) createUserForSAMLRequest(ctx context.Context, email string) ( user, apiErr = m.CreateUser(ctx, user, false) if apiErr != nil { - zap.S().Debugf("CreateUser failed, err: %v\n", apiErr.Err) + zap.L().Error("CreateUser failed", zap.Error(apiErr)) return nil, apiErr } @@ -65,7 +65,7 @@ func (m *modelDao) PrepareSsoRedirect(ctx context.Context, redirectUri, email st userPayload, apierr := m.GetUserByEmail(ctx, email) if !apierr.IsNil() { - zap.S().Errorf(" failed to get user with email received from auth provider", apierr.Error()) + zap.L().Error("failed to get user with email received from auth provider", zap.String("error", apierr.Error())) return "", model.BadRequestStr("invalid user email received from the auth provider") } @@ -75,7 +75,7 @@ func (m *modelDao) PrepareSsoRedirect(ctx context.Context, redirectUri, email st newUser, apiErr := m.createUserForSAMLRequest(ctx, email) user = newUser if apiErr != nil { - zap.S().Errorf("failed to create user with email received from auth provider: %v", apierr.Error()) + zap.L().Error("failed to create user with email received from auth provider", zap.Error(apiErr)) return "", apiErr } } else { @@ -84,7 +84,7 @@ func (m *modelDao) PrepareSsoRedirect(ctx context.Context, redirectUri, email st tokenStore, err := baseauth.GenerateJWTForUser(user) if err != nil { - zap.S().Errorf("failed to generate token for SSO login user", err) + zap.L().Error("failed to generate token for SSO login user", zap.Error(err)) return "", model.InternalErrorStr("failed to generate token for the user") } @@ -143,8 +143,8 @@ func (m *modelDao) PrecheckLogin(ctx context.Context, email, sourceUrl string) ( // do nothing, just skip sso ssoAvailable = false default: - zap.S().Errorf("feature check failed", zap.String("featureKey", model.SSO), zap.Error(err)) - return resp, model.BadRequest(err) + zap.L().Error("feature check failed", zap.String("featureKey", model.SSO), zap.Error(err)) + return resp, model.BadRequestStr(err.Error()) } } @@ -160,7 +160,7 @@ func (m *modelDao) PrecheckLogin(ctx context.Context, email, sourceUrl string) ( if len(emailComponents) > 0 { emailDomain = emailComponents[1] } - zap.S().Errorf("failed to get org domain from email", zap.String("emailDomain", emailDomain), apierr.ToError()) + zap.L().Error("failed to get org domain from email", zap.String("emailDomain", emailDomain), zap.Error(apierr.ToError())) return resp, apierr } @@ -176,7 +176,7 @@ func (m *modelDao) PrecheckLogin(ctx context.Context, email, sourceUrl string) ( escapedUrl, _ := url.QueryUnescape(sourceUrl) siteUrl, err := url.Parse(escapedUrl) if err != nil { - zap.S().Errorf("failed to parse referer", err) + zap.L().Error("failed to parse referer", zap.Error(err)) return resp, model.InternalError(fmt.Errorf("failed to generate login request")) } @@ -185,7 +185,7 @@ func (m *modelDao) PrecheckLogin(ctx context.Context, email, sourceUrl string) ( resp.SsoUrl, err = orgDomain.BuildSsoUrl(siteUrl) if err != nil { - zap.S().Errorf("failed to prepare saml request for domain", zap.String("domain", orgDomain.Name), err) + zap.L().Error("failed to prepare saml request for domain", zap.String("domain", orgDomain.Name), zap.Error(err)) return resp, model.InternalError(err) } diff --git a/ee/query-service/dao/sqlite/domain.go b/ee/query-service/dao/sqlite/domain.go index b515af49c9..fbaa4fe332 100644 --- a/ee/query-service/dao/sqlite/domain.go +++ b/ee/query-service/dao/sqlite/domain.go @@ -48,13 +48,13 @@ func (m *modelDao) GetDomainFromSsoResponse(ctx context.Context, relayState *url if domainIdStr != "" { domainId, err := uuid.Parse(domainIdStr) if err != nil { - zap.S().Errorf("failed to parse domainId from relay state", err) + zap.L().Error("failed to parse domainId from relay state", zap.Error(err)) return nil, fmt.Errorf("failed to parse domainId from IdP response") } domain, err = m.GetDomain(ctx, domainId) if (err != nil) || domain == nil { - zap.S().Errorf("failed to find domain from domainId received in IdP response", err.Error()) + zap.L().Error("failed to find domain from domainId received in IdP response", zap.Error(err)) return nil, fmt.Errorf("invalid credentials") } } @@ -64,7 +64,7 @@ func (m *modelDao) GetDomainFromSsoResponse(ctx context.Context, relayState *url domainFromDB, err := m.GetDomainByName(ctx, domainNameStr) domain = domainFromDB if (err != nil) || domain == nil { - zap.S().Errorf("failed to find domain from domainName received in IdP response", err.Error()) + zap.L().Error("failed to find domain from domainName received in IdP response", zap.Error(err)) return nil, fmt.Errorf("invalid credentials") } } @@ -132,7 +132,7 @@ func (m *modelDao) ListDomains(ctx context.Context, orgId string) ([]model.OrgDo for _, s := range stored { domain := model.OrgDomain{Id: s.Id, Name: s.Name, OrgId: s.OrgId} if err := domain.LoadConfig(s.Data); err != nil { - zap.S().Errorf("ListDomains() failed", zap.Error(err)) + zap.L().Error("ListDomains() failed", zap.Error(err)) } domains = append(domains, domain) } @@ -153,7 +153,7 @@ func (m *modelDao) CreateDomain(ctx context.Context, domain *model.OrgDomain) ba configJson, err := json.Marshal(domain) if err != nil { - zap.S().Errorf("failed to unmarshal domain config", zap.Error(err)) + zap.L().Error("failed to unmarshal domain config", zap.Error(err)) return model.InternalError(fmt.Errorf("domain creation failed")) } @@ -167,7 +167,7 @@ func (m *modelDao) CreateDomain(ctx context.Context, domain *model.OrgDomain) ba time.Now().Unix()) if err != nil { - zap.S().Errorf("failed to insert domain in db", zap.Error(err)) + zap.L().Error("failed to insert domain in db", zap.Error(err)) return model.InternalError(fmt.Errorf("domain creation failed")) } @@ -178,13 +178,13 @@ func (m *modelDao) CreateDomain(ctx context.Context, domain *model.OrgDomain) ba func (m *modelDao) UpdateDomain(ctx context.Context, domain *model.OrgDomain) basemodel.BaseApiError { if domain.Id == uuid.Nil { - zap.S().Errorf("domain update failed", zap.Error(fmt.Errorf("OrgDomain.Id is null"))) + zap.L().Error("domain update failed", zap.Error(fmt.Errorf("OrgDomain.Id is null"))) return model.InternalError(fmt.Errorf("domain update failed")) } configJson, err := json.Marshal(domain) if err != nil { - zap.S().Errorf("domain update failed", zap.Error(err)) + zap.L().Error("domain update failed", zap.Error(err)) return model.InternalError(fmt.Errorf("domain update failed")) } @@ -195,7 +195,7 @@ func (m *modelDao) UpdateDomain(ctx context.Context, domain *model.OrgDomain) ba domain.Id) if err != nil { - zap.S().Errorf("domain update failed", zap.Error(err)) + zap.L().Error("domain update failed", zap.Error(err)) return model.InternalError(fmt.Errorf("domain update failed")) } @@ -206,7 +206,7 @@ func (m *modelDao) UpdateDomain(ctx context.Context, domain *model.OrgDomain) ba func (m *modelDao) DeleteDomain(ctx context.Context, id uuid.UUID) basemodel.BaseApiError { if id == uuid.Nil { - zap.S().Errorf("domain delete failed", zap.Error(fmt.Errorf("OrgDomain.Id is null"))) + zap.L().Error("domain delete failed", zap.Error(fmt.Errorf("OrgDomain.Id is null"))) return model.InternalError(fmt.Errorf("domain delete failed")) } @@ -215,7 +215,7 @@ func (m *modelDao) DeleteDomain(ctx context.Context, id uuid.UUID) basemodel.Bas id) if err != nil { - zap.S().Errorf("domain delete failed", zap.Error(err)) + zap.L().Error("domain delete failed", zap.Error(err)) return model.InternalError(fmt.Errorf("domain delete failed")) } diff --git a/ee/query-service/dao/sqlite/pat.go b/ee/query-service/dao/sqlite/pat.go index b2af1640c3..75169db685 100644 --- a/ee/query-service/dao/sqlite/pat.go +++ b/ee/query-service/dao/sqlite/pat.go @@ -26,12 +26,12 @@ func (m *modelDao) CreatePAT(ctx context.Context, p model.PAT) (model.PAT, basem p.Revoked, ) if err != nil { - zap.S().Errorf("Failed to insert PAT in db, err: %v", zap.Error(err)) + zap.L().Error("Failed to insert PAT in db, err: %v", zap.Error(err)) return model.PAT{}, model.InternalError(fmt.Errorf("PAT insertion failed")) } id, err := result.LastInsertId() if err != nil { - zap.S().Errorf("Failed to get last inserted id, err: %v", zap.Error(err)) + zap.L().Error("Failed to get last inserted id, err: %v", zap.Error(err)) return model.PAT{}, model.InternalError(fmt.Errorf("PAT insertion failed")) } p.Id = strconv.Itoa(int(id)) @@ -62,7 +62,7 @@ func (m *modelDao) UpdatePAT(ctx context.Context, p model.PAT, id string) basemo p.UpdatedByUserID, id) if err != nil { - zap.S().Errorf("Failed to update PAT in db, err: %v", zap.Error(err)) + zap.L().Error("Failed to update PAT in db, err: %v", zap.Error(err)) return model.InternalError(fmt.Errorf("PAT update failed")) } return nil @@ -74,7 +74,7 @@ func (m *modelDao) UpdatePATLastUsed(ctx context.Context, token string, lastUsed lastUsed, token) if err != nil { - zap.S().Errorf("Failed to update PAT last used in db, err: %v", zap.Error(err)) + zap.L().Error("Failed to update PAT last used in db, err: %v", zap.Error(err)) return model.InternalError(fmt.Errorf("PAT last used update failed")) } return nil @@ -84,7 +84,7 @@ func (m *modelDao) ListPATs(ctx context.Context) ([]model.PAT, basemodel.BaseApi pats := []model.PAT{} if err := m.DB().Select(&pats, "SELECT * FROM personal_access_tokens WHERE revoked=false ORDER by updated_at DESC;"); err != nil { - zap.S().Errorf("Failed to fetch PATs err: %v", zap.Error(err)) + zap.L().Error("Failed to fetch PATs err: %v", zap.Error(err)) return nil, model.InternalError(fmt.Errorf("failed to fetch PATs")) } for i := range pats { @@ -129,7 +129,7 @@ func (m *modelDao) RevokePAT(ctx context.Context, id string, userID string) base "UPDATE personal_access_tokens SET revoked=true, updated_by_user_id = $1, updated_at=$2 WHERE id=$3", userID, updatedAt, id) if err != nil { - zap.S().Errorf("Failed to revoke PAT in db, err: %v", zap.Error(err)) + zap.L().Error("Failed to revoke PAT in db, err: %v", zap.Error(err)) return model.InternalError(fmt.Errorf("PAT revoke failed")) } return nil diff --git a/ee/query-service/integrations/signozio/signozio.go b/ee/query-service/integrations/signozio/signozio.go index c1ad5e57e4..c18cfb6572 100644 --- a/ee/query-service/integrations/signozio/signozio.go +++ b/ee/query-service/integrations/signozio/signozio.go @@ -47,13 +47,13 @@ func ActivateLicense(key, siteId string) (*ActivationResponse, *model.ApiError) httpResponse, err := http.Post(C.Prefix+"/licenses/activate", APPLICATION_JSON, bytes.NewBuffer(reqString)) if err != nil { - zap.S().Errorf("failed to connect to license.signoz.io", err) + zap.L().Error("failed to connect to license.signoz.io", zap.Error(err)) return nil, model.BadRequest(fmt.Errorf("unable to connect with license.signoz.io, please check your network connection")) } httpBody, err := io.ReadAll(httpResponse.Body) if err != nil { - zap.S().Errorf("failed to read activation response from license.signoz.io", err) + zap.L().Error("failed to read activation response from license.signoz.io", zap.Error(err)) return nil, model.BadRequest(fmt.Errorf("failed to read activation response from license.signoz.io")) } @@ -63,7 +63,7 @@ func ActivateLicense(key, siteId string) (*ActivationResponse, *model.ApiError) result := ActivationResult{} err = json.Unmarshal(httpBody, &result) if err != nil { - zap.S().Errorf("failed to marshal activation response from license.signoz.io", err) + zap.L().Error("failed to marshal activation response from license.signoz.io", zap.Error(err)) return nil, model.InternalError(errors.Wrap(err, "failed to marshal license activation response")) } diff --git a/ee/query-service/license/db.go b/ee/query-service/license/db.go index 8d2f7065ff..bf71e9376d 100644 --- a/ee/query-service/license/db.go +++ b/ee/query-service/license/db.go @@ -97,7 +97,7 @@ func (r *Repo) InsertLicense(ctx context.Context, l *model.License) error { l.ValidationMessage) if err != nil { - zap.S().Errorf("error in inserting license data: ", zap.Error(err)) + zap.L().Error("error in inserting license data: ", zap.Error(err)) return fmt.Errorf("failed to insert license in db: %v", err) } @@ -121,7 +121,7 @@ func (r *Repo) UpdatePlanDetails(ctx context.Context, _, err := r.db.ExecContext(ctx, query, planDetails, time.Now(), key) if err != nil { - zap.S().Errorf("error in updating license: ", zap.Error(err)) + zap.L().Error("error in updating license: ", zap.Error(err)) return fmt.Errorf("failed to update license in db: %v", err) } diff --git a/ee/query-service/license/manager.go b/ee/query-service/license/manager.go index dcfa8235b1..56cb685fec 100644 --- a/ee/query-service/license/manager.go +++ b/ee/query-service/license/manager.go @@ -100,7 +100,7 @@ func (lm *Manager) SetActive(l *model.License) { err := lm.InitFeatures(lm.activeFeatures) if err != nil { - zap.S().Panicf("Couldn't activate features: %v", err) + zap.L().Panic("Couldn't activate features", zap.Error(err)) } if !lm.validatorRunning { // we want to make sure only one validator runs, @@ -125,13 +125,13 @@ func (lm *Manager) LoadActiveLicense() error { if active != nil { lm.SetActive(active) } else { - zap.S().Info("No active license found, defaulting to basic plan") + zap.L().Info("No active license found, defaulting to basic plan") // if no active license is found, we default to basic(free) plan with all default features lm.activeFeatures = model.BasicPlan setDefaultFeatures(lm) err := lm.InitFeatures(lm.activeFeatures) if err != nil { - zap.S().Error("Couldn't initialize features: ", err) + zap.L().Error("Couldn't initialize features", zap.Error(err)) return err } } @@ -191,7 +191,7 @@ func (lm *Manager) Validator(ctx context.Context) { // Validate validates the current active license func (lm *Manager) Validate(ctx context.Context) (reterr error) { - zap.S().Info("License validation started") + zap.L().Info("License validation started") if lm.activeLicense == nil { return nil } @@ -201,12 +201,12 @@ func (lm *Manager) Validate(ctx context.Context) (reterr error) { lm.lastValidated = time.Now().Unix() if reterr != nil { - zap.S().Errorf("License validation completed with error", reterr) + 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()}, "") } else { - zap.S().Info("License validation completed with no errors") + zap.L().Info("License validation completed with no errors") } lm.mutex.Unlock() @@ -214,7 +214,7 @@ func (lm *Manager) Validate(ctx context.Context) (reterr error) { response, apiError := validate.ValidateLicense(lm.activeLicense.ActivationId) if apiError != nil { - zap.S().Errorf("failed to validate license", apiError) + zap.L().Error("failed to validate license", zap.Error(apiError.Err)) return apiError.Err } @@ -235,7 +235,7 @@ func (lm *Manager) Validate(ctx context.Context) (reterr error) { } if err := l.ParsePlan(); err != nil { - zap.S().Errorf("failed to parse updated license", zap.Error(err)) + zap.L().Error("failed to parse updated license", zap.Error(err)) return err } @@ -245,7 +245,7 @@ func (lm *Manager) Validate(ctx context.Context) (reterr error) { if err != nil { // unexpected db write issue but we can let the user continue // and wait for update to work in next cycle. - zap.S().Errorf("failed to validate license", zap.Error(err)) + zap.L().Error("failed to validate license", zap.Error(err)) } } @@ -270,7 +270,7 @@ func (lm *Manager) Activate(ctx context.Context, key string) (licenseResponse *m response, apiError := validate.ActivateLicense(key, "") if apiError != nil { - zap.S().Errorf("failed to activate license", zap.Error(apiError.Err)) + zap.L().Error("failed to activate license", zap.Error(apiError.Err)) return nil, apiError } @@ -284,14 +284,14 @@ func (lm *Manager) Activate(ctx context.Context, key string) (licenseResponse *m err := l.ParsePlan() if err != nil { - zap.S().Errorf("failed to activate license", zap.Error(err)) + zap.L().Error("failed to activate license", zap.Error(err)) return nil, model.InternalError(err) } // store the license before activating it err = lm.repo.InsertLicense(ctx, l) if err != nil { - zap.S().Errorf("failed to activate license", zap.Error(err)) + zap.L().Error("failed to activate license", zap.Error(err)) return nil, model.InternalError(err) } diff --git a/ee/query-service/main.go b/ee/query-service/main.go index 427f78059b..3323e5bdbd 100644 --- a/ee/query-service/main.go +++ b/ee/query-service/main.go @@ -14,10 +14,10 @@ import ( semconv "go.opentelemetry.io/otel/semconv/v1.4.0" "go.signoz.io/signoz/ee/query-service/app" "go.signoz.io/signoz/pkg/query-service/auth" - "go.signoz.io/signoz/pkg/query-service/constants" baseconst "go.signoz.io/signoz/pkg/query-service/constants" "go.signoz.io/signoz/pkg/query-service/version" "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" zapotlpencoder "github.com/SigNoz/zap_otlp/zap_otlp_encoder" zapotlpsync "github.com/SigNoz/zap_otlp/zap_otlp_sync" @@ -27,18 +27,19 @@ import ( ) func initZapLog(enableQueryServiceLogOTLPExport bool) *zap.Logger { - config := zap.NewDevelopmentConfig() + config := zap.NewProductionConfig() ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt) defer stop() - config.EncoderConfig.EncodeDuration = zapcore.StringDurationEncoder - otlpEncoder := zapotlpencoder.NewOTLPEncoder(config.EncoderConfig) - consoleEncoder := zapcore.NewConsoleEncoder(config.EncoderConfig) - defaultLogLevel := zapcore.DebugLevel - config.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder + config.EncoderConfig.EncodeDuration = zapcore.MillisDurationEncoder + config.EncoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder config.EncoderConfig.TimeKey = "timestamp" config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder + otlpEncoder := zapotlpencoder.NewOTLPEncoder(config.EncoderConfig) + consoleEncoder := zapcore.NewJSONEncoder(config.EncoderConfig) + defaultLogLevel := zapcore.InfoLevel + res := resource.NewWithAttributes( semconv.SchemaURL, semconv.ServiceNameKey.String("query-service"), @@ -48,14 +49,15 @@ func initZapLog(enableQueryServiceLogOTLPExport bool) *zap.Logger { zapcore.NewCore(consoleEncoder, os.Stdout, defaultLogLevel), ) - if enableQueryServiceLogOTLPExport == true { - conn, err := grpc.DialContext(ctx, constants.OTLPTarget, grpc.WithBlock(), grpc.WithInsecure(), grpc.WithTimeout(time.Second*30)) + if enableQueryServiceLogOTLPExport { + ctx, _ := context.WithTimeout(ctx, time.Second*30) + conn, err := grpc.DialContext(ctx, baseconst.OTLPTarget, grpc.WithBlock(), grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { - log.Println("failed to connect to otlp collector to export query service logs with error:", err) + log.Fatalf("failed to establish connection: %v", err) } else { logExportBatchSizeInt, err := strconv.Atoi(baseconst.LogExportBatchSize) if err != nil { - logExportBatchSizeInt = 1000 + logExportBatchSizeInt = 512 } ws := zapcore.AddSync(zapotlpsync.NewOtlpSyncer(conn, zapotlpsync.Options{ BatchSize: logExportBatchSizeInt, @@ -113,7 +115,6 @@ func main() { zap.ReplaceGlobals(loggerMgr) defer loggerMgr.Sync() // flushes buffer, if any - logger := loggerMgr.Sugar() version.PrintVersion() serverOptions := &app.ServerOptions{ @@ -137,22 +138,22 @@ func main() { auth.JwtSecret = os.Getenv("SIGNOZ_JWT_SECRET") if len(auth.JwtSecret) == 0 { - zap.S().Warn("No JWT secret key is specified.") + zap.L().Warn("No JWT secret key is specified.") } else { - zap.S().Info("No JWT secret key set successfully.") + zap.L().Info("JWT secret key set successfully.") } server, err := app.NewServer(serverOptions) if err != nil { - logger.Fatal("Failed to create server", zap.Error(err)) + zap.L().Fatal("Failed to create server", zap.Error(err)) } if err := server.Start(); err != nil { - logger.Fatal("Could not start servers", zap.Error(err)) + zap.L().Fatal("Could not start server", zap.Error(err)) } if err := auth.InitAuthCache(context.Background()); err != nil { - logger.Fatal("Failed to initialize auth cache", zap.Error(err)) + zap.L().Fatal("Failed to initialize auth cache", zap.Error(err)) } signalsChannel := make(chan os.Signal, 1) @@ -161,9 +162,9 @@ func main() { for { select { case status := <-server.HealthCheckStatus(): - logger.Info("Received HealthCheck status: ", zap.Int("status", int(status))) + zap.L().Info("Received HealthCheck status: ", zap.Int("status", int(status))) case <-signalsChannel: - logger.Fatal("Received OS Interrupt Signal ... ") + zap.L().Fatal("Received OS Interrupt Signal ... ") server.Stop() } } diff --git a/ee/query-service/model/domain.go b/ee/query-service/model/domain.go index beadd66a51..4d5ff66df2 100644 --- a/ee/query-service/model/domain.go +++ b/ee/query-service/model/domain.go @@ -9,8 +9,8 @@ import ( "github.com/google/uuid" "github.com/pkg/errors" saml2 "github.com/russellhaering/gosaml2" - "go.signoz.io/signoz/ee/query-service/sso/saml" "go.signoz.io/signoz/ee/query-service/sso" + "go.signoz.io/signoz/ee/query-service/sso/saml" basemodel "go.signoz.io/signoz/pkg/query-service/model" "go.uber.org/zap" ) @@ -24,16 +24,16 @@ const ( // OrgDomain identify org owned web domains for auth and other purposes type OrgDomain struct { - Id uuid.UUID `json:"id"` - Name string `json:"name"` - OrgId string `json:"orgId"` - SsoEnabled bool `json:"ssoEnabled"` - SsoType SSOType `json:"ssoType"` + Id uuid.UUID `json:"id"` + Name string `json:"name"` + OrgId string `json:"orgId"` + SsoEnabled bool `json:"ssoEnabled"` + SsoType SSOType `json:"ssoType"` - SamlConfig *SamlConfig `json:"samlConfig"` + SamlConfig *SamlConfig `json:"samlConfig"` GoogleAuthConfig *GoogleOAuthConfig `json:"googleAuthConfig"` - Org *basemodel.Organization + Org *basemodel.Organization } func (od *OrgDomain) String() string { @@ -100,8 +100,8 @@ func (od *OrgDomain) GetSAMLCert() string { return "" } -// PrepareGoogleOAuthProvider creates GoogleProvider that is used in -// requesting OAuth and also used in processing response from google +// PrepareGoogleOAuthProvider creates GoogleProvider that is used in +// requesting OAuth and also used in processing response from google func (od *OrgDomain) PrepareGoogleOAuthProvider(siteUrl *url.URL) (sso.OAuthCallbackProvider, error) { if od.GoogleAuthConfig == nil { return nil, fmt.Errorf("Google auth is not setup correctly for this domain") @@ -137,38 +137,36 @@ func (od *OrgDomain) PrepareSamlRequest(siteUrl *url.URL) (*saml2.SAMLServicePro } func (od *OrgDomain) BuildSsoUrl(siteUrl *url.URL) (ssoUrl string, err error) { - fmtDomainId := strings.Replace(od.Id.String(), "-", ":", -1) - + // build redirect url from window.location sent by frontend redirectURL := fmt.Sprintf("%s://%s%s", siteUrl.Scheme, siteUrl.Host, siteUrl.Path) // prepare state that gets relayed back when the auth provider // calls back our url. here we pass the app url (where signoz runs) // and the domain Id. The domain Id helps in identifying sso config - // when the call back occurs and the app url is useful in redirecting user - // back to the right path. + // when the call back occurs and the app url is useful in redirecting user + // back to the right path. // why do we need to pass app url? the callback typically is handled by backend // and sometimes backend might right at a different port or is unaware of frontend // endpoint (unless SITE_URL param is set). hence, we receive this build sso request - // along with frontend window.location and use it to relay the information through - // auth provider to the backend (HandleCallback or HandleSSO method). + // along with frontend window.location and use it to relay the information through + // auth provider to the backend (HandleCallback or HandleSSO method). relayState := fmt.Sprintf("%s?domainId=%s", redirectURL, fmtDomainId) - - switch (od.SsoType) { + switch od.SsoType { case SAML: sp, err := od.PrepareSamlRequest(siteUrl) if err != nil { return "", err } - + return sp.BuildAuthURL(relayState) - + case GoogleAuth: - + googleProvider, err := od.PrepareGoogleOAuthProvider(siteUrl) if err != nil { return "", err @@ -176,9 +174,8 @@ func (od *OrgDomain) BuildSsoUrl(siteUrl *url.URL) (ssoUrl string, err error) { return googleProvider.BuildAuthURL(relayState) default: - zap.S().Errorf("found unsupported SSO config for the org domain", zap.String("orgDomain", od.Name)) - return "", fmt.Errorf("unsupported SSO config for the domain") + zap.L().Error("found unsupported SSO config for the org domain", zap.String("orgDomain", od.Name)) + return "", fmt.Errorf("unsupported SSO config for the domain") } - } diff --git a/ee/query-service/sso/saml/request.go b/ee/query-service/sso/saml/request.go index 01af7afe28..c9788d0ff3 100644 --- a/ee/query-service/sso/saml/request.go +++ b/ee/query-service/sso/saml/request.go @@ -102,6 +102,6 @@ func PrepareRequest(issuer, acsUrl, audience, entity, idp, certString string) (* IDPCertificateStore: certStore, SPKeyStore: randomKeyStore, } - zap.S().Debugf("SAML request:", sp) + zap.L().Debug("SAML request", zap.Any("sp", sp)) return sp, nil } diff --git a/ee/query-service/usage/manager.go b/ee/query-service/usage/manager.go index 99158b4345..72535c9ae5 100644 --- a/ee/query-service/usage/manager.go +++ b/ee/query-service/usage/manager.go @@ -91,12 +91,12 @@ func (lm *Manager) UploadUsage() { // check if license is present or not license, err := lm.licenseRepo.GetActiveLicense(ctx) if err != nil { - zap.S().Errorf("failed to get active license: %v", zap.Error(err)) + zap.L().Error("failed to get active license", zap.Error(err)) return } if license == nil { // we will not start the usage reporting if license is not present. - zap.S().Info("no license present, skipping usage reporting") + zap.L().Info("no license present, skipping usage reporting") return } @@ -123,7 +123,7 @@ func (lm *Manager) UploadUsage() { dbusages := []model.UsageDB{} err := lm.clickhouseConn.Select(ctx, &dbusages, fmt.Sprintf(query, db, db), time.Now().Add(-(24 * time.Hour))) if err != nil && !strings.Contains(err.Error(), "doesn't exist") { - zap.S().Errorf("failed to get usage from clickhouse: %v", zap.Error(err)) + zap.L().Error("failed to get usage from clickhouse: %v", zap.Error(err)) return } for _, u := range dbusages { @@ -133,16 +133,16 @@ func (lm *Manager) UploadUsage() { } if len(usages) <= 0 { - zap.S().Info("no snapshots to upload, skipping.") + zap.L().Info("no snapshots to upload, skipping.") return } - zap.S().Info("uploading usage data") + zap.L().Info("uploading usage data") orgName := "" orgNames, orgError := lm.modelDao.GetOrgs(ctx) if orgError != nil { - zap.S().Errorf("failed to get org data: %v", zap.Error(orgError)) + zap.L().Error("failed to get org data: %v", zap.Error(orgError)) } if len(orgNames) == 1 { orgName = orgNames[0].Name @@ -152,14 +152,14 @@ func (lm *Manager) UploadUsage() { for _, usage := range usages { usageDataBytes, err := encryption.Decrypt([]byte(usage.ExporterID[:32]), []byte(usage.Data)) if err != nil { - zap.S().Errorf("error while decrypting usage data: %v", zap.Error(err)) + zap.L().Error("error while decrypting usage data: %v", zap.Error(err)) return } usageData := model.Usage{} err = json.Unmarshal(usageDataBytes, &usageData) if err != nil { - zap.S().Errorf("error while unmarshalling usage data: %v", zap.Error(err)) + zap.L().Error("error while unmarshalling usage data: %v", zap.Error(err)) return } @@ -184,13 +184,13 @@ func (lm *Manager) UploadUsageWithExponentalBackOff(ctx context.Context, payload for i := 1; i <= MaxRetries; i++ { apiErr := licenseserver.SendUsage(ctx, payload) if apiErr != nil && i == MaxRetries { - zap.S().Errorf("retries stopped : %v", zap.Error(apiErr)) + zap.L().Error("retries stopped : %v", zap.Error(apiErr)) // not returning error here since it is captured in the failed count return } else if apiErr != nil { // sleeping for exponential backoff sleepDuration := RetryInterval * time.Duration(i) - zap.S().Errorf("failed to upload snapshot retrying after %v secs : %v", sleepDuration.Seconds(), zap.Error(apiErr.Err)) + zap.L().Error("failed to upload snapshot retrying after %v secs : %v", zap.Duration("sleepDuration", sleepDuration), zap.Error(apiErr.Err)) time.Sleep(sleepDuration) } else { break @@ -201,7 +201,7 @@ func (lm *Manager) UploadUsageWithExponentalBackOff(ctx context.Context, payload func (lm *Manager) Stop() { lm.scheduler.Stop() - zap.S().Debug("sending usage data before shutting down") + zap.L().Info("sending usage data before shutting down") // send usage before shutting down lm.UploadUsage() diff --git a/frontend/public/Icons/cable-car.svg b/frontend/public/Icons/cable-car.svg new file mode 100644 index 0000000000..0c7318debd --- /dev/null +++ b/frontend/public/Icons/cable-car.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/Icons/configure.svg b/frontend/public/Icons/configure.svg new file mode 100644 index 0000000000..088dfa9447 --- /dev/null +++ b/frontend/public/Icons/configure.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/Icons/group.svg b/frontend/public/Icons/group.svg new file mode 100644 index 0000000000..e293cebcd0 --- /dev/null +++ b/frontend/public/Icons/group.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/locales/en-GB/alerts.json b/frontend/public/locales/en-GB/alerts.json index fb360e579b..0901b14d19 100644 --- a/frontend/public/locales/en-GB/alerts.json +++ b/frontend/public/locales/en-GB/alerts.json @@ -37,11 +37,16 @@ "text_condition1": "Send a notification when", "text_condition2": "the threshold", "text_condition3": "during the last", + "option_1min": "1 min", "option_5min": "5 mins", "option_10min": "10 mins", "option_15min": "15 mins", + "option_30min": "30 mins", "option_60min": "60 mins", "option_4hours": "4 hours", + "option_3hours": "3 hours", + "option_6hours": "6 hours", + "option_12hours": "12 hours", "option_24hours": "24 hours", "field_threshold": "Alert Threshold", "option_allthetimes": "all the times", @@ -112,6 +117,7 @@ "exceptions_based_alert_desc": "Send a notification when a condition occurs in the exceptions data.", "field_unit": "Threshold unit", "text_alert_on_absent": "Send a notification if data is missing for", + "text_alert_frequency": "Run alert every", "text_for": "minutes", "selected_query_placeholder": "Select query" } diff --git a/frontend/public/locales/en-GB/organizationsettings.json b/frontend/public/locales/en-GB/organizationsettings.json index deae9666ee..74654d9b46 100644 --- a/frontend/public/locales/en-GB/organizationsettings.json +++ b/frontend/public/locales/en-GB/organizationsettings.json @@ -14,6 +14,5 @@ "delete_domain_message": "Are you sure you want to delete this domain?", "delete_domain": "Delete Domain", "add_domain": "Add Domains", - "saml_settings": "Your SAML settings have been saved, please login from incognito window to confirm that it has been set up correctly", - "invite_link_share_manually": "After inviting members, please copy the invite link and send them the link manually" + "saml_settings": "Your SAML settings have been saved, please login from incognito window to confirm that it has been set up correctly" } diff --git a/frontend/public/locales/en/alerts.json b/frontend/public/locales/en/alerts.json index 0349568c70..597cc24096 100644 --- a/frontend/public/locales/en/alerts.json +++ b/frontend/public/locales/en/alerts.json @@ -37,11 +37,16 @@ "text_condition1": "Send a notification when", "text_condition2": "the threshold", "text_condition3": "during the last", + "option_1min": "1 min", "option_5min": "5 mins", "option_10min": "10 mins", "option_15min": "15 mins", + "option_30min": "30 mins", "option_60min": "60 mins", + "option_3hours": "3 hours", "option_4hours": "4 hours", + "option_6hours": "6 hours", + "option_12hours": "12 hours", "option_24hours": "24 hours", "field_threshold": "Alert Threshold", "option_allthetimes": "all the times", @@ -112,6 +117,7 @@ "exceptions_based_alert_desc": "Send a notification when a condition occurs in the exceptions data.", "field_unit": "Threshold unit", "text_alert_on_absent": "Send a notification if data is missing for", + "text_alert_frequency": "Run alert every", "text_for": "minutes", "selected_query_placeholder": "Select query" } diff --git a/frontend/public/locales/en/billings.json b/frontend/public/locales/en/billings.json new file mode 100644 index 0000000000..fb706e002f --- /dev/null +++ b/frontend/public/locales/en/billings.json @@ -0,0 +1,14 @@ +{ + "days_remaining": "days remaining in your billing period.", + "billing": "Billing", + "manage_billing_and_costs": "Manage your billing information, invoices, and monitor costs.", + "enterprise_cloud": "Enterprise Cloud", + "enterprise": "Enterprise", + "card_details_recieved_and_billing_info": "We have received your card details, your billing will only start after the end of your free trial period.", + "upgrade_plan": "Upgrade Plan", + "manage_billing": "Manage Billing", + "upgrade_now_text": "Upgrade now to have uninterrupted access", + "billing_start_info": "Your billing will start only after the trial period", + "checkout_plans": "Check out features in paid plans", + "here": "here" +} diff --git a/frontend/public/locales/en/organizationsettings.json b/frontend/public/locales/en/organizationsettings.json index deae9666ee..74654d9b46 100644 --- a/frontend/public/locales/en/organizationsettings.json +++ b/frontend/public/locales/en/organizationsettings.json @@ -14,6 +14,5 @@ "delete_domain_message": "Are you sure you want to delete this domain?", "delete_domain": "Delete Domain", "add_domain": "Add Domains", - "saml_settings": "Your SAML settings have been saved, please login from incognito window to confirm that it has been set up correctly", - "invite_link_share_manually": "After inviting members, please copy the invite link and send them the link manually" + "saml_settings": "Your SAML settings have been saved, please login from incognito window to confirm that it has been set up correctly" } diff --git a/frontend/src/api/billing/getUsage.ts b/frontend/src/api/billing/getUsage.ts index 1cb5be5640..da7b6ebd63 100644 --- a/frontend/src/api/billing/getUsage.ts +++ b/frontend/src/api/billing/getUsage.ts @@ -13,6 +13,7 @@ export interface UsageResponsePayloadProps { billTotal: number; }; discount: number; + subscriptionStatus?: string; } const getUsage = async ( diff --git a/frontend/src/assets/Integrations/ConfigureIcon.tsx b/frontend/src/assets/Integrations/ConfigureIcon.tsx new file mode 100644 index 0000000000..84ddef5de0 --- /dev/null +++ b/frontend/src/assets/Integrations/ConfigureIcon.tsx @@ -0,0 +1,23 @@ +import { Color } from '@signozhq/design-tokens'; +import { useIsDarkMode } from 'hooks/useDarkMode'; + +function ConfigureIcon(): JSX.Element { + const isDarkMode = useIsDarkMode(); + return ( + + + + + + + ); +} + +export default ConfigureIcon; diff --git a/frontend/src/components/LogDetail/QueryBuilderSearchWrapper.styles.scss b/frontend/src/components/LogDetail/QueryBuilderSearchWrapper.styles.scss index e3da355621..2a6822dc00 100644 --- a/frontend/src/components/LogDetail/QueryBuilderSearchWrapper.styles.scss +++ b/frontend/src/components/LogDetail/QueryBuilderSearchWrapper.styles.scss @@ -1,10 +1,13 @@ .query-builder-search-wrapper { - margin-top: 10px; - height: 46px; - border: 1px solid var(--bg-slate-400); - border-bottom: none; + margin-top: 10px; + border: 1px solid var(--bg-slate-400); + border-bottom: none; - .ant-select-selector { - border: none !important; - } -} \ No newline at end of file + .ant-select-selector { + border: none !important; + + input { + font-size: 12px; + } + } +} diff --git a/frontend/src/components/Logs/ListLogView/index.tsx b/frontend/src/components/Logs/ListLogView/index.tsx index 5452db0f16..2b828d663c 100644 --- a/frontend/src/components/Logs/ListLogView/index.tsx +++ b/frontend/src/components/Logs/ListLogView/index.tsx @@ -37,12 +37,17 @@ const convert = new Convert(); interface LogFieldProps { fieldKey: string; fieldValue: string; + linesPerRow?: number; } -type LogSelectedFieldProps = LogFieldProps & +type LogSelectedFieldProps = Omit & Pick; -function LogGeneralField({ fieldKey, fieldValue }: LogFieldProps): JSX.Element { +function LogGeneralField({ + fieldKey, + fieldValue, + linesPerRow = 1, +}: LogFieldProps): JSX.Element { const html = useMemo( () => ({ __html: convert.toHtml(dompurify.sanitize(fieldValue)), @@ -55,7 +60,11 @@ function LogGeneralField({ fieldKey, fieldValue }: LogFieldProps): JSX.Element { {`${fieldKey} : `} - + 1 ? linesPerRow : undefined} + /> ); } @@ -92,6 +101,7 @@ type ListLogViewProps = { onSetActiveLog: (log: ILog) => void; onAddToQuery: AddToQueryHOCProps['onAddToQuery']; activeLog?: ILog | null; + linesPerRow: number; }; function ListLogView({ @@ -100,6 +110,7 @@ function ListLogView({ onSetActiveLog, onAddToQuery, activeLog, + linesPerRow, }: ListLogViewProps): JSX.Element { const flattenLogData = useMemo(() => FlatLogData(logData), [logData]); @@ -179,7 +190,11 @@ function ListLogView({ />
- + {flattenLogData.stream && ( )} @@ -222,4 +237,8 @@ ListLogView.defaultProps = { activeLog: null, }; +LogGeneralField.defaultProps = { + linesPerRow: 1, +}; + export default ListLogView; diff --git a/frontend/src/components/Logs/ListLogView/styles.ts b/frontend/src/components/Logs/ListLogView/styles.ts index 84559e5137..52cc2b20d4 100644 --- a/frontend/src/components/Logs/ListLogView/styles.ts +++ b/frontend/src/components/Logs/ListLogView/styles.ts @@ -2,6 +2,10 @@ import { Color } from '@signozhq/design-tokens'; import { Card, Typography } from 'antd'; import styled from 'styled-components'; +interface LogTextProps { + linesPerRow?: number; +} + export const Container = styled(Card)<{ $isActiveLog: boolean; $isDarkMode: boolean; @@ -23,7 +27,7 @@ export const Container = styled(Card)<{ export const Text = styled(Typography.Text)` &&& { - min-width: 1.5rem; + min-width: 2.5rem; white-space: nowrap; } `; @@ -41,11 +45,19 @@ export const LogContainer = styled.div` gap: 6px; `; -export const LogText = styled.div` +export const LogText = styled.div` display: inline-block; text-overflow: ellipsis; overflow: hidden; - white-space: nowrap; + ${({ linesPerRow }): string => + linesPerRow + ? `-webkit-line-clamp: ${linesPerRow}; + line-clamp: ${linesPerRow}; + display: -webkit-box; + -webkit-box-orient: vertical; + white-space: normal; ` + : 'white-space: nowrap;'}; + }; `; export const SelectedLog = styled.div` diff --git a/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.styles.scss b/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.styles.scss index efd668ffe1..af325a2d25 100644 --- a/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.styles.scss +++ b/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.styles.scss @@ -27,7 +27,7 @@ line-height: 18px; letter-spacing: 0.08em; text-align: left; - color: var(--bg-slate-200, #52575c); + color: #52575c; } .menu-items { @@ -65,7 +65,7 @@ padding: 12px; .title { - color: var(--bg-slate-200, #52575c); + color: #52575c; font-family: Inter; font-size: 11px; font-style: normal; @@ -149,7 +149,7 @@ } .title { - color: var(--bg-slate-200, #52575c); + color: #52575c; font-family: Inter; font-size: 11px; font-style: normal; diff --git a/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.tsx b/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.tsx index 558a97c63b..3a42e9a0b0 100644 --- a/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.tsx +++ b/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.tsx @@ -120,38 +120,36 @@ export default function LogsFormatOptionsMenu({ {selectedItem && ( <> - {selectedItem === 'raw' && ( - <> -
-
-
max lines per row
-
- - - -
+ <> +
+
+
max lines per row
+
+ + +
- - )} +
+
{!addNewColumn &&
} diff --git a/frontend/src/constants/query.ts b/frontend/src/constants/query.ts index d3bd2729d1..31ec5fcd20 100644 --- a/frontend/src/constants/query.ts +++ b/frontend/src/constants/query.ts @@ -27,5 +27,6 @@ export enum QueryParams { viewName = 'viewName', viewKey = 'viewKey', expandedWidgetId = 'expandedWidgetId', + integration = 'integration', pagination = 'pagination', } diff --git a/frontend/src/container/BillingContainer/BillingContainer.test.tsx b/frontend/src/container/BillingContainer/BillingContainer.test.tsx index cd447e5d60..1988df313b 100644 --- a/frontend/src/container/BillingContainer/BillingContainer.test.tsx +++ b/frontend/src/container/BillingContainer/BillingContainer.test.tsx @@ -56,14 +56,14 @@ describe('BillingContainer', () => { expect(cost).toBeInTheDocument(); const manageBilling = screen.getByRole('button', { - name: /manage billing/i, + name: 'manage_billing', }); expect(manageBilling).toBeInTheDocument(); const dollar = screen.getByText(/\$0/i); expect(dollar).toBeInTheDocument(); - const currentBill = screen.getByText('Billing'); + const currentBill = screen.getByText('billing'); expect(currentBill).toBeInTheDocument(); }); @@ -75,7 +75,7 @@ describe('BillingContainer', () => { const freeTrailText = await screen.findByText('Free Trial'); expect(freeTrailText).toBeInTheDocument(); - const currentBill = screen.getByText('Billing'); + const currentBill = screen.getByText('billing'); expect(currentBill).toBeInTheDocument(); const dollar0 = await screen.findByText(/\$0/i); @@ -85,18 +85,14 @@ describe('BillingContainer', () => { ); expect(onTrail).toBeInTheDocument(); - const numberOfDayRemaining = await screen.findByText( - /1 days remaining in your billing period./i, - ); + const numberOfDayRemaining = await screen.findByText(/1 days_remaining/i); expect(numberOfDayRemaining).toBeInTheDocument(); const upgradeButton = await screen.findAllByRole('button', { - name: /upgrade/i, + name: /upgrade_plan/i, }); expect(upgradeButton[1]).toBeInTheDocument(); expect(upgradeButton.length).toBe(2); - const checkPaidPlan = await screen.findByText( - /Check out features in paid plans/i, - ); + const checkPaidPlan = await screen.findByText(/checkout_plans/i); expect(checkPaidPlan).toBeInTheDocument(); const link = screen.getByRole('link', { name: /here/i }); @@ -114,7 +110,7 @@ describe('BillingContainer', () => { render(); }); - const currentBill = screen.getByText('Billing'); + const currentBill = screen.getByText('billing'); expect(currentBill).toBeInTheDocument(); const dollar0 = await screen.findByText(/\$0/i); @@ -126,17 +122,17 @@ describe('BillingContainer', () => { expect(onTrail).toBeInTheDocument(); const receivedCardDetails = await screen.findByText( - /We have received your card details, your billing will only start after the end of your free trial period./i, + /card_details_recieved_and_billing_info/i, ); expect(receivedCardDetails).toBeInTheDocument(); const manageBillingButton = await screen.findByRole('button', { - name: /manage billing/i, + name: /manage_billing/i, }); expect(manageBillingButton).toBeInTheDocument(); const dayRemainingInBillingPeriod = await screen.findByText( - /1 days remaining in your billing period./i, + /1 days_remaining/i, ); expect(dayRemainingInBillingPeriod).toBeInTheDocument(); }); @@ -156,7 +152,7 @@ describe('BillingContainer', () => { const billingPeriod = await findByText(billingPeriodText); expect(billingPeriod).toBeInTheDocument(); - const currentBill = screen.getByText('Billing'); + const currentBill = screen.getByText('billing'); expect(currentBill).toBeInTheDocument(); const dollar0 = await screen.findByText(/\$1,278.3/i); @@ -181,7 +177,7 @@ describe('BillingContainer', () => { ); render(); const dayRemainingInBillingPeriod = await screen.findByText( - /11 days remaining in your billing period./i, + /11 days_remaining/i, ); expect(dayRemainingInBillingPeriod).toBeInTheDocument(); }); diff --git a/frontend/src/container/BillingContainer/BillingContainer.tsx b/frontend/src/container/BillingContainer/BillingContainer.tsx index b31f9c4745..9b45801356 100644 --- a/frontend/src/container/BillingContainer/BillingContainer.tsx +++ b/frontend/src/container/BillingContainer/BillingContainer.tsx @@ -17,7 +17,7 @@ import { } from 'antd'; import { ColumnsType } from 'antd/es/table'; import updateCreditCardApi from 'api/billing/checkout'; -import getUsage from 'api/billing/getUsage'; +import getUsage, { UsageResponsePayloadProps } from 'api/billing/getUsage'; import manageCreditCardApi from 'api/billing/manage'; import Spinner from 'components/Spinner'; import { SOMETHING_WENT_WRONG } from 'constants/api'; @@ -26,8 +26,9 @@ import useAnalytics from 'hooks/analytics/useAnalytics'; import useAxiosError from 'hooks/useAxiosError'; import useLicense from 'hooks/useLicense'; import { useNotifications } from 'hooks/useNotifications'; -import { pick } from 'lodash-es'; +import { isEmpty, pick } from 'lodash-es'; import { useCallback, useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { useMutation, useQuery } from 'react-query'; import { useSelector } from 'react-redux'; import { AppState } from 'store/reducers'; @@ -49,6 +50,11 @@ interface DataType { cost: string; } +enum SubscriptionStatus { + PastDue = 'past_due', + Active = 'active', +} + const renderSkeletonInput = (): JSX.Element => ( = [ }, ]; +// eslint-disable-next-line sonarjs/cognitive-complexity export default function BillingContainer(): JSX.Element { - const daysRemainingStr = 'days remaining in your billing period.'; + const { t } = useTranslation(['billings']); + const daysRemainingStr = t('days_remaining'); const [headerText, setHeaderText] = useState(''); const [billAmount, setBillAmount] = useState(0); const [activeLicense, setActiveLicense] = useState(null); const [daysRemaining, setDaysRemaining] = useState(0); const [isFreeTrial, setIsFreeTrial] = useState(false); const [data, setData] = useState([]); - const [apiResponse, setApiResponse] = useState({}); + const [apiResponse, setApiResponse] = useState< + Partial + >({}); const { trackEvent } = useAnalytics(); @@ -139,6 +149,9 @@ export default function BillingContainer(): JSX.Element { const processUsageData = useCallback( (data: any): void => { + if (isEmpty(data?.payload)) { + return; + } const { details: { breakdown = [], billTotal }, billingPeriodStart, @@ -186,6 +199,9 @@ export default function BillingContainer(): JSX.Element { [licensesData?.payload?.onTrial], ); + const isSubscriptionPastDue = + apiResponse.subscriptionStatus === SubscriptionStatus.PastDue; + const { isLoading, isFetching: isFetchingBillingData } = useQuery( [REACT_QUERY_KEY.GET_BILLING_USAGE, user?.userId], { @@ -342,14 +358,27 @@ export default function BillingContainer(): JSX.Element { [apiResponse, billAmount, isLoading, isFetchingBillingData], ); + const { Text } = Typography; + const subscriptionPastDueMessage = (): JSX.Element => ( + + {`We were not able to process payments for your account. Please update your card details `} + + {t('here')} + + {` if your payment information has changed. Email us at `} + cloud-support@signoz.io + {` otherwise. Be sure to provide this information immediately to avoid interruption to your service.`} + + ); + return (
- Billing + {t('billing')} - Manage your billing information, invoices, and monitor costs. + {t('manage_billing_and_costs')} @@ -361,7 +390,7 @@ export default function BillingContainer(): JSX.Element { - {isCloudUserVal ? 'Enterprise Cloud' : 'Enterprise'}{' '} + {isCloudUserVal ? t('enterprise_cloud') : t('enterprise')}{' '} {isFreeTrial ? Free Trial : ''} {!isLoading && !isFetchingBillingData ? ( @@ -378,8 +407,8 @@ export default function BillingContainer(): JSX.Element { onClick={handleBilling} > {isFreeTrial && !licensesData?.payload?.trialConvertedToSubscription - ? 'Upgrade Plan' - : 'Manage Billing'} + ? t('upgrade_plan') + : t('manage_billing')} @@ -389,21 +418,34 @@ export default function BillingContainer(): JSX.Element { ellipsis style={{ fontWeight: '300', color: '#49aa19', fontSize: 12 }} > - We have received your card details, your billing will only start after - the end of your free trial period. + {t('card_details_recieved_and_billing_info')} )} {!isLoading && !isFetchingBillingData ? ( - + headerText && ( + + ) ) : ( )} + + {isSubscriptionPastDue && + (!isLoading && !isFetchingBillingData ? ( + + ) : ( + + ))} @@ -434,16 +476,16 @@ export default function BillingContainer(): JSX.Element { - Upgrade now to have uninterrupted access + {t('upgrade_now_text')} - Your billing will start only after the trial period + {t('Your billing will start only after the trial period')} - Check out features in paid plans   + {t('checkout_plans')}   - here + {t('here')} @@ -464,7 +506,7 @@ export default function BillingContainer(): JSX.Element { loading={isLoadingBilling || isLoadingManageBilling} onClick={handleBilling} > - Upgrade Plan + {t('upgrade_plan')} diff --git a/frontend/src/container/BillingContainer/BillingUsageGraph/BillingUsageGraph.tsx b/frontend/src/container/BillingContainer/BillingUsageGraph/BillingUsageGraph.tsx index fa6ce813a6..be77ebba95 100644 --- a/frontend/src/container/BillingContainer/BillingUsageGraph/BillingUsageGraph.tsx +++ b/frontend/src/container/BillingContainer/BillingUsageGraph/BillingUsageGraph.tsx @@ -3,9 +3,7 @@ import '../../../lib/uPlotLib/uPlotLib.styles.scss'; import { Color } from '@signozhq/design-tokens'; import { Card, Flex, Typography } from 'antd'; -import { getComponentForPanelType } from 'constants/panelTypes'; -import { PANEL_TYPES } from 'constants/queryBuilder'; -import { PropsTypePropsMap } from 'container/GridPanelSwitch/types'; +import Uplot from 'components/Uplot'; import { useIsDarkMode } from 'hooks/useDarkMode'; import { useResizeObserver } from 'hooks/useDimensions'; import tooltipPlugin from 'lib/uPlotLib/plugins/tooltipPlugin'; @@ -14,7 +12,7 @@ import getRenderer from 'lib/uPlotLib/utils/getRenderer'; import { getUPlotChartData } from 'lib/uPlotLib/utils/getUplotChartData'; import { getXAxisScale } from 'lib/uPlotLib/utils/getXAxisScale'; import { getYAxisScale } from 'lib/uPlotLib/utils/getYAxisScale'; -import { FC, useMemo, useRef } from 'react'; +import { useMemo, useRef } from 'react'; import uPlot from 'uplot'; import { @@ -43,6 +41,21 @@ const paths = ( return renderer(u, seriesIdx, idx0, idx1, extendGap, buildClip); }; +const calculateStartEndTime = ( + data: any, +): { startTime: number; endTime: number } => { + const timestamps: number[] = []; + data?.details?.breakdown?.forEach((breakdown: any) => { + breakdown?.dayWiseBreakdown?.breakdown.forEach((entry: any) => { + timestamps.push(entry?.timestamp); + }); + }); + const billingTime = [data?.billingPeriodStart, data?.billingPeriodEnd]; + const startTime: number = Math.min(...timestamps, ...billingTime); + const endTime: number = Math.max(...timestamps, ...billingTime); + return { startTime, endTime }; +}; + export function BillingUsageGraph(props: BillingUsageGraphProps): JSX.Element { const { data, billAmount } = props; const graphCompatibleData = useMemo( @@ -54,11 +67,9 @@ export function BillingUsageGraph(props: BillingUsageGraphProps): JSX.Element { const isDarkMode = useIsDarkMode(); const containerDimensions = useResizeObserver(graphRef); - const { billingPeriodStart: startTime, billingPeriodEnd: endTime } = data; - - const Component = getComponentForPanelType(PANEL_TYPES.BAR) as FC< - PropsTypePropsMap[PANEL_TYPES] - >; + const { startTime, endTime } = useMemo(() => calculateStartEndTime(data), [ + data, + ]); const getGraphSeries = (color: string, label: string): any => ({ drawStyle: 'bars', @@ -183,7 +194,7 @@ export function BillingUsageGraph(props: BillingUsageGraphProps): JSX.Element {
- +
); diff --git a/frontend/src/container/DownloadV2/DownloadV2.styles.scss b/frontend/src/container/DownloadV2/DownloadV2.styles.scss new file mode 100644 index 0000000000..850c1c7d16 --- /dev/null +++ b/frontend/src/container/DownloadV2/DownloadV2.styles.scss @@ -0,0 +1,84 @@ +.download-logs-popover { + .ant-popover-inner { + border-radius: 4px; + border: 1px solid var(--bg-slate-400); + background: linear-gradient( + 139deg, + rgba(18, 19, 23, 0.8) 0%, + rgba(18, 19, 23, 0.9) 98.68% + ); + box-shadow: 4px 10px 16px 2px rgba(0, 0, 0, 0.2); + backdrop-filter: blur(20px); + padding: 12px 18px 12px 14px; + + .download-logs-content { + display: flex; + flex-direction: column; + gap: 8px; + align-items: flex-start; + + .action-btns { + padding: 4px 0px !important; + width: 159px; + display: flex; + align-items: center; + color: var(--bg-vanilla-400); + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: normal; + letter-spacing: 0.14px; + gap: 6px; + + .ant-btn-icon { + margin-inline-end: 0px; + } + } + + .action-btns:hover { + &.ant-btn-text { + background-color: rgba(171, 189, 255, 0.04) !important; + } + } + + .export-heading { + color: #52575c; + font-size: 11px; + font-style: normal; + font-weight: 600; + line-height: 18px; /* 163.636% */ + letter-spacing: 0.88px; + text-transform: uppercase; + } + } + } +} + +.lightMode { + .download-logs-popover { + .ant-popover-inner { + border: 1px solid var(--bg-vanilla-300); + background: linear-gradient( + 139deg, + rgba(255, 255, 255, 0.8) 0%, + rgba(255, 255, 255, 0.9) 98.68% + ); + + box-shadow: 4px 10px 16px 2px rgba(255, 255, 255, 0.2); + + .download-logs-content { + .action-btns { + color: var(--bg-ink-400); + } + .action-btns:hover { + &.ant-btn-text { + background-color: var(--bg-vanilla-300) !important; + } + } + .export-heading { + color: var(--bg-ink-200); + } + } + } + } +} diff --git a/frontend/src/container/DownloadV2/DownloadV2.tsx b/frontend/src/container/DownloadV2/DownloadV2.tsx new file mode 100644 index 0000000000..95630efcb9 --- /dev/null +++ b/frontend/src/container/DownloadV2/DownloadV2.tsx @@ -0,0 +1,84 @@ +import './DownloadV2.styles.scss'; + +import { Button, Popover, Typography } from 'antd'; +import { Excel } from 'antd-table-saveas-excel'; +import { FileDigit, FileDown, Sheet } from 'lucide-react'; +import { unparse } from 'papaparse'; + +import { DownloadProps } from './DownloadV2.types'; + +function Download({ data, isLoading, fileName }: DownloadProps): JSX.Element { + const downloadExcelFile = (): void => { + const headers = Object.keys(Object.assign({}, ...data)).map((item) => { + const updatedTitle = item + .split('_') + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); + return { + title: updatedTitle, + dataIndex: item, + }; + }); + const excel = new Excel(); + excel + .addSheet(fileName) + .addColumns(headers) + .addDataSource(data, { + str2Percent: true, + }) + .saveAs(`${fileName}.xlsx`); + }; + + const downloadCsvFile = (): void => { + const csv = unparse(data); + const csvBlob = new Blob([csv], { type: 'text/csv;charset=utf-8;' }); + const csvUrl = URL.createObjectURL(csvBlob); + const downloadLink = document.createElement('a'); + downloadLink.href = csvUrl; + downloadLink.download = `${fileName}.csv`; + downloadLink.click(); + downloadLink.remove(); + }; + + return ( + + Export As + + +
+ } + > + + + + +
)} - void; } -function ExplorerOptionsDroppableArea({ +function ExplorerOptionsHideArea({ isQueryUpdated, isExplorerOptionHidden, sourcepage, @@ -27,10 +26,6 @@ function ExplorerOptionsDroppableArea({ handleClearSelect, onUpdateQueryHandler, }: DroppableAreaProps): JSX.Element { - const { setNodeRef } = useDroppable({ - id: 'explorer-options-droppable', - }); - const handleShowExplorerOption = (): void => { if (setIsExplorerOptionHidden) { setIsExplorerOptionHidden(false); @@ -39,7 +34,7 @@ function ExplorerOptionsDroppableArea({ }; return ( -
+
{isExplorerOptionHidden && ( <> {isQueryUpdated && ( @@ -75,9 +70,9 @@ function ExplorerOptionsDroppableArea({ ); } -ExplorerOptionsDroppableArea.defaultProps = { +ExplorerOptionsHideArea.defaultProps = { isExplorerOptionHidden: undefined, setIsExplorerOptionHidden: undefined, }; -export default ExplorerOptionsDroppableArea; +export default ExplorerOptionsHideArea; diff --git a/frontend/src/container/FormAlertRules/RuleOptions.tsx b/frontend/src/container/FormAlertRules/RuleOptions.tsx index 88e7c83979..2ef6bba4c0 100644 --- a/frontend/src/container/FormAlertRules/RuleOptions.tsx +++ b/frontend/src/container/FormAlertRules/RuleOptions.tsx @@ -1,5 +1,6 @@ import { Checkbox, + Collapse, Form, InputNumber, InputNumberProps, @@ -19,12 +20,18 @@ import { AlertDef, defaultCompareOp, defaultEvalWindow, + defaultFrequency, defaultMatchType, } from 'types/api/alerts/def'; import { EQueryType } from 'types/common/dashboard'; import { popupContainer } from 'utils/selectPopupContainer'; -import { FormContainer, InlineSelect, StepHeading } from './styles'; +import { + FormContainer, + InlineSelect, + StepHeading, + VerticalLine, +} from './styles'; function RuleOptions({ alertDef, @@ -200,6 +207,35 @@ function RuleOptions({ }); }; + const onChangeFrequency = (value: string | unknown): void => { + const freq = (value as string) || alertDef.frequency; + setAlertDef({ + ...alertDef, + frequency: freq, + }); + }; + + const renderFrequency = (): JSX.Element => ( + + {t('option_1min')} + {t('option_5min')} + {t('option_10min')} + {t('option_15min')} + {t('option_30min')} + {t('option_60min')} + {t('option_3hours')} + {t('option_6hours')} + {t('option_12hours')} + {t('option_24hours')} + + ); + const selectedCategory = getCategoryByOptionId(currentQuery?.unit || ''); const categorySelectOptions = getCategorySelectOptionByName( @@ -238,42 +274,57 @@ function RuleOptions({ /> - - - { - setAlertDef({ - ...alertDef, - condition: { - ...alertDef.condition, - alertOnAbsent: e.target.checked, - }, - }); - }} - /> - - {t('text_alert_on_absent')} + + + + + + {t('text_alert_frequency')} + {renderFrequency()} + + - - { - setAlertDef({ - ...alertDef, - condition: { - ...alertDef.condition, - absentFor: Number(value) || 0, - }, - }); - }} - type="number" - onWheel={(e): void => e.currentTarget.blur()} - /> - - {t('text_for')} - + + + + { + setAlertDef({ + ...alertDef, + condition: { + ...alertDef.condition, + alertOnAbsent: e.target.checked, + }, + }); + }} + /> + + {t('text_alert_on_absent')} + + + { + setAlertDef({ + ...alertDef, + condition: { + ...alertDef.condition, + absentFor: Number(value) || 0, + }, + }); + }} + type="number" + onWheel={(e): void => e.currentTarget.blur()} + /> + + {t('text_for')} + + + + + diff --git a/frontend/src/container/FormAlertRules/styles.ts b/frontend/src/container/FormAlertRules/styles.ts index 9fcaf4c59c..11205c0ab4 100644 --- a/frontend/src/container/FormAlertRules/styles.ts +++ b/frontend/src/container/FormAlertRules/styles.ts @@ -67,6 +67,13 @@ export const SeveritySelect = styled(Select)` width: 25% !important; `; +export const VerticalLine = styled.div` + border-left: 2px solid #e8e8e8; /* Adjust color and thickness as desired */ + padding-left: 20px; /* Adjust spacing to content as needed */ + margin-left: 20px; /* Adjust margin as desired */ + height: 100%; /* Adjust based on your layout needs */ +`; + export const InputSmall = styled(Input)` width: 40% !important; `; diff --git a/frontend/src/container/LiveLogs/LiveLogsList/index.tsx b/frontend/src/container/LiveLogs/LiveLogsList/index.tsx index 7be2927445..39a39ab990 100644 --- a/frontend/src/container/LiveLogs/LiveLogsList/index.tsx +++ b/frontend/src/container/LiveLogs/LiveLogsList/index.tsx @@ -71,6 +71,7 @@ function LiveLogsList({ logs }: LiveLogsListProps): JSX.Element { key={log.id} logData={log} selectedFields={selectedFields} + linesPerRow={options.maxLines} onAddToQuery={onAddToQuery} onSetActiveLog={onSetActiveLog} /> diff --git a/frontend/src/container/LogsExplorerList/LogsExplorerList.style.scss b/frontend/src/container/LogsExplorerList/LogsExplorerList.style.scss index 29dc383b7d..be6b8d627f 100644 --- a/frontend/src/container/LogsExplorerList/LogsExplorerList.style.scss +++ b/frontend/src/container/LogsExplorerList/LogsExplorerList.style.scss @@ -8,4 +8,5 @@ line-height: 18px; letter-spacing: -0.005em; text-align: left; + min-height: 500px; } diff --git a/frontend/src/container/LogsExplorerList/index.tsx b/frontend/src/container/LogsExplorerList/index.tsx index 21b03cf413..fc5a1f6800 100644 --- a/frontend/src/container/LogsExplorerList/index.tsx +++ b/frontend/src/container/LogsExplorerList/index.tsx @@ -90,6 +90,7 @@ function LogsExplorerList({ onAddToQuery={onAddToQuery} onSetActiveLog={onSetActiveLog} activeLog={activeLog} + linesPerRow={options.maxLines} /> ); }, diff --git a/frontend/src/container/LogsExplorerViews/index.tsx b/frontend/src/container/LogsExplorerViews/index.tsx index 45b33d01af..c814ac8cb6 100644 --- a/frontend/src/container/LogsExplorerViews/index.tsx +++ b/frontend/src/container/LogsExplorerViews/index.tsx @@ -14,6 +14,7 @@ import { PANEL_TYPES, } from 'constants/queryBuilder'; import { DEFAULT_PER_PAGE_VALUE } from 'container/Controls/config'; +import Download from 'container/DownloadV2/DownloadV2'; import ExplorerOptionWrapper from 'container/ExplorerOptions/ExplorerOptionWrapper'; import GoToTop from 'container/GoToTop'; import LogsExplorerChart from 'container/LogsExplorerChart'; @@ -21,6 +22,7 @@ import LogsExplorerList from 'container/LogsExplorerList'; import LogsExplorerTable from 'container/LogsExplorerTable'; import { useOptionsMenu } from 'container/OptionsMenu'; import TimeSeriesView from 'container/TimeSeriesView/TimeSeriesView'; +import dayjs from 'dayjs'; import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard'; import { addEmptyWidgetInDashboardJSONWithQuery } from 'hooks/dashboard/utils'; import { LogTimeRange } from 'hooks/logs/types'; @@ -33,8 +35,9 @@ import useClickOutside from 'hooks/useClickOutside'; import { useHandleExplorerTabChange } from 'hooks/useHandleExplorerTabChange'; import { useNotifications } from 'hooks/useNotifications'; import useUrlQueryData from 'hooks/useUrlQueryData'; +import { FlatLogData } from 'lib/logs/flatLogData'; import { getPaginationQueryData } from 'lib/newQueryBuilder/getPaginationQueryData'; -import { defaultTo, isEmpty } from 'lodash-es'; +import { defaultTo, isEmpty, omit } from 'lodash-es'; import { Sliders } from 'lucide-react'; import { SELECTED_VIEWS } from 'pages/LogsExplorer/utils'; import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; @@ -523,6 +526,23 @@ function LogsExplorerViews({ }, }); + const flattenLogData = useMemo( + () => + logs.map((log) => { + const timestamp = + typeof log.timestamp === 'string' + ? dayjs(log.timestamp).format() + : dayjs(log.timestamp / 1e6).format(); + + return FlatLogData({ + timestamp, + body: log.body, + ...omit(log, 'timestamp', 'body'), + }); + }), + [logs], + ); + return (
{showHistogram && ( @@ -578,6 +598,11 @@ function LogsExplorerViews({
{selectedPanelType === PANEL_TYPES.LIST && (
+
+ +
+ +
+ {queriesExcludingEnvironment.map((query) => ( + + ))} + {staging.map((query, idx) => ( + + {idx === 0 ? convertMetricKeyToTrace(query) : query} + + ))} +
+