123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206 |
- package log
- import (
- "io"
- "log"
- "os"
- "strings"
- "sync"
- "time"
- )
- // Defaults for package level variables
- var (
- DefaultLevel = InfoLevel
- DefaultFormat = TextFormat
- DefaultOutput = &peekLogWriter{os.Stderr}
- )
- var (
- level = DefaultLevel
- format = DefaultFormat
- overrides = make(map[string][]*levelOverride)
- output io.Writer = DefaultOutput
- filename = ""
- mu = &sync.RWMutex{}
- )
- // init sets the default log output (including log.SetOutput)
- //
- // This has to be explicitly called, because DefaultOutput is a peekLogWriter,
- // which wraps os.Stderr.
- func init() {
- SetOutput(DefaultOutput)
- }
- // Fatal prints the given message, and exits the program
- func Fatal(message string, v ...any) {
- newEvent().Fatal(message, v...)
- }
- // Error prints the given message, if the current log level is ERROR or lower
- func Error(message string, v ...any) {
- newEvent().Error(message, v...)
- }
- // Warn prints the given message, if the current log level is WARN or lower
- func Warn(message string, v ...any) {
- newEvent().Warn(message, v...)
- }
- // Info prints the given message, if the current log level is INFO or lower
- func Info(message string, v ...any) {
- newEvent().Info(message, v...)
- }
- // Debug prints the given message, if the current log level is DEBUG or lower
- func Debug(message string, v ...any) {
- newEvent().Debug(message, v...)
- }
- // Trace prints the given message, if the current log level is TRACE
- func Trace(message string, v ...any) {
- newEvent().Trace(message, v...)
- }
- // With creates a new log event and adds the fields of the given Contexter structs
- func With(contexts ...Contexter) *Event {
- return newEvent().With(contexts...)
- }
- // Field creates a new log event and adds a custom field and value to it
- func Field(key string, value any) *Event {
- return newEvent().Field(key, value)
- }
- // Fields creates a new log event and adds a map of fields to it
- func Fields(fields Context) *Event {
- return newEvent().Fields(fields)
- }
- // Tag creates a new log event and adds a "tag" field to it
- func Tag(tag string) *Event {
- return newEvent().Tag(tag)
- }
- // Time creates a new log event and sets the time field
- func Time(time time.Time) *Event {
- return newEvent().Time(time)
- }
- // Timing runs f and records the time if took to execute it in "time_taken_ms"
- func Timing(f func()) *Event {
- return newEvent().Timing(f)
- }
- // CurrentLevel returns the current log level
- func CurrentLevel() Level {
- mu.RLock()
- defer mu.RUnlock()
- return level
- }
- // SetLevel sets a new log level
- func SetLevel(newLevel Level) {
- mu.Lock()
- defer mu.Unlock()
- level = newLevel
- }
- // SetLevelOverride adds a log override for the given field
- func SetLevelOverride(field string, value string, level Level) {
- mu.Lock()
- defer mu.Unlock()
- if _, ok := overrides[field]; !ok {
- overrides[field] = make([]*levelOverride, 0)
- }
- overrides[field] = append(overrides[field], &levelOverride{value: value, level: level})
- }
- // ResetLevelOverrides removes all log level overrides
- func ResetLevelOverrides() {
- mu.Lock()
- defer mu.Unlock()
- overrides = make(map[string][]*levelOverride)
- }
- // CurrentFormat returns the current log format
- func CurrentFormat() Format {
- mu.RLock()
- defer mu.RUnlock()
- return format
- }
- // SetFormat sets a new log format
- func SetFormat(newFormat Format) {
- mu.Lock()
- defer mu.Unlock()
- format = newFormat
- if newFormat == JSONFormat {
- DisableDates()
- }
- }
- // SetOutput sets the log output writer
- func SetOutput(w io.Writer) {
- mu.Lock()
- defer mu.Unlock()
- output = &peekLogWriter{w}
- if f, ok := w.(*os.File); ok {
- filename = f.Name()
- } else {
- filename = ""
- }
- log.SetOutput(output)
- }
- // File returns the log file, if any, or an empty string otherwise
- func File() string {
- mu.RLock()
- defer mu.RUnlock()
- return filename
- }
- // IsFile returns true if the output is a non-default file
- func IsFile() bool {
- mu.RLock()
- defer mu.RUnlock()
- return filename != ""
- }
- // DisableDates disables the date/time prefix
- func DisableDates() {
- log.SetFlags(0)
- }
- // Loggable returns true if the given log level is lower or equal to the current log level
- func Loggable(l Level) bool {
- return CurrentLevel() <= l
- }
- // IsTrace returns true if the current log level is TraceLevel
- func IsTrace() bool {
- return Loggable(TraceLevel)
- }
- // IsDebug returns true if the current log level is DebugLevel or below
- func IsDebug() bool {
- return Loggable(DebugLevel)
- }
- // peekLogWriter is an io.Writer which will peek at the rendered log event,
- // and ensure that the rendered output is valid JSON. This is a hack!
- type peekLogWriter struct {
- w io.Writer
- }
- func (w *peekLogWriter) Write(p []byte) (n int, err error) {
- if len(p) == 0 || p[0] == '{' || CurrentFormat() == TextFormat {
- return w.w.Write(p)
- }
- m := newEvent().Tag(tagStdLog).Render(InfoLevel, strings.TrimSpace(string(p)))
- if m == "" {
- return 0, nil
- }
- return w.w.Write([]byte(m + "\n"))
- }
|