登录相关处理

This commit is contained in:
474420502 2024-04-09 18:17:08 +08:00
parent 578d7ecdf9
commit 74b790b542
17 changed files with 544 additions and 24 deletions

1
go.mod
View File

@ -8,6 +8,7 @@ require (
github.com/go-sql-driver/mysql v1.8.1
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/jmoiron/sqlx v1.3.5
github.com/sirupsen/logrus v1.9.3
)
require (

3
go.sum
View File

@ -67,6 +67,8 @@ github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZ
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@ -90,6 +92,7 @@ golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=

5
readme.md Normal file
View File

@ -0,0 +1,5 @@
# 记录需要修改的问题
1. 邮件验证码传递密钥,验证码的传递

View File

@ -1,12 +1,16 @@
package actions
import (
"log"
"github.com/gin-gonic/gin"
"github.com/iapologizewhenimwrong/Vestmore_GO/utils/auth"
"github.com/iapologizewhenimwrong/Vestmore_GO/utils/basic"
"github.com/iapologizewhenimwrong/Vestmore_GO/utils/email"
"github.com/iapologizewhenimwrong/Vestmore_GO/utils/encryption_decryption"
"github.com/iapologizewhenimwrong/Vestmore_GO/utils/log"
)
var CompanyKey = "vestmore-bjwl"
// @Action base/getToken
// Base_GetToken
// action: string;
@ -58,6 +62,12 @@ func AccountForgetSmsCode(ctx *gin.Context, param *AccountForgetSmsCodeParam, re
log.Println()
}
type RegisterValidEmailCode struct {
Code string
Email string
AppMarket int
}
// @Action account/registerEmailCode
// AccountRegisterEmailCode
// randstr: string;
@ -68,7 +78,30 @@ func AccountForgetSmsCode(ctx *gin.Context, param *AccountForgetSmsCodeParam, re
// timestamp: int64;
// token: string;
func AccountRegisterEmailCode(ctx *gin.Context, param *AccountRegisterEmailCodeParam, resp *basic.Response) {
log.Println(param)
if !email.IsEmailValid(param.Email) {
resp.Error(basic.ErrEmailFormat)
return
}
gcm := encryption_decryption.NewSecretGCM[RegisterValidEmailCode](CompanyKey)
code := auth.GenerateVerificationCode()
codetoken := &RegisterValidEmailCode{
Code: code,
Email: param.Email,
AppMarket: param.AppMarket,
}
tokenstr, err := gcm.Encrypt(codetoken)
if err != nil {
resp.Error(basic.ErrEncGcm)
return
}
resp.Success(map[string]any{
"token": tokenstr,
})
}
// @Action member/alterPassword

View File

@ -1,28 +1,27 @@
package actions
import (
"log"
"github.com/gin-gonic/gin"
"github.com/iapologizewhenimwrong/Vestmore_GO/utils/basic"
"github.com/iapologizewhenimwrong/Vestmore_GO/utils/log"
)
var HandlersFuncRoutes map[string]gin.HandlerFunc = make(map[string]gin.HandlerFunc)
func init() {
// func AccountForgetSmsCode(ctx gin.Context, param AccountForgetSmsCodeParam, resp *basic.Response)
// func AccountForgetSmsCode(ctx *gin.Context, param *AccountForgetSmsCodeParam, resp *basic.Response)
HandlersFuncRoutes["account/forgetSmsCode"] = AccountForgetSmsCodeHandler
// func AccountLoginWithEmailPassword(ctx gin.Context, param AccountLoginWithEmailPasswordParam, resp *basic.Response)
// func AccountLoginWithEmailPassword(ctx *gin.Context, param *AccountLoginWithEmailPasswordParam, resp *basic.Response)
HandlersFuncRoutes["account/loginWithEmailPassword"] = AccountLoginWithEmailPasswordHandler
// func AccountLoginWithTelephonePassword(ctx gin.Context, param AccountLoginWithTelephonePasswordParam, resp *basic.Response)
// func AccountLoginWithTelephonePassword(ctx *gin.Context, param *AccountLoginWithTelephonePasswordParam, resp *basic.Response)
HandlersFuncRoutes["account/loginWithTelephonePassword"] = AccountLoginWithTelephonePasswordHandler
// func AccountRegisterEmailCode(ctx gin.Context, param AccountRegisterEmailCodeParam, resp *basic.Response)
// func AccountRegisterEmailCode(ctx *gin.Context, param *AccountRegisterEmailCodeParam, resp *basic.Response)
HandlersFuncRoutes["account/registerEmailCode"] = AccountRegisterEmailCodeHandler
// func AccountRegisterSmsCode(ctx gin.Context, param AccountRegisterSmsCodeParam, resp *basic.Response)
// func AccountRegisterSmsCode(ctx *gin.Context, param *AccountRegisterSmsCodeParam, resp *basic.Response)
HandlersFuncRoutes["account/registerSmsCode"] = AccountRegisterSmsCodeHandler
// func BaseGetToken(ctx gin.Context, param BaseGetTokenParam, resp *basic.Response)
// func BaseGetToken(ctx *gin.Context, param *BaseGetTokenParam, resp *basic.Response)
HandlersFuncRoutes["base/getToken"] = BaseGetTokenHandler
// func MemberAlterPassword(ctx gin.Context, param MemberAlterPasswordParam, resp *basic.Response)
// func MemberAlterPassword(ctx *gin.Context, param *MemberAlterPasswordParam, resp *basic.Response)
HandlersFuncRoutes["member/alterPassword"] = MemberAlterPasswordHandler
}
@ -34,7 +33,7 @@ type AccountForgetSmsCodeParam struct {
}
func AccountForgetSmsCodeHandler(ctx *gin.Context) {
resp := &basic.Response{}
resp := &basic.Response{IsSuccess: true}
defer ctx.JSON(200, resp)
param := &AccountForgetSmsCodeParam{}
@ -62,7 +61,7 @@ type AccountLoginWithEmailPasswordParam struct {
}
func AccountLoginWithEmailPasswordHandler(ctx *gin.Context) {
resp := &basic.Response{}
resp := &basic.Response{IsSuccess: true}
defer ctx.JSON(200, resp)
param := &AccountLoginWithEmailPasswordParam{}
@ -88,7 +87,7 @@ type AccountLoginWithTelephonePasswordParam struct {
}
func AccountLoginWithTelephonePasswordHandler(ctx *gin.Context) {
resp := &basic.Response{}
resp := &basic.Response{IsSuccess: true}
defer ctx.JSON(200, resp)
param := &AccountLoginWithTelephonePasswordParam{}
@ -113,7 +112,7 @@ type AccountRegisterEmailCodeParam struct {
}
func AccountRegisterEmailCodeHandler(ctx *gin.Context) {
resp := &basic.Response{}
resp := &basic.Response{IsSuccess: true}
defer ctx.JSON(200, resp)
param := &AccountRegisterEmailCodeParam{}
@ -135,7 +134,7 @@ type AccountRegisterSmsCodeParam struct {
}
func AccountRegisterSmsCodeHandler(ctx *gin.Context) {
resp := &basic.Response{}
resp := &basic.Response{IsSuccess: true}
defer ctx.JSON(200, resp)
param := &AccountRegisterSmsCodeParam{}
@ -157,7 +156,7 @@ type BaseGetTokenParam struct {
}
func BaseGetTokenHandler(ctx *gin.Context) {
resp := &basic.Response{}
resp := &basic.Response{IsSuccess: true}
defer ctx.JSON(200, resp)
param := &BaseGetTokenParam{}
@ -180,7 +179,7 @@ type MemberAlterPasswordParam struct {
}
func MemberAlterPasswordHandler(ctx *gin.Context) {
resp := &basic.Response{}
resp := &basic.Response{IsSuccess: true}
defer ctx.JSON(200, resp)
param := &MemberAlterPasswordParam{}

View File

@ -1,2 +0,0 @@
package handlers

View File

@ -3,6 +3,7 @@ package actions
import (
"github.com/gin-gonic/gin"
"github.com/iapologizewhenimwrong/Vestmore_GO/utils/basic"
"github.com/iapologizewhenimwrong/Vestmore_GO/utils/log"
)
var HandlersFuncRoutes map[string]gin.HandlerFunc = make(map[string]gin.HandlerFunc)
@ -22,7 +23,7 @@ type {{.ParamStruct.ParamStructName}} struct {
}
func {{.FuncName}}Handler(ctx *gin.Context) {
resp := &basic.Response{}
resp := &basic.Response{IsSuccess: true}
defer ctx.JSON(200, resp)
param := &{{.ParamStruct.ParamStructName}}{}

21
utils/auth/valid_code.go Normal file
View File

@ -0,0 +1,21 @@
package auth
import (
"fmt"
"math/rand"
)
func GenerateVerificationCode() string {
var code string
for i := 0; i < 3; i++ {
// 生成两个 10-99 之间的随机数
a := rand.Intn(90-10+1) + 10
b := rand.Intn(90-10+1) + 10
c := rand.Intn(90-10+1) + 10
// 将随机数拼接到验证码字符串
code += fmt.Sprintf("%d%d%d", a, b, c)
}
return code
}

View File

@ -6,5 +6,9 @@ type ErrorCode struct {
}
var (
ErrEncGcm = &ErrorCode{Code: 10001, Message: "gmc加密错误"}
ErrParamParse = &ErrorCode{Code: 10100, Message: "参数解析错误"}
ErrEmailFormat = &ErrorCode{Code: 10101, Message: "email 格式错误"}
)

View File

@ -34,7 +34,7 @@ func (resp *Response) setData(Data []interface{}) {
}
if len(Data) == 1 {
resp.Data = Data
resp.Data = Data[0]
} else {
resp.Data = Data
}

View File

@ -11,7 +11,7 @@ func SetCors(router *gin.Engine) {
router.OPTIONS("/*path", func(c *gin.Context) {
c.Header("Access-Control-Allow-Origin", "*") // 可以根据实际情况改为特定域名
c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization")
c.Header("Access-Control-Allow-Headers", "*")
c.Header("Access-Control-Allow-Credentials", "true")
c.Status(http.StatusOK)
})
@ -20,7 +20,7 @@ func SetCors(router *gin.Engine) {
router.Use(func(c *gin.Context) {
c.Header("Access-Control-Allow-Origin", "*") // 允许所有域名跨域访问,也可以指定特定的域名
c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization")
c.Header("Access-Control-Allow-Headers", "*")
c.Header("Access-Control-Allow-Credentials", "true")
c.Next()
})

15
utils/email/verify.go Normal file
View File

@ -0,0 +1,15 @@
package email
import "regexp"
// 验证是否邮箱
func IsEmailValid(email string) bool {
// 邮箱正则表达式
regex := `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`
// 编译正则表达式
regexPattern := regexp.MustCompile(regex)
// 根据正则表达式验证邮箱
return regexPattern.MatchString(email)
}

85
utils/log/json_format.go Normal file
View File

@ -0,0 +1,85 @@
package log
import (
"bytes"
"encoding/json"
"fmt"
"runtime"
"strings"
"sync"
"time"
"github.com/sirupsen/logrus"
)
type levelSkip struct {
Skip int
Once sync.Once
}
// JSONFormatter formats logs into parsable json
type JSONFormatter struct {
skip []*levelSkip
once sync.Once
}
// Format renders a single log entry
func (h *JSONFormatter) Format(e *logrus.Entry) ([]byte, error) {
skipOnce := h.skip[int(e.Level)]
skipOnce.Once.Do(func() {
for i := 4; i < 100; i++ {
// log.Println(i)
if pc, _, _, ok := runtime.Caller(i); ok {
funcStruct := runtime.FuncForPC(pc)
// log.Println(funcStruct.Name(), file, line)
if !strings.Contains(funcStruct.Name(), "github.com/sirupsen/logrus.") {
skipOnce.Skip++
if skipOnce.Skip >= 2 {
skipOnce.Skip = i - 3
break
}
}
} else {
break
}
}
})
var fileinfo string
if _, file, line, ok := runtime.Caller(skipOnce.Skip); ok {
if e.Level == logrus.InfoLevel {
fileinfo = fmt.Sprintf("%s:%d", file, line)
} else {
ps := strings.Split(file, "/")
ps = ps[len(ps)-4:]
fileinfo = fmt.Sprintf("%s:%d", strings.Join(ps, "/"), line)
}
}
var Data map[string]any = make(map[string]any, 4)
Data[logrus.FieldKeyTime] = e.Time.Format(time.RFC3339)
Data[logrus.FieldKeyMsg] = e.Message
Data[logrus.FieldKeyLevel] = e.Level
Data[logrus.FieldKeyFile] = fileinfo
var b *bytes.Buffer
if e.Buffer != nil {
b = e.Buffer
} else {
b = &bytes.Buffer{}
}
encoder := json.NewEncoder(b)
encoder.SetEscapeHTML(false)
if err := encoder.Encode(Data); err != nil {
return nil, fmt.Errorf("failed to marshal fields to JSON, %w", err)
}
return b.Bytes(), nil
}

252
utils/log/log.go Normal file
View File

@ -0,0 +1,252 @@
package log
import (
"context"
"fmt"
"log"
"runtime"
"strings"
"sync"
"time"
"github.com/sirupsen/logrus"
)
var l *logrus.Logger
func init() {
l = logrus.New()
// 配置 Logstash 作为输出
l.AddHook(NewUTCTimeHook())
jf := &JSONFormatter{
skip: make([]*levelSkip, len(logrus.AllLevels)),
}
for i := range jf.skip {
jf.skip[i] = &levelSkip{}
}
l.Formatter = jf
// l.AddHook(&SkipHook{})
l.SetReportCaller(true)
}
type SkipHook struct {
autoSkip int
Formatter func(*logrus.Hook, *logrus.Entry) error
once sync.Once
}
func (h *SkipHook) Levels() []logrus.Level {
return logrus.AllLevels
}
func (h *SkipHook) Fire(e *logrus.Entry) error {
h.once.Do(func() {
for i := 4; i < 100; i++ {
log.Println(i)
if pc, file, line, ok := runtime.Caller(i); ok {
funcStruct := runtime.FuncForPC(pc)
log.Println(funcStruct.Name(), file, line)
if !strings.Contains(funcStruct.Name(), "github.com/sirupsen/logrus.") {
h.autoSkip++
if h.autoSkip >= 2 {
h.autoSkip = i - 3
break
}
}
} else {
break
}
}
})
if _, file, line, ok := runtime.Caller(h.autoSkip); ok {
// funcStruct := runtime.FuncForPC(pc)
// log.Println(file, line, funcStruct.Name())
// funcName := funcStruct.Name()
file := fmt.Sprintf("%s:%d", file, line)
e.Data["file"] = file
}
return nil
}
// 自定义钩子以设置时间为UTC
type UTCTimeHook struct{}
func NewUTCTimeHook() *UTCTimeHook {
return &UTCTimeHook{}
}
func (hook *UTCTimeHook) Levels() []logrus.Level {
return logrus.AllLevels
}
func (hook *UTCTimeHook) Fire(entry *logrus.Entry) error {
entry.Time = time.Now().UTC()
return nil
}
// WithField allocates a new entry and adds a field to it.
// Debug, Print, Info, Warn, Error, Fatal or Panic must be then applied to
// this new returned entry.
// If you want multiple fields, use `WithFields`.
func WithField(key string, value interface{}) *logrus.Entry {
return l.WithField(key, value)
}
// Adds a struct of fields to the log entry. All it does is call `WithField` for
// each `Field`.
func WithFields(fields logrus.Fields) *logrus.Entry {
return l.WithFields(fields)
}
// Add an error as single field to the log entry. All it does is call
// `WithError` for the given `error`.
func WithError(err error) *logrus.Entry {
return l.WithError(err)
}
// Add a context to the log entry.
func WithContext(ctx context.Context) *logrus.Entry {
return l.WithContext(ctx)
}
// Overrides the time of the log entry.
func WithTime(t time.Time) *logrus.Entry {
return l.WithTime(t)
}
func Logf(level logrus.Level, format string, args ...interface{}) {
l.Logf(level, format, args...)
}
func Tracef(format string, args ...interface{}) {
l.Tracef(format, args...)
}
func Debugf(format string, args ...interface{}) {
l.Debugf(format, args...)
}
func Infof(format string, args ...interface{}) {
l.Infof(format, args...)
}
func Printf(format string, args ...interface{}) {
l.Printf(format, args...)
}
func Warnf(format string, args ...interface{}) {
l.Warnf(format, args...)
}
func Warningf(format string, args ...interface{}) {
l.Warningf(format, args...)
}
func Errorf(format string, args ...interface{}) {
l.Errorf(format, args...)
}
func Fatalf(format string, args ...interface{}) {
l.Fatalf(format, args...)
}
func Panicf(format string, args ...interface{}) {
l.Panicf(format, args...)
}
func Log(level logrus.Level, args ...interface{}) {
l.Log(level, args...)
}
func Trace(args ...interface{}) {
l.Trace(args...)
}
func Debug(args ...interface{}) {
l.Debug(args...)
}
func Info(args ...interface{}) {
l.Info(args...)
}
func Print(args ...interface{}) {
l.Print(args...)
}
func Warn(args ...interface{}) {
l.Warn(args...)
}
func Warning(args ...interface{}) {
l.Warning(args...)
}
func Error(args ...interface{}) {
l.Error(args...)
}
func Fatal(args ...interface{}) {
l.Fatal(args...)
}
func Panic(args ...interface{}) {
l.Panic(args...)
}
func Logln(level logrus.Level, args ...interface{}) {
l.Logln(level, args...)
}
func Traceln(args ...interface{}) {
l.Traceln(args...)
}
func Debugln(args ...interface{}) {
l.Debugln(args...)
}
func Infoln(args ...interface{}) {
l.Infoln(args...)
}
func Println(args ...interface{}) {
l.Println(args...)
}
func Warnln(args ...interface{}) {
l.Warnln(args...)
}
func Warningln(args ...interface{}) {
l.Warningln(args...)
}
func Errorln(args ...interface{}) {
l.Errorln(args...)
}
func Fatalln(args ...interface{}) {
l.Fatalln(args...)
}
func Panicln(args ...interface{}) {
l.Panicln(args...)
}
func Exit(code int) {
l.Exit(code)
}

28
utils/log/log_time.go Normal file
View File

@ -0,0 +1,28 @@
package log
import (
"fmt"
"time"
)
// 跟踪时间
func DebuglnTrackTime(do func(), fargs ...interface{}) {
now := time.Now()
do()
var t interface{} = fmt.Sprintf("[%dms]", time.Since(now).Milliseconds())
var out []interface{}
out = append(out, t)
out = append(out, fargs...)
l.Debugln(out...)
}
func InfolnTrackTime(do func(), fargs ...interface{}) {
now := time.Now()
do()
var t interface{} = fmt.Sprintf("[%dms]", time.Since(now).Milliseconds())
var out []interface{}
out = append(out, t)
out = append(out, fargs...)
l.Infoln(out...)
}

74
utils/log/readme.md Normal file
View File

@ -0,0 +1,74 @@
# Log Package README
## 概述
此`log`包提供了一套全面的日志记录功能,基于`github.com/sirupsen/logrus`库进行了深度定制和扩展。通过本包,您可以实现多级别的日志输出,并能灵活控制日志格式、时间戳显示以及调用栈信息等。
用于fusen的全部服务的统一格式化, 便于运维统一处理。
### 功能特性
1. **多级别日志**:支持`Trace`、`Debug`、`Info`、`Warn`、`Error`、`Fatal`和`Panic`等多种日志级别。
2. **追踪时间**:包含`DebuglnTrackTime`与`InfolnTrackTime`函数,用于执行指定函数并在输出时附加执行耗时(毫秒)。
3. **UTC时间**:使用了自定义的`UTCTimeHook`钩子确保所有日志的时间戳采用协调世界时UTC格式。
4. **JSON格式化**:实现了`JSONFormatter`结构体将日志条目格式化为可解析的JSON字符串。它自动跳过与logrus库自身相关的调用层级以便更准确地显示应用代码中的文件和行号信息。
5. **上下文关联**:提供了`WithContext`方法允许在日志条目中携带Go context.Context便于进行请求级或事务级的跟踪。
6. **错误处理**:通过`WithError`方法可以在日志条目中便捷地添加错误对象。
7. **时间戳覆盖**`WithTime`方法允许开发者为日志条目设置特定的时间戳。
8. **灵活格式化**:除了基本的日志输出外,还支持带有格式字符串的函数如`Printf`、`Debugf`等,以适应不同的消息格式需求。
9. **源码定位**:已启用报告调用者位置的功能,每条日志都会附带其产生的源代码文件名及行号。
### 使用示例
```go
package main
import (
"github.com/yourorg/log" // 替换为实际导入路径
"time"
)
func main() {
log.Debugln("This is a debug message.")
log.Infoln("Normal info message.")
// 添加自定义字段并格式化输出
entry := log.WithField("user_id", "123")
entry.Info("User logged in.")
// 记录并追踪函数执行时间
log.DebuglnTrackTime(func() {
time.Sleep(200 * time.Millisecond)
}, "Function execution")
// 设置日志上下文
ctx := context.Background()
ctx = log.WithContext(ctx, "request_id", "abc123")
log.Infof(ctx, "Received request with ID: %s", "abc123")
// 输出JSON格式化的日志
log.SetFormatter(&log.JSONFormatter{})
log.Warn("JSON-formatted warning.")
}
```
### 配置选项
- 在初始化阶段已经配置了Logstash作为日志输出的目标。
- 可通过修改`l.Formatter`属性来更换或自定义日志格式器。
- 通过`AddHook`方法可以添加自定义的钩子,比如这里有一个未使用的`SkipHook`,它可以用来过滤调用栈。
### 注意事项
- 当需要同时记录多个字段时,请使用`WithFields`方法而不是多次调用`WithField`。
- 若要调整日志输出级别请直接操作全局logger实例的level设置。
### 结构体与方法详解
- `levelSkip` 结构体用于在堆栈跟踪过程中计算需要跳过的层级数。
- `UTCTimeHook` 是一个自定义钩子当触发时会将日志条目的时间戳设置为当前UTC时间。
- `JSONFormatter` 实现了`Format`方法将日志条目转化为JSON格式。

1
utils/log/tx/log_test.go Normal file
View File

@ -0,0 +1 @@
package log_test