package log import ( "fmt" "os" "time" "github.com/jmoiron/sqlx" ) type ( Logger struct { DB *sqlx.DB } Log struct { ID string `json:"id" db:"id"` Level LogLevel `json:"level" db:"level"` Type string `json:"type" db:"type"` Content string `json:"content" db:"content"` CreatedAt time.Time `json:"created_at" db:"created_at"` } ) const ( TYPE_ACCOUNT string = "account" TYPE_MUSIC string = "music" TYPE_BLOG string = "blog" TYPE_ARTWORK string = "artwork" TYPE_MISC string = "misc" ) type LogLevel int const ( LEVEL_INFO LogLevel = 0 LEVEL_WARN LogLevel = 1 ) const DEFAULT_LOG_PAGE_LENGTH = 25 func (self *Logger) Info(logType string, format string, args ...any) { logString := fmt.Sprintf(format, args...) fmt.Printf("[%s] INFO: %s", logType, logString) err := createLog(self.DB, LEVEL_INFO, logType, logString) if err != nil { fmt.Fprintf(os.Stderr, "WARN: Failed to push log to database: %v\n", err) } } func (self *Logger) Warn(logType string, format string, args ...any) { logString := fmt.Sprintf(format, args...) fmt.Fprintf(os.Stderr, "[%s] WARN: %s", logType, logString) err := createLog(self.DB, LEVEL_WARN, logType, logString) if err != nil { fmt.Fprintf(os.Stderr, "WARN: Failed to push log to database: %v\n", err) } } func (self *Logger) Fatal(logType string, format string, args ...any) { fmt.Fprintf(os.Stderr, fmt.Sprintf("[%s] FATAL: %s", logType, format), args...) // we won't need to push fatal logs to DB, as these usually precede a panic or crash } func (self *Logger) Fetch(id string) (*Log, error) { log := Log{} err := self.DB.Get(&log, "SELECT * FROM auditlog WHERE id=$1", id) return &log, err } func (self *Logger) Search(levelFilters []LogLevel, typeFilters []string, content string, offset int, limit int) ([]*Log, error) { logs := []*Log{} params := []any{ limit, offset } conditions := "" if len(content) > 0 { content = "%" + content + "%" conditions += " WHERE content LIKE $3" params = append(params, content) } if len(levelFilters) > 0 { conditions += " AND level IN (" for i := range levelFilters { conditions += fmt.Sprintf("$%d", len(params) + 1) if i < len(levelFilters) - 1 { conditions += "," } params = append(params, levelFilters[i]) } conditions += ")" } if len(typeFilters) > 0 { conditions += " AND type IN (" for i := range typeFilters { conditions += fmt.Sprintf("$%d", len(params) + 1) if i < len(typeFilters) - 1 { conditions += "," } params = append(params, typeFilters[i]) } conditions += ")" } query := fmt.Sprintf( "SELECT * FROM auditlog%s ORDER BY created_at DESC LIMIT $1 OFFSET $2", conditions, ) // TODO: remove after testing fmt.Println(query) err := self.DB.Select(&logs, query, params...) if err != nil { return nil, err } return logs, nil } func createLog(db *sqlx.DB, logLevel LogLevel, logType string, content string) error { _, err := db.Exec( "INSERT INTO auditlog (level, type, content) VALUES ($1,$2,$3)", logLevel, logType, content, ) return err }