logger

package
v1.19.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 8, 2023 License: LGPL-3.0 Imports: 12 Imported by: 26

README

Overview

Package logger is a goroutine-safe logging facility which writes logs with different severity levels to files, console, or both. Logs with different severity levels are written to different logfiles.

Features

  1. Auto rotation: It'll create a new logfile whenever day changes or size of the current logfile exceeds the configured size limit.
  2. Auto purging: It'll delete some oldest logfiles whenever the number of logfiles exceeds the configured limit.
  3. Log-through: Logs with higher severity level will be written to all the logfiles with lower severity level.
  4. Log levels: 6 different levels are supported. Logs with different levels are written to different logfiles. By setting the Logger object to a higher log level, lower level logs will be filtered out.
  5. Logs are not buffered, they are written to logfiles immediately with os.(*File).Write().
  6. It'll create symlinks that link to the most current logfiles.

Basic examples

Use the global Logger object

// logger.Init must be called first to create the global Logger object
err := logger.Init(&logger.Config{
	LogDir:          "./logs",
	LogFileMaxSize:  200,
	LogFileMaxNum:   500,
	LogFileNumToDel: 50,
	LogLevel:        logger.LogLevelInfo,
	LogDest:         logger.LogDestFile,
	Flag:            logger.ControlFlagLogLineNum,
})
if err != nil {
    panic(err)
}
logger.Infof("Failed to find player! uid=%d plid=%d cmd=%s xxx=%d", 1234, 678942, "getplayer", 102020101)
logger.Warnf("Failed to parse protocol! uid=%d plid=%d cmd=%s", 1234, 678942, "getplayer")

Create a new Logger object

While the global Logger object should be enough for most cases, you can also create as many Logger objects as desired.

newLogger, err := logger.New(&logger.Config{
	LogDir:          "./logs",
	LogFileMaxSize:  200,
	LogFileMaxNum:   500,
	LogFileNumToDel: 50,
	LogLevel:        logger.LogLevelInfo,
	LogDest:         logger.LogDestFile,
	Flag:            logger.ControlFlagLogLineNum,
})
if err != nil {
    panic(err)
}
newLogger.Info("abc", 123, 444.77)
newLogger.Error("abc", 123, 444.77)
newLogger.Close()

Documentation

Please refer to godoc or pkg.go.dev.

Performance

package main

import (
    "fmt"
    "github.com/antigloss/go/logger"
    "runtime"
    "sync"
    "time"
)

var wg sync.WaitGroup

func main() {
    logger.Init(&logger.Config{
        LogDir:          "./logs",
        LogFileMaxSize:  200,
        LogFileMaxNum:   500,
        LogFileNumToDel: 50,
        LogLevel:        logger.LogLevelInfo,
        LogDest:         logger.LogDestFile,
        Flag:            logger.ControlFlagLogLineNum,
    })

    fmt.Print("Single goroutine (1000000 writes), GOMAXPROCS(1): ")
    tSaved := time.Now()
    for i := 0; i != 1000000; i++ {
        logger.Infof("Failed to find player! uid=%d plid=%d cmd=%s xxx=%d", 1234, 678942, "getplayer", 102020101)
    }
    fmt.Println(time.Now().Sub(tSaved))

    fmt.Print("100000 goroutines (each makes 10 writes), GOMAXPROCS(1): ")
    test()

    fmt.Print("100000 goroutines (each makes 10 writes), GOMAXPROCS(2): ")
    runtime.GOMAXPROCS(2)
    test()

    fmt.Print("100000 goroutines (each makes 10 writes), GOMAXPROCS(4): ")
    runtime.GOMAXPROCS(4)
    test()

    fmt.Print("100000 goroutines (each makes 10 writes), GOMAXPROCS(8): ")
    runtime.GOMAXPROCS(8)
    test()

    fmt.Print("100000 goroutines (each makes 10 writes), GOMAXPROCS(16): ")
    runtime.GOMAXPROCS(16)
    test()
}

