package  logger 
 
import  ( 
	"context"  
	"errors"  
	"fmt"  
	"io"  
	"log"  
	"os"  
	"time"  
 
	"gorm.io/gorm/utils"  
) 
 
 
var  ErrRecordNotFound  = errors .New ("record not found" ) 
 
 
const  ( 
	Reset        = "\033[0m"  
	Red          = "\033[31m"  
	Green        = "\033[32m"  
	Yellow       = "\033[33m"  
	Blue         = "\033[34m"  
	Magenta      = "\033[35m"  
	Cyan         = "\033[36m"  
	White        = "\033[37m"  
	BlueBold     = "\033[34;1m"  
	MagentaBold  = "\033[35;1m"  
	RedBold      = "\033[31;1m"  
	YellowBold   = "\033[33;1m"  
) 
 
 
type  LogLevel  int  
 
const  ( 
	 
	Silent  LogLevel  = iota  + 1  
	 
	Error  
	 
	Warn  
	 
	Info  
) 
 
 
type  Writer  interface  { 
	Printf (string , ...interface {}) 
} 
 
 
type  Config  struct  { 
	SlowThreshold             time .Duration  
	Colorful                  bool  
	IgnoreRecordNotFoundError bool  
	ParameterizedQueries      bool  
	LogLevel                  LogLevel  
} 
 
 
type  Interface  interface  { 
	LogMode (LogLevel ) Interface  
	Info (context .Context , string , ...interface {}) 
	Warn (context .Context , string , ...interface {}) 
	Error (context .Context , string , ...interface {}) 
	Trace (ctx context .Context , begin time .Time , fc func () (sql string , rowsAffected int64 ), err error ) 
} 
 
var  ( 
	 
	Discard  = New (log .New (io .Discard , "" , log .LstdFlags ), Config {}) 
	 
	Default  = New (log .New (os .Stdout , "\r\n" , log .LstdFlags ), Config { 
		SlowThreshold :             200  * time .Millisecond , 
		LogLevel :                  Warn , 
		IgnoreRecordNotFoundError : false , 
		Colorful :                  true , 
	}) 
	 
	Recorder  = traceRecorder {Interface : Default , BeginAt : time .Now ()} 
) 
 
 
func  New  (writer  Writer , config  Config ) Interface  { 
	var  ( 
		infoStr       = "%s\n[info] "  
		warnStr       = "%s\n[warn] "  
		errStr        = "%s\n[error] "  
		traceStr      = "%s\n[%.3fms] [rows:%v] %s"  
		traceWarnStr  = "%s %s\n[%.3fms] [rows:%v] %s"  
		traceErrStr   = "%s %s\n[%.3fms] [rows:%v] %s"  
	) 
 
	if  config .Colorful  { 
		infoStr  = Green  + "%s\n"  + Reset  + Green  + "[info] "  + Reset  
		warnStr  = BlueBold  + "%s\n"  + Reset  + Magenta  + "[warn] "  + Reset  
		errStr  = Magenta  + "%s\n"  + Reset  + Red  + "[error] "  + Reset  
		traceStr  = Green  + "%s\n"  + Reset  + Yellow  + "[%.3fms] "  + BlueBold  + "[rows:%v]"  + Reset  + " %s"  
		traceWarnStr  = Green  + "%s "  + Yellow  + "%s\n"  + Reset  + RedBold  + "[%.3fms] "  + Yellow  + "[rows:%v]"  + Magenta  + " %s"  + Reset  
		traceErrStr  = RedBold  + "%s "  + MagentaBold  + "%s\n"  + Reset  + Yellow  + "[%.3fms] "  + BlueBold  + "[rows:%v]"  + Reset  + " %s"  
	} 
 
	return  &logger { 
		Writer :       writer , 
		Config :       config , 
		infoStr :      infoStr , 
		warnStr :      warnStr , 
		errStr :       errStr , 
		traceStr :     traceStr , 
		traceWarnStr : traceWarnStr , 
		traceErrStr :  traceErrStr , 
	} 
} 
 
