diff --git a/cmd/test/main.go b/cmd/test/main.go index 674cc2f..758eed3 100644 --- a/cmd/test/main.go +++ b/cmd/test/main.go @@ -2,6 +2,7 @@ package main import ( "kumoly.io/kumoly/app/auth" + "kumoly.io/kumoly/app/history" "kumoly.io/kumoly/app/server" "kumoly.io/kumoly/app/store" "kumoly.io/kumoly/app/system" @@ -17,7 +18,7 @@ func main() { auth.SetDB(store.DB) sys.Inject(auth.Injector(server.API)) - sys.Append(server, auth.New(server), &task.Service{}) + sys.Append(server, auth.New(server), &task.Service{}, &history.Service{}) sys.Start() } diff --git a/history/helper.go b/history/helper.go new file mode 100644 index 0000000..f1bf463 --- /dev/null +++ b/history/helper.go @@ -0,0 +1,27 @@ +package history + +import "kumoly.io/kumoly/app/util" + +func Error() *History { + h := getBase() + h.Type = ERROR + if !PROD { + h.Trace = util.Stack() + } + return h +} + +func Info() *History { + h := getBase() + h.Type = INFO + return h +} + +func getBase() *History { + mod, file := util.CallerMod(3) + h := &History{ + Module: mod, + Caller: file, + } + return h +} diff --git a/history/history.go b/history/history.go index 0819c2e..0e06433 100644 --- a/history/history.go +++ b/history/history.go @@ -1,12 +1,18 @@ package history import ( + "encoding/json" "fmt" "time" + "github.com/rs/zerolog/log" + "gorm.io/gorm" "kumoly.io/kumoly/app/errors" + "kumoly.io/kumoly/app/store" ) +var PROD = false + const ( ERROR = "ERROR" INFO = "INFO" @@ -19,11 +25,22 @@ type History struct { Module string Type string - Message string - Body string - Issuer string - Caller string - Trace string + Message string + BodyJson string + Body interface{} `gorm:"-"` + Issuer string + Caller string + Trace string +} + +func (h *History) BeforeCreate(tx *gorm.DB) (err error) { + if h.Body != nil { + body, err := json.Marshal(h.Body) + if err != nil { + h.BodyJson = string(body) + } + } + return } var Tunnel chan *History @@ -40,12 +57,15 @@ func Start(r Receiver) { select { case started <- struct{}{}: default: - panic(errors.New(500, "history has already started!")) + panic(errors.New(500, "history reporter has already started!")) } go func() { for { select { case h := <-Tunnel: + if Interceptor != nil { + go Interceptor(h) + } r(h) case <-quit: <-started @@ -68,8 +88,13 @@ func Send(h *History) { type Receiver func(*History) -var DBReceiver Receiver = func(h *History) { +var Interceptor func(*History) = nil +var DBReceiver Receiver = func(h *History) { + err := store.DB.Create(h).Error + if err != nil { + log.Error().Str("mod", "history").Err(err).Msg("DBReceiver error") + } } var ConsoleReceiver Receiver = func(h *History) { diff --git a/history/service.go b/history/service.go index 143e6a8..e561d71 100644 --- a/history/service.go +++ b/history/service.go @@ -1,6 +1,11 @@ package history -import "kumoly.io/kumoly/app/system" +import ( + "github.com/rs/zerolog/log" + "github.com/spf13/viper" + "kumoly.io/kumoly/app/store" + "kumoly.io/kumoly/app/system" +) type Service struct { system.BaseService @@ -8,6 +13,18 @@ type Service struct { func (srv Service) GetName() string { return "history.Service" } func (srv Service) IsService() bool { return true } +func (srv Service) Init() error { + PROD = viper.GetBool("prod") + l := log.With().Str("mod", "history"). + Str("service", "history.Service"). + Logger() + l.Debug().Msg("Migrating database for history.Service ...") + if err := store.Migrate(&History{}); err != nil { + l.Error().Err(err).Msg("Migrating database") + return err + } + return nil +} func (srv Service) Main() error { Start(DBReceiver) return nil diff --git a/util/logging.go b/util/logging.go index 80e92ea..2111582 100644 --- a/util/logging.go +++ b/util/logging.go @@ -7,11 +7,39 @@ import ( "runtime" ) +func Stack() string { + buf := make([]byte, 1024) + for { + n := runtime.Stack(buf, false) + if n < len(buf) { + return string(buf[:n]) + } + buf = make([]byte, 2*len(buf)) + } +} + func CallerFull(depth int) string { _, file, line, _ := runtime.Caller(depth) return fmt.Sprintf("%s:%d", file, line) } +func CallerMod(depth int) (mod, file string) { + _, f, l, _ := runtime.Caller(depth) + ptr := 0 + for i := len(f) - 1; i > 0; i-- { + if f[i] == '/' { + if ptr == 0 { + file = fmt.Sprintf("%v:%v", f[i+1:], l) + ptr = i + } else { + mod = f[i+1 : ptr] + break + } + } + } + return +} + func Caller(depth int) string { _, file, line, _ := runtime.Caller(depth) short := file diff --git a/util/logging_test.go b/util/logging_test.go new file mode 100644 index 0000000..4e456d6 --- /dev/null +++ b/util/logging_test.go @@ -0,0 +1,22 @@ +package util + +import ( + "fmt" + "testing" +) + +func TestStack(t *testing.T) { + fmt.Println(Stack()) +} + +func TestCallerFull(t *testing.T) { + fmt.Println(CallerFull(1)) +} + +func TestCallerMod(t *testing.T) { + fmt.Println(CallerMod(1)) +} + +func TestCaller(t *testing.T) { + fmt.Println(Caller(1)) +}