func test() {
    tSaved := time.Now()
    for i := 0; i != 100000; i++ {
        wg.Add(1)
        go func() {
            logger.Infof("Failed to find player! uid=%d plid=%d cmd=%s xxx=%d", 1234, 678942, "getplayer", 102020101)
            logger.Infof("Failed to find player! uid=%d plid=%d cmd=%s xxx=%d", 1234, 678942, "getplayer", 102020101)
            logger.Infof("Failed to find player! uid=%d plid=%d cmd=%s xxx=%d", 1234, 678942, "getplayer", 102020101)
            logger.Infof("Failed to find player! uid=%d plid=%d cmd=%s xxx=%d", 1234, 678942, "getplayer", 102020101)
            logger.Infof("Failed to find player! uid=%d plid=%d cmd=%s xxx=%d", 1234, 678942, "getplayer", 102020101)
            logger.Infof("Failed to find player! uid=%d plid=%d cmd=%s xxx=%d", 1234, 678942, "getplayer", 102020101)
            logger.Infof("Failed to find player! uid=%d plid=%d cmd=%s xxx=%d", 1234, 678942, "getplayer", 102020101)
            logger.Infof("Failed to find player! uid=%d plid=%d cmd=%s xxx=%d", 1234, 678942, "getplayer", 102020101)
            logger.Infof("Failed to find player! uid=%d plid=%d cmd=%s xxx=%d", 1234, 678942, "getplayer", 102020101)
            logger.Infof("Failed to find player! uid=%d plid=%d cmd=%s xxx=%d", 1234, 678942, "getplayer", 102020101)
            wg.Add(-1)
        }()
    }
    wg.Wait()
    fmt.Println(time.Now().Sub(tSaved))
}

Running this test program under Win10, i7-9700 @ 3.00GHz(8 cores, 8 threads), WDS500G2B0A-00SM50(SSD):

Single goroutine (1000000 writes), GOMAXPROCS(1): 3.2784983s
100000 goroutines (each makes 10 writes), GOMAXPROCS(1): 4.0297497s
100000 goroutines (each makes 10 writes), GOMAXPROCS(2): 3.7067683s
100000 goroutines (each makes 10 writes), GOMAXPROCS(4): 3.8241992s
100000 goroutines (each makes 10 writes), GOMAXPROCS(8): 4.2463802s
100000 goroutines (each makes 10 writes), GOMAXPROCS(16): 4.1943385s

About 233 ~ 303 thousand writes per second.

Under macOS Catalina 10.15.6,i9-9880H @ 2.3GHz(8 cores, 16 threads), APPLE SSD AP2048N:

Single goroutine (1000000 writes), GOMAXPROCS(1): 4.311780023s
100000 goroutines (each makes 10 writes), GOMAXPROCS(1): 6.627393432s
100000 goroutines (each makes 10 writes), GOMAXPROCS(2): 5.553245254s
100000 goroutines (each makes 10 writes), GOMAXPROCS(4): 6.579052789s
100000 goroutines (each makes 10 writes), GOMAXPROCS(8): 5.725944572s
100000 goroutines (each makes 10 writes), GOMAXPROCS(16): 5.952777505s

About 149 ~ 231 thousand writes per second.

Under a cloud server, Ubuntu 18.04, Xeon Gold 6149 @ 3.10GHz(16 cores), unknown brand HDD:

Single goroutine (1000000 writes), GOMAXPROCS(1): 3.183363298s
100000 goroutines (each makes 10 writes), GOMAXPROCS(1): 3.992387381s
100000 goroutines (each makes 10 writes), GOMAXPROCS(2): 3.96368545s
100000 goroutines (each makes 10 writes), GOMAXPROCS(4): 3.936505055s
100000 goroutines (each makes 10 writes), GOMAXPROCS(8): 3.843349416s
100000 goroutines (each makes 10 writes), GOMAXPROCS(16): 3.864881766s

About 250 ~ 312 thousand writes per second.

Documentation

Overview

Package logger is a goroutine-safe logging facility which writes logs with different severity levels to files, console, or both. Logs with different severity levels are written to different logfiles.

It's recommended to use logger.Init to create a global Logger object, then use logger.Info/Infof/Warn/Warnf... to write logs.

logger.New can be use to create as many Logger objects as desired if in need.

For a quick reference about this package's features and performance, please refer to the associated README.md.(https://github.com/antigloss/go/blob/master/logger/README.md)

Index

Examples

Constants

View Source
const (
	LogDestBoth = LogDestFile | LogDestConsole // Write logs both to files and console.
)

Variables

This section is empty.

Functions

func Error

func Error(args ...interface{})

Error uses the global Logger object created by Init to write a log with error level.

func Errorf

func Errorf(format string, args ...interface{})

Errorf uses the global Logger object created by Init to write a log with error level.

func Fatal

func Fatal(args ...interface{})

Fatal uses the global Logger object created by Init to write a log with fatal level followed by a call to os.Exit(-1).

func Fatalf

func Fatalf(format string, args ...interface{})

Fatalf uses the global Logger object created by Init to write a log with fatal level followed by a call to os.Exit(-1).

func Info

func Info(args ...interface{})

Info uses the global Logger object created by Init to write a log with info level.

