From f8d0d456fc55a0e32d690250213923530588e7a0 Mon Sep 17 00:00:00 2001 From: tanlang Date: Wed, 6 Dec 2023 18:12:38 +0800 Subject: [PATCH] feat: add more metrics --- .golangci.yml | 11 +++++ auth/jwt.go | 116 +++++++++++++++++++++++++++++++++++++++++------ cli/run.go | 13 ++++++ config/config.go | 21 ++++++--- core/metrics.go | 26 +++++++++++ go.mod | 2 +- go.sum | 4 +- 7 files changed, 170 insertions(+), 23 deletions(-) create mode 100644 core/metrics.go diff --git a/.golangci.yml b/.golangci.yml index 34dcdd7..af771b2 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -43,6 +43,17 @@ issues: linters-settings: goconst: min-occurrences: 6 + + + revive: + rules: + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unused-parameter + - name: unused-parameter + severity: warning + disabled: true + arguments: + - allowRegex: "^_" + run: skip-dirs-use-default: false diff --git a/auth/jwt.go b/auth/jwt.go index 08758a5..ae15d46 100644 --- a/auth/jwt.go +++ b/auth/jwt.go @@ -12,6 +12,7 @@ import ( "github.com/gbrlsnchs/jwt/v3" "github.com/google/uuid" + "go.opencensus.io/tag" "golang.org/x/xerrors" "github.com/filecoin-project/go-address" @@ -86,11 +87,47 @@ type JWTPayload struct { } func NewOAuthService(dbPath string, cnf *config.DBConfig) (OAuthService, error) { + ctx := context.Background() store, err := storage.NewStore(cnf, dbPath) if err != nil { return nil, err } + // check amount of token and user + tokens, err := store.List(0, 0) + if err != nil { + return nil, fmt.Errorf("check token: %w", err) + } + tokenCount := map[core.Permission]int64{ + core.PermRead: 0, + core.PermWrite: 0, + core.PermSign: 0, + core.PermAdmin: 0, + } + for _, token := range tokens { + tokenCount[token.Perm]++ + } + for perm, count := range tokenCount { + core.TokenGauge.Set(ctx, perm, count) + } + + // check user + users, err := store.ListUsers(0, 1, core.UserStateUndefined) + if err != nil { + return nil, fmt.Errorf("check user: %w", err) + } + userCount := map[core.UserState]int64{ + core.UserStateUndefined: 0, + core.UserStateEnabled: 0, + core.UserStateDisabled: 0, + } + for _, user := range users { + userCount[user.State] += 1 + } + for state, count := range userCount { + core.UserGauge.Set(ctx, state.String(), count) + } + jwtOAuthInstance = &jwtOAuth{ store: store, mp: newMapper(), @@ -137,30 +174,52 @@ func (o *jwtOAuth) GenerateToken(ctx context.Context, pl *JWTPayload) (string, e if err != nil { return core.EmptyString, xerrors.Errorf("store token failed :%s", err) } + + core.TokenGauge.Inc(ctx, pl.Perm, 1) return token.String(), nil } -func (o *jwtOAuth) Verify(ctx context.Context, token string) (*JWTPayload, error) { - err := permCheck(ctx, core.PermRead) +func (o *jwtOAuth) Verify(ctx context.Context, token string) (payload *JWTPayload, err error) { + defer func() { + if payload != nil { + ctx, _ = tag.New(ctx, tag.Upsert(core.TagPerm, payload.Perm), tag.Upsert(core.TagTokenName, payload.Name)) + } + if err != nil { + ctx, _ = tag.New(ctx, tag.Upsert(core.TagVerifyState, core.VerifyStateFailed)) + } else { + ctx, _ = tag.New(ctx, tag.Upsert(core.TagVerifyState, core.VerifyStateSuccess)) + } + core.TokenVerifyCounter.Tick(ctx) + }() + + payload, err = DecodeToken(token) if err != nil { - return nil, fmt.Errorf("need read prem: %w", err) + return + } + + err = permCheck(ctx, core.PermRead) + if err != nil { + err = fmt.Errorf("need read prem: %w", err) + return } - p := new(JWTPayload) tk := []byte(token) kp, err := o.store.Get(storage.Token(token)) if err != nil { - return nil, xerrors.Errorf("get token: %v", err) + err = xerrors.Errorf("get token: %v", err) + return } secret, err := hex.DecodeString(kp.Secret) if err != nil { - return nil, xerrors.Errorf("decode secret %v", err) + err = xerrors.Errorf("decode secret %v", err) + return } - if _, err := jwt.Verify(tk, jwt.NewHS256(secret), p); err != nil { - return nil, ErrorVerificationFailed + if _, err = jwt.Verify(tk, jwt.NewHS256(secret), payload); err != nil { + err = ErrorVerificationFailed + return } - return p, nil + return } type TokenInfo struct { @@ -249,6 +308,9 @@ func (o *jwtOAuth) RemoveToken(ctx context.Context, token string) error { if err != nil { return fmt.Errorf("remove token %s: %w", token, err) } + + payload, _ := DecodeToken(token) + core.TokenGauge.Inc(ctx, payload.Perm, -1) return nil } @@ -262,6 +324,9 @@ func (o *jwtOAuth) RecoverToken(ctx context.Context, token string) error { if err != nil { return fmt.Errorf("recover token %s: %w", token, err) } + + payload, _ := DecodeToken(token) + core.TokenGauge.Inc(ctx, payload.Perm, 1) return nil } @@ -297,6 +362,7 @@ func (o *jwtOAuth) CreateUser(ctx context.Context, req *CreateUserRequest) (*Cre if err != nil { return nil, err } + core.UserGauge.Inc(ctx, userNew.State.String(), 1) return o.mp.ToOutPutUser(userNew), nil } @@ -356,8 +422,12 @@ func (o *jwtOAuth) DeleteUser(ctx context.Context, req *DeleteUserRequest) error if err != nil { return fmt.Errorf("need admin prem: %w", err) } - - return o.store.DeleteUser(req.Name) + err = o.store.DeleteUser(req.Name) + if err != nil { + return err + } + core.UserGauge.Inc(ctx, core.UserStateDisabled.String(), -1) + return nil } func (o *jwtOAuth) RecoverUser(ctx context.Context, req *RecoverUserRequest) error { @@ -365,7 +435,12 @@ func (o *jwtOAuth) RecoverUser(ctx context.Context, req *RecoverUserRequest) err if err != nil { return fmt.Errorf("need admin prem: %w", err) } - return o.store.RecoverUser(req.Name) + err = o.store.RecoverUser(req.Name) + if err != nil { + return err + } + core.UserGauge.Inc(ctx, core.UserStateDisabled.String(), 1) + return nil } func (o *jwtOAuth) GetUserByMiner(ctx context.Context, req *GetUserByMinerRequest) (*OutputUser, error) { @@ -630,7 +705,7 @@ func DecodeToBytes(enc []byte) ([]byte, error) { func JwtUserFromToken(token string) (string, error) { sks := strings.Split(token, ".") - if len(sks) < 1 { + if len(sks) < 2 { return "", fmt.Errorf("can't parse user from input token") } dec, err := DecodeToBytes([]byte(sks[1])) @@ -643,6 +718,21 @@ func JwtUserFromToken(token string) (string, error) { return payload.Name, err } +func DecodeToken(token string) (*JWTPayload, error) { + sks := strings.Split(token, ".") + if len(sks) < 2 { + return nil, fmt.Errorf("can't parse user from input token") + } + dec, err := DecodeToBytes([]byte(sks[1])) + if err != nil { + return nil, err + } + payload := &JWTPayload{} + err = json.Unmarshal(dec, payload) + + return payload, err +} + func IsSignerAddress(addr address.Address) bool { protocol := addr.Protocol() return protocol == address.SECP256K1 || protocol == address.BLS || protocol == address.Delegated diff --git a/cli/run.go b/cli/run.go index d327fe4..ee85612 100644 --- a/cli/run.go +++ b/cli/run.go @@ -9,6 +9,7 @@ import ( "github.com/ipfs-force-community/metrics" "github.com/ipfs-force-community/sophon-auth/auth" "github.com/ipfs-force-community/sophon-auth/config" + "github.com/ipfs-force-community/sophon-auth/core" "github.com/ipfs-force-community/sophon-auth/log" "github.com/ipfs-force-community/sophon-auth/util" "github.com/urfave/cli/v2" @@ -78,6 +79,7 @@ func fillConfigByFlag(cnf *config.Config, cliCtx *cli.Context) *config.Config { } func run(cliCtx *cli.Context) error { + ctx := cliCtx.Context repoPath, err := GetRepoPath(cliCtx) if err != nil { return err @@ -136,6 +138,14 @@ func run(cliCtx *cli.Context) error { } } + // set up metrics + if cnf.Metrics != nil { + err := metrics.SetupMetrics(ctx, cnf.Metrics) + if err != nil { + log.Warnf("setup metrics: %s", err) + } + } + server := &http.Server{ Addr: cnf.Listen, Handler: router, @@ -144,5 +154,8 @@ func run(cliCtx *cli.Context) error { IdleTimeout: cnf.IdleTimeout, } log.Infof("server start and listen on %s", cnf.Listen) + + core.ApiState.Set(ctx, 1) + defer core.ApiState.Set(ctx, 0) return server.ListenAndServe() } diff --git a/config/config.go b/config/config.go index 61211e8..2666cfc 100644 --- a/config/config.go +++ b/config/config.go @@ -13,13 +13,15 @@ import ( ) type Config struct { - Listen string `json:"listen"` - ReadTimeout time.Duration `json:"readTimeout"` - WriteTimeout time.Duration `json:"writeTimeout"` - IdleTimeout time.Duration `json:"idleTimeout"` - Log *LogConfig `json:"log"` - DB *DBConfig `json:"db"` - Trace *metrics.TraceConfig `json:"traceConfig"` + Listen string `json:"listen"` + ReadTimeout time.Duration `json:"readTimeout"` + WriteTimeout time.Duration `json:"writeTimeout"` + IdleTimeout time.Duration `json:"idleTimeout"` + Log *LogConfig `json:"log"` + DB *DBConfig `json:"db"` + + Trace *metrics.TraceConfig `json:"traceConfig"` + Metrics *metrics.MetricsConfig `json:"metricsExporter"` } type DBType = string @@ -49,6 +51,10 @@ func RandSecret() ([]byte, error) { } func DefaultConfig() *Config { + defMetricsCfg := metrics.DefaultMetricsConfig() + defMetricsCfg.Exporter.Graphite.Namespace = "sophon_auth" + defMetricsCfg.Exporter.Prometheus.Namespace = "sophon_auth" + return &Config{ Listen: "127.0.0.1:8989", ReadTimeout: time.Minute, @@ -60,6 +66,7 @@ func DefaultConfig() *Config { JaegerEndpoint: "localhost:6831", ServerName: "sophon-auth", }, + Metrics: defMetricsCfg, Log: &LogConfig{ LogLevel: "trace", HookSwitch: false, diff --git a/core/metrics.go b/core/metrics.go new file mode 100644 index 0000000..85ac2f3 --- /dev/null +++ b/core/metrics.go @@ -0,0 +1,26 @@ +package core + +import ( + "github.com/ipfs-force-community/metrics" + "go.opencensus.io/tag" +) + +var ( + emptyUnit = "" + + VerifyStateFailed = "failed" + VerifyStateSuccess = "success" + + TagPerm = tag.MustNewKey("perm") + TagUserState = tag.MustNewKey("user_state") + TagTokenName = tag.MustNewKey("token_name") + TagUserName = tag.MustNewKey("user_name") + TagVerifyState = tag.MustNewKey("verify_state") +) + +var ( + TokenGauge = metrics.NewInt64WithCategory("token/amount", "amount of token", emptyUnit) + UserGauge = metrics.NewInt64WithCategory("user/amount", "amount of user", emptyUnit) + TokenVerifyCounter = metrics.NewCounter("token/verify", "amount of token verify", TagPerm, TagVerifyState) + ApiState = metrics.NewInt64("api/state", "api service state. 0: down, 1: up", emptyUnit) +) diff --git a/go.mod b/go.mod index 11c7b21..5b62181 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/golang/mock v1.6.0 github.com/google/uuid v1.3.0 github.com/influxdata/influxdb-client-go/v2 v2.2.2 - github.com/ipfs-force-community/metrics v1.0.1-0.20231011024528-8c881d456601 + github.com/ipfs-force-community/metrics v1.0.1-0.20231205060849-0b0d16ed0e8d github.com/ipfs/go-log/v2 v2.5.1 github.com/mitchellh/go-homedir v1.1.0 github.com/multiformats/go-multiaddr v0.8.0 diff --git a/go.sum b/go.sum index ce1f67b..a9f1f37 100644 --- a/go.sum +++ b/go.sum @@ -319,8 +319,8 @@ github.com/influxdata/influxdb-client-go/v2 v2.2.2 h1:O0CGIuIwQafvAxttAJ/VqMKfbW github.com/influxdata/influxdb-client-go/v2 v2.2.2/go.mod h1:fa/d1lAdUHxuc1jedx30ZfNG573oQTQmUni3N6pcW+0= github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 h1:W9WBk7wlPfJLvMCdtV4zPulc4uCPrlywQOmbFOhgQNU= github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= -github.com/ipfs-force-community/metrics v1.0.1-0.20231011024528-8c881d456601 h1:zxKQ30KAD6KfvSFAx9tuqQXLDsEHyF+eVaUBXXYC2bU= -github.com/ipfs-force-community/metrics v1.0.1-0.20231011024528-8c881d456601/go.mod h1:wM6EmkEcnJgWOFcVytgvK0u15awEmt8He0f2kAdsFDA= +github.com/ipfs-force-community/metrics v1.0.1-0.20231205060849-0b0d16ed0e8d h1:U7aqH9MnV+HNErbZ4VNhr0tKk158GjTXvNfsj8UDZjI= +github.com/ipfs-force-community/metrics v1.0.1-0.20231205060849-0b0d16ed0e8d/go.mod h1:wM6EmkEcnJgWOFcVytgvK0u15awEmt8He0f2kAdsFDA= github.com/ipfs/go-block-format v0.0.2/go.mod h1:AWR46JfpcObNfg3ok2JHDUfdiHRgWhJgCQF+KIgOPJY= github.com/ipfs/go-block-format v0.1.1 h1:129vSO3zwbsYADcyQWcOYiuCpAqt462SFfqFHdFJhhI= github.com/ipfs/go-block-format v0.1.1/go.mod h1:+McEIT+g52p+zz5xGAABGSOKrzmrdX97bc0USBdWPUs=