type  logger struct  { 
	Writer  
	Config  
	infoStr, warnStr, errStr            string  
	traceStr, traceErrStr, traceWarnStr string  
} 
 
 
func  (l  *logger ) LogMode (level  LogLevel ) Interface  { 
	newlogger  := *l  
	newlogger .LogLevel  = level  
	return  &newlogger  
} 
 
 
func  (l  logger ) Info (ctx  context .Context , msg  string , data  ...interface {}) { 
	if  l .LogLevel  >= Info  { 
		l .Printf (l .infoStr +msg , append ([]interface {}{utils .FileWithLineNum ()}, data ...)...) 
	} 
} 
 
 
func  (l  logger ) Warn (ctx  context .Context , msg  string , data  ...interface {}) { 
	if  l .LogLevel  >= Warn  { 
		l .Printf (l .warnStr +msg , append ([]interface {}{utils .FileWithLineNum ()}, data ...)...) 
	} 
} 
 
 
func  (l  logger ) Error (ctx  context .Context , msg  string , data  ...interface {}) { 
	if  l .LogLevel  >= Error  { 
		l .Printf (l .errStr +msg , append ([]interface {}{utils .FileWithLineNum ()}, data ...)...) 
	} 
} 
 
 
func  (l  logger ) Trace (ctx  context .Context , begin  time .Time , fc  func () (string , int64 ), err  error ) { 
	if  l .LogLevel  <= Silent  { 
		return  
	} 
 
	elapsed  := time .Since (begin ) 
	switch  { 
	case  err  != nil  && l .LogLevel  >= Error  && (!errors .Is (err , ErrRecordNotFound ) || !l .IgnoreRecordNotFoundError ): 
		sql , rows  := fc () 
		if  rows  == -1  { 
			l .Printf (l .traceErrStr , utils .FileWithLineNum (), err , float64 (elapsed .Nanoseconds ())/1e6 , "-" , sql ) 
		} else  { 
			l .Printf (l .traceErrStr , utils .FileWithLineNum (), err , float64 (elapsed .Nanoseconds ())/1e6 , rows , sql ) 
		} 
	case  elapsed  > l .SlowThreshold  && l .SlowThreshold  != 0  && l .LogLevel  >= Warn : 
		sql , rows  := fc () 
		slowLog  := fmt .Sprintf ("SLOW SQL >= %v" , l .SlowThreshold ) 
		if  rows  == -1  { 
			l .Printf (l .traceWarnStr , utils .FileWithLineNum (), slowLog , float64 (elapsed .Nanoseconds ())/1e6 , "-" , sql ) 
		} else  { 
			l .Printf (l .traceWarnStr , utils .FileWithLineNum (), slowLog , float64 (elapsed .Nanoseconds ())/1e6 , rows , sql ) 
		} 
	case  l .LogLevel  == Info : 
		sql , rows  := fc () 
		if  rows  == -1  { 
			l .Printf (l .traceStr , utils .FileWithLineNum (), float64 (elapsed .Nanoseconds ())/1e6 , "-" , sql ) 
		} else  { 
			l .Printf (l .traceStr , utils .FileWithLineNum (), float64 (elapsed .Nanoseconds ())/1e6 , rows , sql ) 
		} 
	} 
} 
 
 
func  (l  logger ) ParamsFilter (ctx  context .Context , sql  string , params  ...interface {}) (string , []interface {}) { 
	if  l .Config .ParameterizedQueries  { 
		return  sql , nil  
	} 
	return  sql , params  
} 
 
type  traceRecorder struct  { 
	Interface  
	BeginAt      time .Time  
	SQL          string  
	RowsAffected int64  
	Err          error  
} 
 
 
func  (l  traceRecorder ) New () *traceRecorder  { 
	return  &traceRecorder {Interface : l .Interface , BeginAt : time .Now ()} 
} 
 
 
func  (l  *traceRecorder ) Trace (ctx  context .Context , begin  time .Time , fc  func () (string , int64 ), err  error ) { 
	l .BeginAt  = begin  
	l .SQL , l .RowsAffected  = fc () 
	l .Err  = err  
} 
  
The pages are generated with Golds   v0.6.7 . (GOOS=linux GOARCH=amd64)
Golds  is a Go 101  project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1  (reachable from the left QR code) to get the latest news of Golds .