func Infof

func Infof(format string, args ...interface{})

Infof uses the global Logger object created by Init to write a log with info level.

func Init

func Init(cfg *Config) (err error)

Init is used to create the global Logger object with cfg. It must be called once and only once before any other function backed by the global Logger object can be used. It returns nil if all goes well, otherwise it returns the corresponding error.

Example (GlobalLoggerObject)

This example shows how to write logs with the global Logger object.

package main

import (
	"github.com/antigloss/go/logger"
)

func main() {
	// Create the global Logger object
	err := logger.Init(&logger.Config{
		LogDir:          "./logs",
		LogFileMaxSize:  200,
		LogFileMaxNum:   500,
		LogFileNumToDel: 50,
		LogLevel:        logger.LogLevelInfo,
		LogDest:         logger.LogDestFile,
		Flag:            logger.ControlFlagLogLineNum,
	})
	if err != nil {
		panic(err)
	}
	logger.Info(123, 456.789, "abc", 432)
	logger.Infof("This is an %s", "example.")
}
Output:

func Panic

func Panic(args ...interface{})

Panic uses the global Logger object created by Init to write a log with panic level followed by a call to panic("Panicf").

func Panicf

func Panicf(format string, args ...interface{})

Panicf uses the global Logger object created by Init to write a log with panic level followed by a call to panic("Panicf").

func SetLogLevel

func SetLogLevel(logLevel LogLevel)

SetLogLevel is used to tell the global Logger object created by Init not to write logs below logLevel.

func Trace

func Trace(args ...interface{})

Trace uses the global Logger object created by Init to write a log with trace level.

func Tracef

func Tracef(format string, args ...interface{})

Tracef uses the global Logger object created by Init to write a log with trace level.

func Warn

func Warn(args ...interface{})

Warn uses the global Logger object created by Init to write a log with warning level.

func Warnf

func Warnf(format string, args ...interface{})

Warnf uses the global Logger object created by Init to write a log with warning level.

Types

type Config

type Config struct {
	// Directory to hold the log files. If left empty, current working directory is used.
	// Should you need to create multiple Logger objects, better to associate them with different directories.
	LogDir string
	// Name of a log file is formatted as `LogFilenamePrefix.LogLevel.DateTime.log`.
	// 3 placeholders are pre-defined: %P, %H and %U. When used in the prefix,
	// %P will be replaced with the program's name, %H will be replaced with hostname,
	// and %U will be replaced with username.
	// If LogFilenamePrefix is left empty, it'll be defaulted to `%P.%H.%U`.
	// If you create multiple Logger objects with the same directory, you must associate them with different prefixes.
	LogFilenamePrefix string
	// Latest log files of each level are associated with symbolic links. Name of a symlink is formatted as `LogSymlinkPrefix.LogLevel`.
	// If LogSymlinkPrefix is left empty, it'll be defaulted to `%P.%U`.
	// If you create multiple Logger objects with the same directory, you must associate them with different prefixes.
	LogSymlinkPrefix string
	// Limit the maximum size in MB for a single log file. 0 means unlimited.
	LogFileMaxSize uint32
	// Limit the maximum number of log files under `LogDir`. `LogFileNumToDel` log files will be deleted if reached. <=0 means unlimited.
	LogFileMaxNum int
	// Number of log files to be deleted when `LogFileMaxNum` reached. <=0 means don't delete.
	LogFileNumToDel int
	// Don't write logs below `LogLevel`.
	LogLevel LogLevel
	// Where the logs are written.
	LogDest LogDest
	// How the logs are written.
	Flag ControlFlag
}

Config contains options for creating a new Logger object.

type ControlFlag

type ControlFlag int // ControlFlag controls how the logs are written. Use `|`(Or operator) to mix multiple flags.
const (
	ControlFlagLogThrough  ControlFlag = 1 << iota // Controls if logs with higher level are written to lower level log files.
	ControlFlagLogFuncName                         // Controls if function name is prepended to the logs.
	ControlFlagLogLineNum                          // Controls if filename and line number are prepended to the logs.
	ControlFlagLogDate                             // Controls if a date string formatted as '20201201' is prepended to the logs.
	ControlFlagNone        = 0
)

type LogDest

type LogDest uint32 // LogDest controls where the logs are written.
const (
	LogDestFile    LogDest = 1 << iota // Write logs to files.
	LogDestConsole                     // Write logs to console.
	LogDestNone    = 0                 // Don't write logs.
)

type LogLevel

