mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-11 17:19:06 +08:00
feat: add support for apdex settings (#3186)
This commit is contained in:
parent
b9409820cc
commit
cac637ac88
46
pkg/query-service/app/apdex.go
Normal file
46
pkg/query-service/app/apdex.go
Normal file
@ -0,0 +1,46 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"go.signoz.io/signoz/pkg/query-service/dao"
|
||||
"go.signoz.io/signoz/pkg/query-service/model"
|
||||
)
|
||||
|
||||
func (aH *APIHandler) setApdexSettings(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := parseSetApdexScoreRequest(r)
|
||||
if aH.HandleError(w, err, http.StatusBadRequest) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := dao.DB().SetApdexSettings(context.Background(), req); err != nil {
|
||||
RespondError(w, &model.ApiError{Err: err, Typ: model.ErrorInternal}, nil)
|
||||
return
|
||||
}
|
||||
|
||||
aH.WriteJSON(w, r, map[string]string{"data": "apdex score updated successfully"})
|
||||
}
|
||||
|
||||
func (aH *APIHandler) getApdexSettings(w http.ResponseWriter, r *http.Request) {
|
||||
services := r.URL.Query().Get("services")
|
||||
apdexSet, err := dao.DB().GetApdexSettings(context.Background(), strings.Split(strings.TrimSpace(services), ","))
|
||||
if err != nil {
|
||||
RespondError(w, &model.ApiError{Err: err, Typ: model.ErrorInternal}, nil)
|
||||
return
|
||||
}
|
||||
|
||||
aH.WriteJSON(w, r, apdexSet)
|
||||
}
|
||||
|
||||
func (aH *APIHandler) getLatencyMetricMetadata(w http.ResponseWriter, r *http.Request) {
|
||||
metricName := r.URL.Query().Get("metricName")
|
||||
metricMetadata, err := aH.reader.GetLatencyMetricMetadata(r.Context(), metricName, aH.preferDelta)
|
||||
if err != nil {
|
||||
RespondError(w, &model.ApiError{Err: err, Typ: model.ErrorInternal}, nil)
|
||||
return
|
||||
}
|
||||
|
||||
aH.WriteJSON(w, r, metricMetadata)
|
||||
}
|
@ -5,6 +5,7 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"math"
|
||||
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
@ -3829,6 +3830,52 @@ func (r *ClickHouseReader) GetMetricAttributeValues(ctx context.Context, req *v3
|
||||
return &attributeValues, nil
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetLatencyMetricMetadata(ctx context.Context, metricName string, preferDelta bool) (*v3.LatencyMetricMetadataResponse, error) {
|
||||
query := fmt.Sprintf("SELECT DISTINCT(temporality) from %s.%s WHERE metric_name='%s'", signozMetricDBName, signozTSTableName, metricName)
|
||||
rows, err := r.db.Query(ctx, query, metricName)
|
||||
if err != nil {
|
||||
zap.S().Error(err)
|
||||
return nil, fmt.Errorf("error while executing query: %s", err.Error())
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var deltaExists bool
|
||||
for rows.Next() {
|
||||
var temporality string
|
||||
if err := rows.Scan(&temporality); err != nil {
|
||||
return nil, fmt.Errorf("error while scanning rows: %s", err.Error())
|
||||
}
|
||||
if temporality == string(v3.Delta) {
|
||||
deltaExists = true
|
||||
}
|
||||
}
|
||||
|
||||
query = fmt.Sprintf("SELECT DISTINCT(toFloat64(JSONExtractString(labels, 'le'))) as le from %s.%s WHERE metric_name='%s' ORDER BY le", signozMetricDBName, signozTSTableName, metricName)
|
||||
rows, err = r.db.Query(ctx, query, metricName)
|
||||
if err != nil {
|
||||
zap.S().Error(err)
|
||||
return nil, fmt.Errorf("error while executing query: %s", err.Error())
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var leFloat64 []float64
|
||||
for rows.Next() {
|
||||
var le float64
|
||||
if err := rows.Scan(&le); err != nil {
|
||||
return nil, fmt.Errorf("error while scanning rows: %s", err.Error())
|
||||
}
|
||||
if math.IsInf(le, 0) {
|
||||
continue
|
||||
}
|
||||
leFloat64 = append(leFloat64, le)
|
||||
}
|
||||
|
||||
return &v3.LatencyMetricMetadataResponse{
|
||||
Delta: deltaExists && preferDelta,
|
||||
Le: leFloat64,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func isColumn(tableStatement, field string) bool {
|
||||
return strings.Contains(tableStatement, fmt.Sprintf("`%s` ", field))
|
||||
}
|
||||
|
@ -332,6 +332,10 @@ func (aH *APIHandler) RegisterRoutes(router *mux.Router, am *AuthMiddleware) {
|
||||
router.HandleFunc("/api/v1/dependency_graph", am.ViewAccess(aH.dependencyGraph)).Methods(http.MethodPost)
|
||||
router.HandleFunc("/api/v1/settings/ttl", am.AdminAccess(aH.setTTL)).Methods(http.MethodPost)
|
||||
router.HandleFunc("/api/v1/settings/ttl", am.ViewAccess(aH.getTTL)).Methods(http.MethodGet)
|
||||
router.HandleFunc("/api/v1/settings/apdex", am.AdminAccess(aH.setApdexSettings)).Methods(http.MethodPost)
|
||||
router.HandleFunc("/api/v1/settings/apdex", am.ViewAccess(aH.getApdexSettings)).Methods(http.MethodGet)
|
||||
|
||||
router.HandleFunc("/api/v1/metric_meta", am.ViewAccess(aH.getLatencyMetricMetadata)).Methods(http.MethodGet)
|
||||
|
||||
router.HandleFunc("/api/v1/version", am.OpenAccess(aH.getVersion)).Methods(http.MethodGet)
|
||||
router.HandleFunc("/api/v1/featureFlags", am.OpenAccess(aH.getFeatureFlags)).Methods(http.MethodGet)
|
||||
|
@ -726,6 +726,14 @@ func parseInviteRequest(r *http.Request) (*model.InviteRequest, error) {
|
||||
return &req, nil
|
||||
}
|
||||
|
||||
func parseSetApdexScoreRequest(r *http.Request) (*model.ApdexSettings, error) {
|
||||
var req model.ApdexSettings
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &req, nil
|
||||
}
|
||||
|
||||
func parseRegisterRequest(r *http.Request) (*auth.RegisterRequest, error) {
|
||||
var req auth.RegisterRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
|
@ -32,6 +32,8 @@ type Queries interface {
|
||||
GetResetPasswordEntry(ctx context.Context, token string) (*model.ResetPasswordEntry, *model.ApiError)
|
||||
GetUsersByOrg(ctx context.Context, orgId string) ([]model.UserPayload, *model.ApiError)
|
||||
GetUsersByGroup(ctx context.Context, groupId string) ([]model.UserPayload, *model.ApiError)
|
||||
|
||||
GetApdexSettings(ctx context.Context, services []string) ([]model.ApdexSettings, *model.ApiError)
|
||||
}
|
||||
|
||||
type Mutations interface {
|
||||
@ -56,4 +58,6 @@ type Mutations interface {
|
||||
|
||||
UpdateUserPassword(ctx context.Context, hash, userId string) *model.ApiError
|
||||
UpdateUserGroup(ctx context.Context, userId, groupId string) *model.ApiError
|
||||
|
||||
SetApdexSettings(ctx context.Context, set *model.ApdexSettings) *model.ApiError
|
||||
}
|
||||
|
74
pkg/query-service/dao/sqlite/apdex.go
Normal file
74
pkg/query-service/dao/sqlite/apdex.go
Normal file
@ -0,0 +1,74 @@
|
||||
package sqlite
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"go.signoz.io/signoz/pkg/query-service/model"
|
||||
)
|
||||
|
||||
const defaultApdexThreshold = 0.5
|
||||
|
||||
func (mds *ModelDaoSqlite) GetApdexSettings(ctx context.Context, services []string) ([]model.ApdexSettings, *model.ApiError) {
|
||||
var apdexSettings []model.ApdexSettings
|
||||
var serviceName string
|
||||
|
||||
for i, service := range services {
|
||||
if i == 0 {
|
||||
serviceName = fmt.Sprintf("'%s'", service)
|
||||
} else {
|
||||
serviceName = fmt.Sprintf("%s, '%s'", serviceName, service)
|
||||
}
|
||||
}
|
||||
|
||||
query := fmt.Sprintf("SELECT * FROM apdex_settings WHERE service_name IN (%s)", serviceName)
|
||||
|
||||
err := mds.db.Select(&apdexSettings, query)
|
||||
if err != nil {
|
||||
return nil, &model.ApiError{
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
// add default apdex settings for services that don't have any
|
||||
for _, service := range services {
|
||||
var found bool
|
||||
for _, apdexSetting := range apdexSettings {
|
||||
if apdexSetting.ServiceName == service {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
apdexSettings = append(apdexSettings, model.ApdexSettings{
|
||||
ServiceName: service,
|
||||
Threshold: defaultApdexThreshold,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return apdexSettings, nil
|
||||
}
|
||||
|
||||
func (mds *ModelDaoSqlite) SetApdexSettings(ctx context.Context, apdexSettings *model.ApdexSettings) *model.ApiError {
|
||||
|
||||
fmt.Println("apdexSettings:", apdexSettings)
|
||||
_, err := mds.db.NamedExec(`
|
||||
INSERT OR REPLACE INTO apdex_settings (
|
||||
service_name,
|
||||
threshold,
|
||||
exclude_status_codes
|
||||
) VALUES (
|
||||
:service_name,
|
||||
:threshold,
|
||||
:exclude_status_codes
|
||||
)`, apdexSettings)
|
||||
if err != nil {
|
||||
return &model.ApiError{
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -73,6 +73,11 @@ func InitDB(dataSourceName string) (*ModelDaoSqlite, error) {
|
||||
flags TEXT,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS apdex_settings (
|
||||
service_name TEXT PRIMARY KEY,
|
||||
threshold FLOAT NOT NULL,
|
||||
exclude_status_codes TEXT NOT NULL
|
||||
);
|
||||
`
|
||||
|
||||
_, err = db.Exec(table_schema)
|
||||
|
@ -95,6 +95,8 @@ type Reader interface {
|
||||
|
||||
QueryDashboardVars(ctx context.Context, query string) (*model.DashboardVar, error)
|
||||
CheckClickHouse(ctx context.Context) error
|
||||
|
||||
GetLatencyMetricMetadata(context.Context, string, bool) (*v3.LatencyMetricMetadataResponse, error)
|
||||
}
|
||||
|
||||
type Querier interface {
|
||||
|
@ -36,6 +36,12 @@ type User struct {
|
||||
GroupId string `json:"groupId,omitempty" db:"group_id"`
|
||||
}
|
||||
|
||||
type ApdexSettings struct {
|
||||
ServiceName string `json:"serviceName" db:"service_name"`
|
||||
Threshold float64 `json:"threshold" db:"threshold"`
|
||||
ExcludeStatusCodes string `json:"excludeStatusCodes" db:"exclude_status_codes"` // sqlite doesn't support array type
|
||||
}
|
||||
|
||||
type UserFlag map[string]string
|
||||
|
||||
func (uf UserFlag) Value() (driver.Value, error) {
|
||||
|
@ -663,3 +663,8 @@ func (eq *ExplorerQuery) Validate() error {
|
||||
}
|
||||
return eq.CompositeQuery.Validate()
|
||||
}
|
||||
|
||||
type LatencyMetricMetadataResponse struct {
|
||||
Delta bool `json:"delta"`
|
||||
Le []float64 `json:"le"`
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user