diff --git a/go.mod b/go.mod index ef9113ab..ba9efe29 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,8 @@ go 1.23.0 toolchain go1.23.11 require ( - github.com/karincake/apem v0.0.16-e + github.com/karincake/apem v0.0.16-g + github.com/karincake/dodol v0.0.1 github.com/karincake/lepet v0.0.1 gorm.io/gorm v1.25.10 ) diff --git a/go.sum b/go.sum index 5ff115b0..cd5f268c 100644 --- a/go.sum +++ b/go.sum @@ -20,8 +20,10 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD 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/karincake/apem v0.0.16-e h1:KGeQYmgNw8luidSWkZyGcMbW1PP3KMRCHMTEHQ7twU8= -github.com/karincake/apem v0.0.16-e/go.mod h1:cQP2sJfDrLRIiwWoaLWw/z8uAya+DWu/FpmYeinMQXM= +github.com/karincake/apem v0.0.16-g h1:jPIr/YiaJhVSftdA1PyB2tlDiQtFeTVZohO1qf0qpw0= +github.com/karincake/apem v0.0.16-g/go.mod h1:cQP2sJfDrLRIiwWoaLWw/z8uAya+DWu/FpmYeinMQXM= +github.com/karincake/dodol v0.0.1 h1:jUXmJh1r0Ei4fmHPZ6IUkoplW/V9d27L63JEl6zudL0= +github.com/karincake/dodol v0.0.1/go.mod h1:2f1NcvkvY0J3GMUkwILNDYVvRUpz0W3lpPp/Ha/Ld24= github.com/karincake/lepet v0.0.1 h1:eq/cwn5BBg0jWZ1c/MmvhFIBma0zBpVs2LwkfDOncy4= github.com/karincake/lepet v0.0.1/go.mod h1:U84w7olXO3BPJw2Hu6MBonFmJmPKaFjtyAj1HTu3z1A= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= diff --git a/internal/use-case/_use-case-template/crud/case.go b/internal/use-case/_use-case-template/crud/case.go index 6312c950..75adb080 100644 --- a/internal/use-case/_use-case-template/crud/case.go +++ b/internal/use-case/_use-case-template/crud/case.go @@ -5,11 +5,14 @@ Any functions that are available to be used externally. package crud import ( + "fmt" "strconv" dg "github.com/karincake/apem/db-gorm-pg" - lo "github.com/karincake/apem/logero" d "github.com/karincake/dodol" + + pl "simrs-vx/pkg/logger" + "gorm.io/gorm" e "simrs-vx/internal/domain/_template/single" @@ -21,24 +24,74 @@ func Create(input e.Createdto) (*d.Data, error) { data := e.Single{} setData(&data, &input) + + event := pl.Event{ + Feature: "Create", + Source: source, + } + + // Start log + event.Action = "Create" + event.Status = "started" + pl.SetLogInfo(event, input) err := dg.I.Transaction(func(tx *gorm.DB) error { - lo.I. for i := range createPreMw { + mwName := fmt.Sprintf("createPreMw[%d]", i) + + event.Action = mwName + event.Status = "started" + pl.SetLogInfo(event, data) + if err := createPreMw[i](&input, &data, tx); err != nil { - return nil + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_PRE_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Pre-middleware %s failed", mwName), + Raw: mwErr, + } + return pl.SetLogError(event, data) } - } - slog. - if result, err := CreateData(&data, tx).Error; err != nil { - return nil + + event.Status = "completed" + pl.SetLogInfo(event, nil) } + event.Action = "DBCreate" + event.Status = "started" + pl.SetLogInfo(event, data) + if result, err := CreateData(&data, tx).Error; err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-create-fail", + Detail: "Database insert failed", + Raw: err, + } + return pl.SetLogError(event, data) + } + + event.Status = "completed" + pl.SetLogInfo(event, nil) + for i := range createPostMw { + mwName := fmt.Sprintf("createPostMw[%d]", i) + + event.Action = mwName + event.Status = "started" + pl.SetLogInfo(event, input) if err := createPostMw[i](&input, &data, tx); err != nil { - return nil + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "MW_POST_FAILED", // TODO: add to lang json + Detail: fmt.Sprintf("Post-middleware %s failed", mwName), + Raw: mwErr, + } + return pl.SetLogError(event, data) } } + event.Status = "completed" + pl.SetLogInfo(event, nil) + return nil }) diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go new file mode 100644 index 00000000..2a9047ca --- /dev/null +++ b/pkg/logger/logger.go @@ -0,0 +1,66 @@ +// Package logger provides helper functions for logging +package logger + +import ( + "encoding/json" + + lz "github.com/karincake/apem/logger-zerolog" + d "github.com/karincake/dodol" + l "github.com/karincake/lepet" +) + +type Event struct { + // Context about the operation + Feature string // Feature area, e.g. "Create" + Action string // Action being performed, e.g. "DBCreate" + Source string // Source of event, usually present in each use case + Status string // e.g. "started", "failed", "success" + + // Error context + ErrInfo ErrorInfo +} + +type ErrorInfo struct { + Code string // used in lang json + Detail string + Raw error +} + +func SetLogInfo(e Event, data any) { + dataString, _ := json.Marshal(data) + lz.O.Info(). + String("source", e.Source). + String("feature", e.Feature). + String("action", e.Action). + String("status", "started"). + String("input", string(dataString)). + Send() +} + +func SetLogError(e Event, data any) error { + dataString, _ := json.Marshal(data) + msg := l.I.Msg(e.ErrInfo.Code) + + lz.O.Error(). + String("message", msg). + String("source", e.Source). + String("feature", e.Feature). + String("action", e.Action). + String("status", e.Status). + String("error_code", e.ErrInfo.Code). + String("error_detail", e.ErrInfo.Detail). + String("data", string(dataString)). + Send() + + if err, ok := e.ErrInfo.Raw.(d.FieldError); ok { + return err + } + if err, ok := e.ErrInfo.Raw.(d.FieldErrors); ok { + return err + } + + if e.ErrInfo.Detail != "" { + return d.FieldError{Code: e.ErrInfo.Code, Message: e.ErrInfo.Detail} + } + return d.FieldError{Code: e.ErrInfo.Code, Message: msg} +}