type LogLevel int // LogLevel is used to exclude logs with lower level.
const (
	LogLevelTrace LogLevel = iota
	LogLevelInfo
	LogLevelWarn
	LogLevelError
	LogLevelPanic // Call panic() after log is written.
	LogLevelFatal // Call os.Exit(-1) after log is written.
	LogLevelCount // Number of different log levels.
)

type Logger

type Logger struct {
	// contains filtered or unexported fields
}

Logger can be used to write logs with different severity levels to files, console, or both. Logs with different severity levels are written to different files. It is goroutine-safe and supports the following features:

  1. Auto rotation: It'll create a new logfile whenever day changes or size of the current logfile exceeds the configured size limit.
  2. Auto purging: It'll delete some oldest logfiles whenever the number of logfiles exceeds the configured limit.
  3. Log-through: Logs with higher severity level will be written to all the logfiles with lower severity level.
  4. Log levels: 6 different levels are supported. Logs with different levels are written to different logfiles. By setting the Logger object to a higher log level, lower level logs will be filtered out.
  5. Logs are not buffered, they are written to logfiles immediately with os.(*File).Write().
  6. It'll create symlinks that link to the most current logfiles.

func New

func New(cfg *Config) (logger *Logger, err error)

New can be used to create as many Logger objects as desired, while the global Logger object created by Init should be enough for most cases. Should you need to create multiple Logger objects, better to associate them with different directories, at least with different filename prefixes(including symlink prefixes), otherwise they will not work properly.

Example (MultiLoggerObject)

This example shows how to create multiple Logger objects and write logs with them.

package main

import (
	"github.com/antigloss/go/logger"
)

func main() {
	lg1, err := logger.New(&logger.Config{
		LogDir:          "./logs1", // Better to associate different Logger object with different directory.
		LogFileMaxSize:  200,
		LogFileMaxNum:   500,
		LogFileNumToDel: 50,
		LogLevel:        logger.LogLevelInfo,
		LogDest:         logger.LogDestFile,
		Flag:            logger.ControlFlagLogLineNum,
	})
	if err != nil {
		panic(err)
	}
	defer lg1.Close() // Don't forget to close the Logger object when you've done with it.

	lg2, err := logger.New(&logger.Config{
		LogDir:          "./logs2", // Better to associate different Logger object with different directory.
		LogFileMaxSize:  200,
		LogFileMaxNum:   500,
		LogFileNumToDel: 50,
		LogLevel:        logger.LogLevelInfo,
		LogDest:         logger.LogDestFile,
		Flag:            logger.ControlFlagLogLineNum,
	})
	if err != nil {
		panic(err)
	}
	defer lg2.Close() // Don't forget to close the Logger object when you've done with it.

	lg1.Error(333, 444.55, "This", "is", "an", "example.")
	lg2.Warnf("This is %s %s %s", "yet", "another", "example.")
}
Output:

func (*Logger) Close

func (l *Logger) Close() error

Close should be call once and only once to destroy the Logger object.

func (*Logger) Error

func (l *Logger) Error(args ...interface{})

Error writes a log with error level.

func (*Logger) Errorf

func (l *Logger) Errorf(format string, args ...interface{})

Errorf writes a log with error level.

func (*Logger) Fatal

func (l *Logger) Fatal(args ...interface{})

Fatal writes a log with fatal level followed by a call to os.Exit(-1).

func (*Logger) Fatalf

func (l *Logger) Fatalf(format string, args ...interface{})

Fatalf writes a log with fatal level followed by a call to os.Exit(-1).

func (*Logger) Info

func (l *Logger) Info(args ...interface{})

Info writes a log with info level.

func (*Logger) Infof

func (l *Logger) Infof(format string, args ...interface{})

Infof writes a log with info level.

func (*Logger) Panic

func (l *Logger) Panic(args ...interface{})

Panic writes a log with panic level followed by a call to panic("Panic").

func (*Logger) Panicf

func (l *Logger) Panicf(format string, args ...interface{})

Panicf writes a log with panic level followed by a call to panic("Panicf").

func (*Logger) SetLogLevel

func (l *Logger) SetLogLevel(logLevel LogLevel)

SetLogLevel tells the Logger object not to write logs below `logLevel`.

func (*Logger) Trace

func (l *Logger) Trace(args ...interface{})

Trace writes a log with trace level.

func (*Logger) Tracef

func (l *Logger) Tracef(format string, args ...interface{})

Tracef writes a log with trace level.

func (*Logger) Warn

func (l *Logger) Warn(args ...interface{})

Warn writes a log with warning level.

func (*Logger) Warnf

func (l *Logger) Warnf(format string, args ...interface{})

Warnf writes a log with warning level.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL