diff --git a/a2r/api2rpc.go b/a2r/api2rpc.go index cc7dcca..f49ee5f 100644 --- a/a2r/api2rpc.go +++ b/a2r/api2rpc.go @@ -31,17 +31,17 @@ func Call[A, B, C any](rpc func(client C, ctx context.Context, req *A, options . var req A if err := c.BindJSON(&req); err != nil { log.ZWarn(c, "gin bind json error", err, "req", req) - apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap()) // 参数错误 + apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap()) //args error return } if err := checker.Validate(&req); err != nil { - apiresp.GinError(c, err) // 参数校验失败 + apiresp.GinError(c, err) // args validate error return } data, err := rpc(client, c, &req) if err != nil { - apiresp.GinError(c, err) // RPC调用失败 + apiresp.GinError(c, err) // rpc call failed return } - apiresp.GinSuccess(c, data) // 成功 + apiresp.GinSuccess(c, data) // rpc call success } diff --git a/errs/coderr.go b/errs/coderr.go index d478ecf..1ef292c 100644 --- a/errs/coderr.go +++ b/errs/coderr.go @@ -28,7 +28,8 @@ type CodeError interface { Msg() string Detail() string WithDetail(detail string) CodeError - // Is 判断是否是某个错误, loose为false时, 只有错误码相同就认为是同一个错误, 默认为true + // Is checks if the error is of a certain type, when loose is false, + //only the error code is the same is considered the same error, default is true Is(err error, loose ...bool) bool Wrap() error WrapMsg(msg string, kv ...any) error diff --git a/errs/predefine.go b/errs/predefine.go index 50c5d9a..22c4ff7 100644 --- a/errs/predefine.go +++ b/errs/predefine.go @@ -38,11 +38,11 @@ var ( ErrData = NewCodeError(DataError, "DataError") ErrTokenExpired = NewCodeError(TokenExpiredError, "TokenExpiredError") ErrTokenInvalid = NewCodeError(TokenInvalidError, "TokenInvalidError") // - ErrTokenMalformed = NewCodeError(TokenMalformedError, "TokenMalformedError") // 格式错误 - ErrTokenNotValidYet = NewCodeError(TokenNotValidYetError, "TokenNotValidYetError") // 还未生效 - ErrTokenUnknown = NewCodeError(TokenUnknownError, "TokenUnknownError") // 未知错误 + ErrTokenMalformed = NewCodeError(TokenMalformedError, "TokenMalformedError") // + ErrTokenNotValidYet = NewCodeError(TokenNotValidYetError, "TokenNotValidYetError") // + ErrTokenUnknown = NewCodeError(TokenUnknownError, "TokenUnknownError") // ErrTokenKicked = NewCodeError(TokenKickedError, "TokenKickedError") - ErrTokenNotExist = NewCodeError(TokenNotExistError, "TokenNotExistError") // 在redis中不存在 + ErrTokenNotExist = NewCodeError(TokenNotExistError, "TokenNotExistError") // ErrDuplicateKey = NewCodeError(DuplicateKeyError, "DuplicateKeyError") ErrMessageHasReadDisable = NewCodeError(MessageHasReadDisable, "MessageHasReadDisable") diff --git a/go.mod b/go.mod index 5309b0a..bd65ee9 100644 --- a/go.mod +++ b/go.mod @@ -15,6 +15,7 @@ require ( go.mongodb.org/mongo-driver v1.12.0 go.uber.org/zap v1.24.0 google.golang.org/grpc v1.56.2 + gorm.io/gorm v1.25.8 ) require ( @@ -32,6 +33,8 @@ require ( github.com/jcmturner/gofork v1.7.6 // indirect github.com/jcmturner/gokrb5/v8 v8.4.4 // indirect github.com/jcmturner/rpc/v2 v2.0.3 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect github.com/kr/text v0.2.0 // indirect github.com/minio/md5-simd v1.1.2 // indirect github.com/minio/sha256-simd v1.0.1 // indirect diff --git a/go.sum b/go.sum index 1266899..3aecb3d 100644 --- a/go.sum +++ b/go.sum @@ -85,6 +85,10 @@ github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh6 github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs= github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY= github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -251,4 +255,6 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/gorm v1.25.8 h1:WAGEZ/aEcznN4D03laj8DKnehe1e9gYQAjW8xyPRdeo= +gorm.io/gorm v1.25.8/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/log/sql_logger.go b/log/sql_logger.go new file mode 100644 index 0000000..ae6cafb --- /dev/null +++ b/log/sql_logger.go @@ -0,0 +1,89 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package log + +import ( + "context" + "fmt" + "time" + + "github.com/pkg/errors" + "gorm.io/gorm" + gormLogger "gorm.io/gorm/logger" + gormUtils "gorm.io/gorm/utils" +) + +type SqlLogger struct { + LogLevel gormLogger.LogLevel + IgnoreRecordNotFoundError bool + SlowThreshold time.Duration +} + +func NewSqlLogger(logLevel gormLogger.LogLevel, ignoreRecordNotFoundError bool, slowThreshold time.Duration) *SqlLogger { + return &SqlLogger{ + LogLevel: logLevel, + IgnoreRecordNotFoundError: ignoreRecordNotFoundError, + SlowThreshold: slowThreshold, + } +} + +func (l *SqlLogger) LogMode(logLevel gormLogger.LogLevel) gormLogger.Interface { + newLogger := *l + newLogger.LogLevel = logLevel + return &newLogger +} + +func (SqlLogger) Info(ctx context.Context, msg string, args ...interface{}) { + ZInfo(ctx, msg, args) +} + +func (SqlLogger) Warn(ctx context.Context, msg string, args ...interface{}) { + ZWarn(ctx, msg, nil, args) +} + +func (SqlLogger) Error(ctx context.Context, msg string, args ...interface{}) { + ZError(ctx, msg, nil, args) +} + +func (l *SqlLogger) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) { + if l.LogLevel <= gormLogger.Silent { + return + } + elapsed := time.Since(begin) + switch { + case err != nil && l.LogLevel >= gormLogger.Error && (!errors.Is(err, gorm.ErrRecordNotFound) || !l.IgnoreRecordNotFoundError): + sql, rows := fc() + if rows == -1 { + ZError(ctx, "sql exec detail", err, "gorm", gormUtils.FileWithLineNum(), "elapsed time", fmt.Sprintf("%f(ms)", float64(elapsed.Nanoseconds())/1e6), "sql", sql) + } else { + ZError(ctx, "sql exec detail", err, "gorm", gormUtils.FileWithLineNum(), "elapsed time", fmt.Sprintf("%f(ms)", float64(elapsed.Nanoseconds())/1e6), "rows", rows, "sql", sql) + } + case elapsed > l.SlowThreshold && l.SlowThreshold != 0 && l.LogLevel >= gormLogger.Warn: + sql, rows := fc() + slowLog := fmt.Sprintf("SLOW SQL >= %v", l.SlowThreshold) + if rows == -1 { + ZWarn(ctx, "sql exec detail", nil, "gorm", gormUtils.FileWithLineNum(), "slow sql", slowLog, "elapsed time", fmt.Sprintf("%f(ms)", float64(elapsed.Nanoseconds())/1e6), "sql", sql) + } else { + ZWarn(ctx, "sql exec detail", nil, "gorm", gormUtils.FileWithLineNum(), "slow sql", slowLog, "elapsed time", fmt.Sprintf("%f(ms)", float64(elapsed.Nanoseconds())/1e6), "rows", rows, "sql", sql) + } + case l.LogLevel == gormLogger.Info: + sql, rows := fc() + if rows == -1 { + ZDebug(ctx, "sql exec detail", "gorm", gormUtils.FileWithLineNum(), "elapsed time", fmt.Sprintf("%f(ms)", float64(elapsed.Nanoseconds())/1e6), "sql", sql) + } else { + ZDebug(ctx, "sql exec detail", "gorm", gormUtils.FileWithLineNum(), "elapsed time", fmt.Sprintf("%f(ms)", float64(elapsed.Nanoseconds())/1e6), "rows", rows, "sql", sql) + } + } +} diff --git a/log/zk_logger.go b/log/zk_logger.go new file mode 100644 index 0000000..3579e20 --- /dev/null +++ b/log/zk_logger.go @@ -0,0 +1,30 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package log + +import ( + "context" + "fmt" +) + +type ZkLogger struct{} + +func NewZkLogger() *ZkLogger { + return &ZkLogger{} +} + +func (l *ZkLogger) Printf(format string, a ...interface{}) { + ZInfo(context.Background(), "zookeeper output", "msg", fmt.Sprintf(format, a...)) +} diff --git a/utils/strings.go b/utils/strings.go new file mode 100644 index 0000000..eba94c1 --- /dev/null +++ b/utils/strings.go @@ -0,0 +1,127 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package utils + +import ( + "encoding/json" + "strconv" +) + +func IntToString(i int) string { + return strconv.FormatInt(int64(i), 10) +} + +func StringToInt(i string) int { + j, _ := strconv.Atoi(i) + return j +} +func StringToInt64(i string) int64 { + j, _ := strconv.ParseInt(i, 10, 64) + return j +} +func StringToInt32(i string) int32 { + j, _ := strconv.ParseInt(i, 10, 64) + return int32(j) +} +func Int32ToString(i int32) string { + return strconv.FormatInt(int64(i), 10) +} + +func Uint32ToString(i uint32) string { + return strconv.FormatInt(int64(i), 10) +} + +// judge a string whether in the string list +func IsContain(target string, List []string) bool { + for _, element := range List { + + if target == element { + return true + } + } + return false +} +func IsContainInt32(target int32, List []int32) bool { + for _, element := range List { + if target == element { + return true + } + } + return false +} +func IsContainInt(target int, List []int) bool { + for _, element := range List { + if target == element { + return true + } + } + return false +} +func InterfaceArrayToStringArray(data []interface{}) (i []string) { + for _, param := range data { + i = append(i, param.(string)) + } + return i +} + +func StructToJsonBytes(param interface{}) []byte { + dataType, _ := json.Marshal(param) + return dataType +} + +// The incoming parameter must be a pointer +func JsonStringToStruct(s string, args interface{}) error { + err := json.Unmarshal([]byte(s), args) + return err +} + +func Int64ToString(i int64) string { + return strconv.FormatInt(i, 10) +} + +func RemoveDuplicateElement(idList []string) []string { + result := make([]string, 0, len(idList)) + temp := map[string]struct{}{} + for _, item := range idList { + if _, ok := temp[item]; !ok { + temp[item] = struct{}{} + result = append(result, item) + } + } + return result +} + +func RemoveDuplicate[T comparable](arr []T) []T { + result := make([]T, 0, len(arr)) + temp := map[T]struct{}{} + for _, item := range arr { + if _, ok := temp[item]; !ok { + temp[item] = struct{}{} + result = append(result, item) + } + } + return result +} + +func IsDuplicateStringSlice(arr []string) bool { + t := make(map[string]struct{}) + for _, s := range arr { + if _, ok := t[s]; ok { + return true + } + t[s] = struct{}{} + } + return false +}