|
|
@ -13,11 +13,11 @@ import (
|
|
|
|
"time"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
const CallerDepth = 4
|
|
|
|
const callerDepth = 4
|
|
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
var (
|
|
|
|
timeFormat = "2006-01-02T15:04:05.000Z07:00"
|
|
|
|
timeFormat = "2006-01-02T15:04:05.000Z07:00"
|
|
|
|
logLevel uint32 = DebugLevel
|
|
|
|
logLevel uint32
|
|
|
|
encoding uint32 = plainEncodingType
|
|
|
|
encoding uint32 = plainEncodingType
|
|
|
|
// maxContentLength is used to truncate the log content, 0 for not truncating.
|
|
|
|
// maxContentLength is used to truncate the log content, 0 for not truncating.
|
|
|
|
maxContentLength uint32
|
|
|
|
maxContentLength uint32
|
|
|
@ -51,173 +51,304 @@ type (
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// Field returns a LogField for the given key and value.
|
|
|
|
// Alert alerts v in alert level, and the message is written to error log.
|
|
|
|
func Field(key string, value any) LogField {
|
|
|
|
func Alert(v string) {
|
|
|
|
switch val := value.(type) {
|
|
|
|
getWriter().Alert(v)
|
|
|
|
case error:
|
|
|
|
|
|
|
|
return LogField{Key: key, Value: val.Error()}
|
|
|
|
|
|
|
|
case []error:
|
|
|
|
|
|
|
|
var errs []string
|
|
|
|
|
|
|
|
for _, err := range val {
|
|
|
|
|
|
|
|
errs = append(errs, err.Error())
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return LogField{Key: key, Value: errs}
|
|
|
|
|
|
|
|
case time.Duration:
|
|
|
|
|
|
|
|
return LogField{Key: key, Value: fmt.Sprint(val)}
|
|
|
|
|
|
|
|
case []time.Duration:
|
|
|
|
|
|
|
|
var durs []string
|
|
|
|
|
|
|
|
for _, dur := range val {
|
|
|
|
|
|
|
|
durs = append(durs, fmt.Sprint(dur))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return LogField{Key: key, Value: durs}
|
|
|
|
|
|
|
|
case []time.Time:
|
|
|
|
|
|
|
|
var times []string
|
|
|
|
|
|
|
|
for _, t := range val {
|
|
|
|
|
|
|
|
times = append(times, fmt.Sprint(t))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return LogField{Key: key, Value: times}
|
|
|
|
|
|
|
|
case fmt.Stringer:
|
|
|
|
|
|
|
|
return LogField{Key: key, Value: val.String()}
|
|
|
|
|
|
|
|
case []fmt.Stringer:
|
|
|
|
|
|
|
|
var strs []string
|
|
|
|
|
|
|
|
for _, str := range val {
|
|
|
|
|
|
|
|
strs = append(strs, str.String())
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return LogField{Key: key, Value: strs}
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
return LogField{Key: key, Value: val}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Close closes the logging.
|
|
|
|
|
|
|
|
func Close() error {
|
|
|
|
|
|
|
|
if w := writer.Swap(nil); w != nil {
|
|
|
|
|
|
|
|
return w.(io.Closer).Close()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Alert alerts v in alert level, and the message is written to error log.
|
|
|
|
return nil
|
|
|
|
func Alert(v string) {
|
|
|
|
|
|
|
|
GetWriter().Alert(v)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Debug writes v into access log.
|
|
|
|
// Debug writes v into access log.
|
|
|
|
func Debug(v ...any) {
|
|
|
|
func Debug(v ...any) {
|
|
|
|
|
|
|
|
if shallLog(DebugLevel) {
|
|
|
|
writeDebug(fmt.Sprint(v...))
|
|
|
|
writeDebug(fmt.Sprint(v...))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Debugf writes v with format into access log.
|
|
|
|
// Debugf writes v with format into access log.
|
|
|
|
func Debugf(format string, v ...any) {
|
|
|
|
func Debugf(format string, v ...any) {
|
|
|
|
|
|
|
|
if shallLog(DebugLevel) {
|
|
|
|
writeDebug(fmt.Sprintf(format, v...))
|
|
|
|
writeDebug(fmt.Sprintf(format, v...))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Debugv writes v into access log with json content.
|
|
|
|
// Debugv writes v into access log with json content.
|
|
|
|
func Debugv(v any) {
|
|
|
|
func Debugv(v any) {
|
|
|
|
|
|
|
|
if shallLog(DebugLevel) {
|
|
|
|
writeDebug(v)
|
|
|
|
writeDebug(v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Debugw writes msg along with fields into access log.
|
|
|
|
// Debugw writes msg along with fields into access log.
|
|
|
|
func Debugw(msg string, fields ...LogField) {
|
|
|
|
func Debugw(msg string, fields ...LogField) {
|
|
|
|
|
|
|
|
if shallLog(DebugLevel) {
|
|
|
|
writeDebug(msg, fields...)
|
|
|
|
writeDebug(msg, fields...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Disable disables the logging.
|
|
|
|
|
|
|
|
func Disable() {
|
|
|
|
|
|
|
|
atomic.StoreUint32(&disableLog, 1)
|
|
|
|
|
|
|
|
writer.Store(nopWriter{})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// DisableStat disables the stat logs.
|
|
|
|
|
|
|
|
func DisableStat() {
|
|
|
|
|
|
|
|
atomic.StoreUint32(&disableStat, 1)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Error writes v into error log.
|
|
|
|
// Error writes v into error log.
|
|
|
|
func Error(v ...any) {
|
|
|
|
func Error(v ...any) {
|
|
|
|
|
|
|
|
if shallLog(ErrorLevel) {
|
|
|
|
writeError(fmt.Sprint(v...))
|
|
|
|
writeError(fmt.Sprint(v...))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Errorf writes v with format into error log.
|
|
|
|
// Errorf writes v with format into error log.
|
|
|
|
func Errorf(format string, v ...any) {
|
|
|
|
func Errorf(format string, v ...any) {
|
|
|
|
|
|
|
|
if shallLog(ErrorLevel) {
|
|
|
|
writeError(fmt.Errorf(format, v...).Error())
|
|
|
|
writeError(fmt.Errorf(format, v...).Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ErrorStack writes v along with call stack into error log.
|
|
|
|
// ErrorStack writes v along with call stack into error log.
|
|
|
|
func ErrorStack(v ...any) {
|
|
|
|
func ErrorStack(v ...any) {
|
|
|
|
|
|
|
|
if shallLog(ErrorLevel) {
|
|
|
|
// there is newline in stack string
|
|
|
|
// there is newline in stack string
|
|
|
|
writeStack(fmt.Sprint(v...))
|
|
|
|
writeStack(fmt.Sprint(v...))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ErrorStackf writes v along with call stack in format into error log.
|
|
|
|
// ErrorStackf writes v along with call stack in format into error log.
|
|
|
|
func ErrorStackf(format string, v ...any) {
|
|
|
|
func ErrorStackf(format string, v ...any) {
|
|
|
|
|
|
|
|
if shallLog(ErrorLevel) {
|
|
|
|
// there is newline in stack string
|
|
|
|
// there is newline in stack string
|
|
|
|
writeStack(fmt.Sprintf(format, v...))
|
|
|
|
writeStack(fmt.Sprintf(format, v...))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Errorv writes v into error log with json content.
|
|
|
|
// Errorv writes v into error log with json content.
|
|
|
|
// No call stack attached, because not elegant to pack the messages.
|
|
|
|
// No call stack attached, because not elegant to pack the messages.
|
|
|
|
func Errorv(v any) {
|
|
|
|
func Errorv(v any) {
|
|
|
|
|
|
|
|
if shallLog(ErrorLevel) {
|
|
|
|
writeError(v)
|
|
|
|
writeError(v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Errorw writes msg along with fields into error log.
|
|
|
|
// Errorw writes msg along with fields into error log.
|
|
|
|
func Errorw(msg string, fields ...LogField) {
|
|
|
|
func Errorw(msg string, fields ...LogField) {
|
|
|
|
|
|
|
|
if shallLog(ErrorLevel) {
|
|
|
|
writeError(msg, fields...)
|
|
|
|
writeError(msg, fields...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Must checks if err is nil, otherwise logs the error and exits.
|
|
|
|
|
|
|
|
func Must(err error) {
|
|
|
|
|
|
|
|
if err == nil {
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
msg := err.Error()
|
|
|
|
// Field returns a LogField for the given key and value.
|
|
|
|
log.Print(msg)
|
|
|
|
func Field(key string, value any) LogField {
|
|
|
|
GetWriter().Severe(msg)
|
|
|
|
switch val := value.(type) {
|
|
|
|
os.Exit(1)
|
|
|
|
case error:
|
|
|
|
|
|
|
|
return LogField{Key: key, Value: val.Error()}
|
|
|
|
|
|
|
|
case []error:
|
|
|
|
|
|
|
|
var errs []string
|
|
|
|
|
|
|
|
for _, err := range val {
|
|
|
|
|
|
|
|
errs = append(errs, err.Error())
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return LogField{Key: key, Value: errs}
|
|
|
|
|
|
|
|
case time.Duration:
|
|
|
|
|
|
|
|
return LogField{Key: key, Value: fmt.Sprint(val)}
|
|
|
|
|
|
|
|
case []time.Duration:
|
|
|
|
|
|
|
|
var durs []string
|
|
|
|
|
|
|
|
for _, dur := range val {
|
|
|
|
|
|
|
|
durs = append(durs, fmt.Sprint(dur))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return LogField{Key: key, Value: durs}
|
|
|
|
|
|
|
|
case []time.Time:
|
|
|
|
|
|
|
|
var times []string
|
|
|
|
|
|
|
|
for _, t := range val {
|
|
|
|
|
|
|
|
times = append(times, fmt.Sprint(t))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return LogField{Key: key, Value: times}
|
|
|
|
|
|
|
|
case fmt.Stringer:
|
|
|
|
|
|
|
|
return LogField{Key: key, Value: val.String()}
|
|
|
|
|
|
|
|
case []fmt.Stringer:
|
|
|
|
|
|
|
|
var strs []string
|
|
|
|
|
|
|
|
for _, str := range val {
|
|
|
|
|
|
|
|
strs = append(strs, str.String())
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return LogField{Key: key, Value: strs}
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
return LogField{Key: key, Value: val}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Info writes v into access log.
|
|
|
|
// Info writes v into access log.
|
|
|
|
func Info(v ...any) {
|
|
|
|
func Info(v ...any) {
|
|
|
|
|
|
|
|
if shallLog(InfoLevel) {
|
|
|
|
writeInfo(fmt.Sprint(v...))
|
|
|
|
writeInfo(fmt.Sprint(v...))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Infof writes v with format into access log.
|
|
|
|
// Infof writes v with format into access log.
|
|
|
|
func Infof(format string, v ...any) {
|
|
|
|
func Infof(format string, v ...any) {
|
|
|
|
|
|
|
|
if shallLog(InfoLevel) {
|
|
|
|
writeInfo(fmt.Sprintf(format, v...))
|
|
|
|
writeInfo(fmt.Sprintf(format, v...))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Infov writes v into access log with json content.
|
|
|
|
// Infov writes v into access log with json content.
|
|
|
|
func Infov(v any) {
|
|
|
|
func Infov(v any) {
|
|
|
|
|
|
|
|
if shallLog(InfoLevel) {
|
|
|
|
writeInfo(v)
|
|
|
|
writeInfo(v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Infow writes msg along with fields into access log.
|
|
|
|
// Infow writes msg along with fields into access log.
|
|
|
|
func Infow(msg string, fields ...LogField) {
|
|
|
|
func Infow(msg string, fields ...LogField) {
|
|
|
|
|
|
|
|
if shallLog(InfoLevel) {
|
|
|
|
writeInfo(msg, fields...)
|
|
|
|
writeInfo(msg, fields...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Must checks if err is nil, otherwise logs the error and exits.
|
|
|
|
|
|
|
|
func Must(err error) {
|
|
|
|
|
|
|
|
if err == nil {
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
msg := fmt.Sprintf("%+v\n\n%s", err.Error(), debug.Stack())
|
|
|
|
|
|
|
|
log.Print(msg)
|
|
|
|
|
|
|
|
getWriter().Severe(msg)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ExitOnFatal.True() {
|
|
|
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
panic(msg)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// MustSetup sets up logging with given config c. It exits on error.
|
|
|
|
|
|
|
|
func MustSetup(c LogConf) {
|
|
|
|
|
|
|
|
Must(SetUp(c))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Reset clears the writer and resets the log level.
|
|
|
|
|
|
|
|
func Reset() Writer {
|
|
|
|
|
|
|
|
return writer.Swap(nil)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// SetLevel sets the logging level. It can be used to suppress some logs.
|
|
|
|
|
|
|
|
func SetLevel(level uint32) {
|
|
|
|
|
|
|
|
atomic.StoreUint32(&logLevel, level)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// SetWriter sets the logging writer. It can be used to customize the logging.
|
|
|
|
|
|
|
|
func SetWriter(w Writer) {
|
|
|
|
|
|
|
|
if atomic.LoadUint32(&disableLog) == 0 {
|
|
|
|
|
|
|
|
writer.Store(w)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// SetUp sets up the logx. If already set up, just return nil.
|
|
|
|
|
|
|
|
// we allow SetUp to be called multiple times, because for example
|
|
|
|
|
|
|
|
// we need to allow different service frameworks to initialize logx respectively.
|
|
|
|
|
|
|
|
func SetUp(c LogConf) (err error) {
|
|
|
|
|
|
|
|
// Just ignore the subsequent SetUp calls.
|
|
|
|
|
|
|
|
// Because multiple services in one process might call SetUp respectively.
|
|
|
|
|
|
|
|
// Need to wait for the first caller to complete the execution.
|
|
|
|
|
|
|
|
setupOnce.Do(func() {
|
|
|
|
|
|
|
|
setupLogLevel(c)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if !c.Stat {
|
|
|
|
|
|
|
|
DisableStat()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if len(c.TimeFormat) > 0 {
|
|
|
|
|
|
|
|
timeFormat = c.TimeFormat
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
atomic.StoreUint32(&maxContentLength, c.MaxContentLength)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
switch c.Encoding {
|
|
|
|
|
|
|
|
case plainEncoding:
|
|
|
|
|
|
|
|
atomic.StoreUint32(&encoding, plainEncodingType)
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
atomic.StoreUint32(&encoding, jsonEncodingType)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
switch c.Mode {
|
|
|
|
|
|
|
|
case fileMode:
|
|
|
|
|
|
|
|
err = setupWithFiles(c)
|
|
|
|
|
|
|
|
case volumeMode:
|
|
|
|
|
|
|
|
err = setupWithVolume(c)
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
setupWithConsole()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Severe writes v into severe log.
|
|
|
|
// Severe writes v into severe log.
|
|
|
|
func Severe(v ...any) {
|
|
|
|
func Severe(v ...any) {
|
|
|
|
|
|
|
|
if shallLog(SevereLevel) {
|
|
|
|
writeSevere(fmt.Sprint(v...))
|
|
|
|
writeSevere(fmt.Sprint(v...))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Severef writes v with format into severe log.
|
|
|
|
// Severef writes v with format into severe log.
|
|
|
|
func Severef(format string, v ...any) {
|
|
|
|
func Severef(format string, v ...any) {
|
|
|
|
|
|
|
|
if shallLog(SevereLevel) {
|
|
|
|
writeSevere(fmt.Sprintf(format, v...))
|
|
|
|
writeSevere(fmt.Sprintf(format, v...))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Slow writes v into slow log.
|
|
|
|
// Slow writes v into slow log.
|
|
|
|
func Slow(v ...any) {
|
|
|
|
func Slow(v ...any) {
|
|
|
|
|
|
|
|
if shallLog(ErrorLevel) {
|
|
|
|
writeSlow(fmt.Sprint(v...))
|
|
|
|
writeSlow(fmt.Sprint(v...))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Slowf writes v with format into slow log.
|
|
|
|
// Slowf writes v with format into slow log.
|
|
|
|
func Slowf(format string, v ...any) {
|
|
|
|
func Slowf(format string, v ...any) {
|
|
|
|
|
|
|
|
if shallLog(ErrorLevel) {
|
|
|
|
writeSlow(fmt.Sprintf(format, v...))
|
|
|
|
writeSlow(fmt.Sprintf(format, v...))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Slowv writes v into slow log with json content.
|
|
|
|
// Slowv writes v into slow log with json content.
|
|
|
|
func Slowv(v any) {
|
|
|
|
func Slowv(v any) {
|
|
|
|
|
|
|
|
if shallLog(ErrorLevel) {
|
|
|
|
writeSlow(v)
|
|
|
|
writeSlow(v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Sloww writes msg along with fields into slow log.
|
|
|
|
// Sloww writes msg along with fields into slow log.
|
|
|
|
func Sloww(msg string, fields ...LogField) {
|
|
|
|
func Sloww(msg string, fields ...LogField) {
|
|
|
|
|
|
|
|
if shallLog(ErrorLevel) {
|
|
|
|
writeSlow(msg, fields...)
|
|
|
|
writeSlow(msg, fields...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Stat writes v into stat log.
|
|
|
|
// Stat writes v into stat log.
|
|
|
|
func Stat(v ...any) {
|
|
|
|
func Stat(v ...any) {
|
|
|
|
|
|
|
|
if shallLogStat() && shallLog(InfoLevel) {
|
|
|
|
writeStat(fmt.Sprint(v...))
|
|
|
|
writeStat(fmt.Sprint(v...))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Statf writes v with format into stat log.
|
|
|
|
// Statf writes v with format into stat log.
|
|
|
|
func Statf(format string, v ...any) {
|
|
|
|
func Statf(format string, v ...any) {
|
|
|
|
|
|
|
|
if shallLogStat() && shallLog(InfoLevel) {
|
|
|
|
writeStat(fmt.Sprintf(format, v...))
|
|
|
|
writeStat(fmt.Sprintf(format, v...))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// WithCooldownMillis customizes logging on writing call stack interval.
|
|
|
|
// WithCooldownMillis customizes logging on writing call stack interval.
|
|
|
|
func WithCooldownMillis(millis int) LogOption {
|
|
|
|
func WithCooldownMillis(millis int) LogOption {
|
|
|
@ -261,90 +392,8 @@ func WithRotation(r string) LogOption {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// MustSetup sets up logging with given config c. It exits on error.
|
|
|
|
|
|
|
|
func MustSetup(c LogConf) {
|
|
|
|
|
|
|
|
Must(SetUp(c))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Reset clears the writer and resets the log level.
|
|
|
|
|
|
|
|
func Reset() Writer {
|
|
|
|
|
|
|
|
return writer.Swap(nil)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// SetLevel sets the logging level. It can be used to suppress some logs.
|
|
|
|
|
|
|
|
func SetLevel(level uint32) {
|
|
|
|
|
|
|
|
atomic.StoreUint32(&logLevel, level)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// SetWriter sets the logging writer. It can be used to customize the logging.
|
|
|
|
|
|
|
|
func SetWriter(w Writer) {
|
|
|
|
|
|
|
|
if atomic.LoadUint32(&disableLog) == 0 {
|
|
|
|
|
|
|
|
writer.Store(w)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// SetUp sets up the logx. If already set up, just return nil.
|
|
|
|
|
|
|
|
// we allow SetUp to be called multiple times, because for example
|
|
|
|
|
|
|
|
// we need to allow different service frameworks to initialize logx respectively.
|
|
|
|
|
|
|
|
func SetUp(c LogConf) (err error) {
|
|
|
|
|
|
|
|
// Just ignore the subsequent SetUp calls.
|
|
|
|
|
|
|
|
// Because multiple services in one process might call SetUp respectively.
|
|
|
|
|
|
|
|
// Need to wait for the first caller to complete the execution.
|
|
|
|
|
|
|
|
setupOnce.Do(func() {
|
|
|
|
|
|
|
|
setupLogLevel(c)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if !c.Stat {
|
|
|
|
|
|
|
|
DisableStat()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if len(c.TimeFormat) > 0 {
|
|
|
|
|
|
|
|
timeFormat = c.TimeFormat
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
atomic.StoreUint32(&maxContentLength, c.MaxContentLength)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
switch c.Encoding {
|
|
|
|
|
|
|
|
case plainEncoding:
|
|
|
|
|
|
|
|
atomic.StoreUint32(&encoding, plainEncodingType)
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
atomic.StoreUint32(&encoding, jsonEncodingType)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
switch c.Mode {
|
|
|
|
|
|
|
|
case fileMode:
|
|
|
|
|
|
|
|
err = setupWithFiles(c)
|
|
|
|
|
|
|
|
case volumeMode:
|
|
|
|
|
|
|
|
err = setupWithVolume(c)
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
setupWithConsole()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Close closes the logging.
|
|
|
|
|
|
|
|
func Close() error {
|
|
|
|
|
|
|
|
if w := writer.Swap(nil); w != nil {
|
|
|
|
|
|
|
|
return w.(io.Closer).Close()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Disable disables the logging.
|
|
|
|
|
|
|
|
func Disable() {
|
|
|
|
|
|
|
|
atomic.StoreUint32(&disableLog, 1)
|
|
|
|
|
|
|
|
writer.Store(nopWriter{})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// DisableStat disables the stat logs.
|
|
|
|
|
|
|
|
func DisableStat() {
|
|
|
|
|
|
|
|
atomic.StoreUint32(&disableStat, 1)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func addCaller(fields ...LogField) []LogField {
|
|
|
|
func addCaller(fields ...LogField) []LogField {
|
|
|
|
return append(fields, Field(callerKey, getCaller(CallerDepth)))
|
|
|
|
return append(fields, Field(callerKey, getCaller(callerDepth)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func createOutput(path string) (io.WriteCloser, error) {
|
|
|
|
func createOutput(path string) (io.WriteCloser, error) {
|
|
|
@ -352,17 +401,19 @@ func createOutput(path string) (io.WriteCloser, error) {
|
|
|
|
return nil, ErrLogPathNotSet
|
|
|
|
return nil, ErrLogPathNotSet
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var rule RotateRule
|
|
|
|
switch options.rotationRule {
|
|
|
|
switch options.rotationRule {
|
|
|
|
case sizeRotationRule:
|
|
|
|
case sizeRotationRule:
|
|
|
|
return NewLogger(path, NewSizeLimitRotateRule(path, backupFileDelimiter, options.keepDays,
|
|
|
|
rule = NewSizeLimitRotateRule(path, backupFileDelimiter, options.keepDays, options.maxSize,
|
|
|
|
options.maxSize, options.maxBackups, options.gzipEnabled), options.gzipEnabled)
|
|
|
|
options.maxBackups, options.gzipEnabled)
|
|
|
|
default:
|
|
|
|
default:
|
|
|
|
return NewLogger(path, DefaultRotateRule(path, backupFileDelimiter, options.keepDays,
|
|
|
|
rule = DefaultRotateRule(path, backupFileDelimiter, options.keepDays, options.gzipEnabled)
|
|
|
|
options.gzipEnabled), options.gzipEnabled)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return NewLogger(path, rule, options.gzipEnabled)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func GetWriter() Writer {
|
|
|
|
func getWriter() Writer {
|
|
|
|
w := writer.Load()
|
|
|
|
w := writer.Load()
|
|
|
|
if w == nil {
|
|
|
|
if w == nil {
|
|
|
|
w = writer.StoreIfNil(newConsoleWriter())
|
|
|
|
w = writer.StoreIfNil(newConsoleWriter())
|
|
|
@ -421,44 +472,58 @@ func shallLogStat() bool {
|
|
|
|
return atomic.LoadUint32(&disableStat) == 0
|
|
|
|
return atomic.LoadUint32(&disableStat) == 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// writeDebug writes v into debug log.
|
|
|
|
|
|
|
|
// Not checking shallLog here is for performance consideration.
|
|
|
|
|
|
|
|
// If we check shallLog here, the fmt.Sprint might be called even if the log level is not enabled.
|
|
|
|
|
|
|
|
// The caller should check shallLog before calling this function.
|
|
|
|
func writeDebug(val any, fields ...LogField) {
|
|
|
|
func writeDebug(val any, fields ...LogField) {
|
|
|
|
if shallLog(DebugLevel) {
|
|
|
|
getWriter().Debug(val, addCaller(fields...)...)
|
|
|
|
GetWriter().Debug(val, addCaller(fields...)...)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// writeError writes v into error log.
|
|
|
|
|
|
|
|
// Not checking shallLog here is for performance consideration.
|
|
|
|
|
|
|
|
// If we check shallLog here, the fmt.Sprint might be called even if the log level is not enabled.
|
|
|
|
|
|
|
|
// The caller should check shallLog before calling this function.
|
|
|
|
func writeError(val any, fields ...LogField) {
|
|
|
|
func writeError(val any, fields ...LogField) {
|
|
|
|
if shallLog(ErrorLevel) {
|
|
|
|
getWriter().Error(val, addCaller(fields...)...)
|
|
|
|
GetWriter().Error(val, addCaller(fields...)...)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// writeInfo writes v into info log.
|
|
|
|
|
|
|
|
// Not checking shallLog here is for performance consideration.
|
|
|
|
|
|
|
|
// If we check shallLog here, the fmt.Sprint might be called even if the log level is not enabled.
|
|
|
|
|
|
|
|
// The caller should check shallLog before calling this function.
|
|
|
|
func writeInfo(val any, fields ...LogField) {
|
|
|
|
func writeInfo(val any, fields ...LogField) {
|
|
|
|
if shallLog(InfoLevel) {
|
|
|
|
getWriter().Info(val, addCaller(fields...)...)
|
|
|
|
GetWriter().Info(val, addCaller(fields...)...)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// writeSevere writes v into severe log.
|
|
|
|
|
|
|
|
// Not checking shallLog here is for performance consideration.
|
|
|
|
|
|
|
|
// If we check shallLog here, the fmt.Sprint might be called even if the log level is not enabled.
|
|
|
|
|
|
|
|
// The caller should check shallLog before calling this function.
|
|
|
|
func writeSevere(msg string) {
|
|
|
|
func writeSevere(msg string) {
|
|
|
|
if shallLog(SevereLevel) {
|
|
|
|
getWriter().Severe(fmt.Sprintf("%s\n%s", msg, string(debug.Stack())))
|
|
|
|
GetWriter().Severe(fmt.Sprintf("%s\n%s", msg, string(debug.Stack())))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// writeSlow writes v into slow log.
|
|
|
|
|
|
|
|
// Not checking shallLog here is for performance consideration.
|
|
|
|
|
|
|
|
// If we check shallLog here, the fmt.Sprint might be called even if the log level is not enabled.
|
|
|
|
|
|
|
|
// The caller should check shallLog before calling this function.
|
|
|
|
func writeSlow(val any, fields ...LogField) {
|
|
|
|
func writeSlow(val any, fields ...LogField) {
|
|
|
|
if shallLog(ErrorLevel) {
|
|
|
|
getWriter().Slow(val, addCaller(fields...)...)
|
|
|
|
GetWriter().Slow(val, addCaller(fields...)...)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// writeStack writes v into stack log.
|
|
|
|
|
|
|
|
// Not checking shallLog here is for performance consideration.
|
|
|
|
|
|
|
|
// If we check shallLog here, the fmt.Sprint might be called even if the log level is not enabled.
|
|
|
|
|
|
|
|
// The caller should check shallLog before calling this function.
|
|
|
|
func writeStack(msg string) {
|
|
|
|
func writeStack(msg string) {
|
|
|
|
if shallLog(ErrorLevel) {
|
|
|
|
getWriter().Stack(fmt.Sprintf("%s\n%s", msg, string(debug.Stack())))
|
|
|
|
GetWriter().Stack(fmt.Sprintf("%s\n%s", msg, string(debug.Stack())))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// writeStat writes v into stat log.
|
|
|
|
|
|
|
|
// Not checking shallLog here is for performance consideration.
|
|
|
|
|
|
|
|
// If we check shallLog here, the fmt.Sprint might be called even if the log level is not enabled.
|
|
|
|
|
|
|
|
// The caller should check shallLog before calling this function.
|
|
|
|
func writeStat(msg string) {
|
|
|
|
func writeStat(msg string) {
|
|
|
|
if shallLogStat() && shallLog(InfoLevel) {
|
|
|
|
getWriter().Stat(msg, addCaller()...)
|
|
|
|
GetWriter().Stat(msg, addCaller()...)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|