package history import ( "encoding/json" "fmt" "sync" "time" "gorm.io/gorm" "kumoly.io/kumoly/app/errors" "kumoly.io/kumoly/app/store" ) const ( ERROR = "ERROR" INFO = "INFO" ) type History struct { ID uint `gorm:"primaryKey"` Module string Type string Caller string Trace string BodyJson string Name string Message string Body interface{} `gorm:"-"` Issuer string // Group to identify auth, empty for admin Group string // Scope for users to narrow down view point Scope string CreatedAt time.Time } func (h *History) BeforeCreate(tx *gorm.DB) (err error) { if h.Body != nil { if body, err := json.Marshal(h.Body); err != nil { l.Error().Str("mod", "history").Err(err).Msg("history create error") } else { h.BodyJson = string(body) } } return } var Tunnel chan *History var quit chan struct{} var started chan struct{} var wg sync.WaitGroup func init() { Tunnel = make(chan *History) quit = make(chan struct{}, 1) started = make(chan struct{}, 1) } func Start(r Receiver) { if len(started) == 1 { panic(errors.New(500, "history reporter has already started!")) } started <- struct{}{} go func() { for { select { case h := <-Tunnel: wg.Add(1) if Interceptor != nil { Interceptor(h) } r(h) wg.Done() case <-quit: <-started return } } }() } func Stop() { if len(started) == 0 { return } quit <- struct{}{} l.Debug().Str("mod", "history").Msg("stop received") wg.Wait() l.Debug().Str("mod", "history").Msg("stopped") } func Send(h *History) { if len(started) == 0 { l.Warn().Str("mod", "history"). Interface("history", h). Msg("history reporter has not started, report will be discarded") return } if h.Type == "" { h.Type = INFO } Tunnel <- h } type Receiver func(*History) var Interceptor func(*History) = nil var DBReceiver Receiver = func(h *History) { err := store.DB.Create(h).Error if err != nil { l.Error().Str("mod", "history"). Interface("history", h). Err(err).Msg("DBReceiver error") } } var ConsoleReceiver Receiver = func(h *History) { fmt.Printf("%+v\n", h) }