1. Methods in log
1.1 Log
Print
/Printf
/Println
: call Output to print to the standard logger.Panic
/Panicf
/Panicln
: equal toPrint
followed by a call topanic()
.Fatal
/Fatalf
/Fatalln
: equal toPrint
followed by a call toos.Exist(1)
.
example:
1type User struct {2 Name string3 Age int4}5func main() {6 u := User{7 Name: "Alice",8 Age: 18,9 }10 log.Printf("%s login, age:%d", u.Name, u.Age)11 // **output:**12 // 2024/11/23 19:04:00 Alice login, age:1813
14 log.Panicf("Oh, system error when %s login", u.Name)15 // **output:**10 collapsed lines
16 // panic: Oh, system error when Alice login17 //18 // goroutine 1 [running]:19 // log.Panicf({0x104d19fa4?, 0x5?}, {0x1400010cf28?, 0x0?, 0x1400010cf38?})20
21 log.Fatalf("Danger! hacker %s login", u.Name)22 // **output:**23 // 2024/11/23 19:06:45 Danger! hacker Alice login24 // exit status 125}
1.2 Custom
0) logger struct
1type Logger struct {2 outMu sync.Mutex3 out io.Writer // destination for output4
5 prefix atomic.Pointer[string] // prefix on each line to identify the logger6 flag atomic.Int32 // flags which control various aspects of the logger's behavior7 isDiscard atomic.Bool // whether the logger should discard log messages8}
1) flags
The log library provides these predefined flags to prefix to each log entry generated by the Logger.
1const (2 Ldate = 1 << iota // the date in the local time zone: 2009/01/233 Ltime // the time in the local time zone: 01:23:234 Lmicroseconds // microsecond resolution: 01:23:23.123123. assumes Ltime.5 Llongfile // full file name and line number: /a/b/c/d.go:236 Lshortfile // final file name element and line number: d.go:23. overrides Llongfile7 LUTC // if Ldate or Ltime is set, use UTC rather than the local time zone8 Lmsgprefix // move the "prefix" from the beginning of the line to before the message9 LstdFlags = Ldate | Ltime // initial values for the standard logger10)
example:
1log.SetFlags(log.Llongfile | log.LstdFlags)2log.Printf("%s login, age:%d", u.Name, u.Age)3
4// **output:**5// 2024/11/23 19:16:09 main.go:19: Alice login, age:186
7log.SetFlags(log.Lshortfile | log.Ldate | log.Lmicroseconds)8log.Printf("%s login, age:%d", u.Name, u.Age)9// **output:**10// 2024/11/23 19:17:53.498562 main.go:19: Alice login, age:18
2) prefix of log message
The log library provides a SetPrefix
method to allow users to set the output prefix for the standard logger:
1log.SetPrefix("[app]: ")2log.SetFlags(log.Lshortfile | log.Ldate | log.Lmicroseconds)3log.Printf("%s login, age:%d", u.Name, u.Age)4
5// **output:**6// [app]: 2024/11/23 19:19:47.075523 main.go:21: Alice login, age:18
3) log file
The log library also provdies a SetOutput
method to set the output destination for the standard logger:
1logFile, err := os.OpenFile("./output.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)2if err != nil {3 fmt.Println("open log file failed, err:", err)4 return5}6
7log.SetPrefix("[app]: ")8log.SetFlags(log.Lshortfile | log.Ldate | log.Lmicroseconds)9// the log message would be written to ./output.log, instead of the default CLI10log.SetOutput(logFile)11
12log.Printf("%s login, age:%d", u.Name, u.Age)13log.Printf("Hello, %s", u.Name)
4) create a custom logger
The New
method creates a new Logger
.
Syntax: func New(out io.Writer, prefix string, flag int) *Logger
1// the following logger behaves the same as the one above, except the prefix is "[new]: "2newLogger := log.New(logFile, "[new]: ", log.Lshortfile|log.Ldate|log.Lmicroseconds)3newLogger.Printf("Hello, %s", u.Name)
If you want to write to multiple places, you can use io.MultiWriter
:
1writer1 := os.Stdout2writer2, err := os.OpenFile(logFileName, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)3if err != nil {4 log.Fatalf("create file log.txt failed: %v", err)5}6
7logger := log.New(io.MultiWriter(writer1, writer2), "[logger]: ", log.Lshortfile|log.LstdFlags)8logger.Printf("%s login, age:%d", u.Name, u.Age)
2. More customs
Sometimes, we want to categorize and prioritize log messages based on their severity and importance by different levels, like Debug
, Info
, Error
, etc. Here how we can achieve that using the log library:
1var logFileName = "./output2.log"2var (3 InfoLogger *log.Logger4 WarnLogger *log.Logger5 DebugLogger *log.Logger6 ErrorLogger *log.Logger7 FatalLogger *log.Logger8)9
10func init() {11 logFile, err := os.OpenFile(logFileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)12 if err != nil {13 log.Println("Unable to create Logger file:", err.Error())14 return15 }18 collapsed lines
16 log.SetOutput(logFile)17 logFlag := log.Ldate | log.Ltime | log.Lshortfile18 InfoLogger = log.New(logFile, "Info:", logFlag)19 WarnLogger = log.New(logFile, "Warn:", logFlag)20 DebugLogger = log.New(logFile, "Debug:", logFlag)21 ErrorLogger = log.New(logFile, "Error:", logFlag)22 FatalLogger = log.New(logFile, "Fatal:", logFlag)23}24
25func main() {26 InfoLogger.Println("Starting Service!")27 t := time.Now()28 InfoLogger.Printf("Time taken: %s \n", time.Since(t))29 DebugLogger.Println("Debug: Service is running!")30 WarnLogger.Println("Warning: Service is about to shutdown!")31 ErrorLogger.Println("Error: Service is shutting down!")32 FatalLogger.Println("Fatal: Service shut down!")33}
Here comes the problems and limitation in functionalities:
- The setup is complex and hard to maintain.
- Not natively support different log levels.
- Not support structured data (e.g. JSON).
- Not support log filtering by log levels and sources.
- Limited ability to customize log messages.
Thus, we might need to use other logger libraries, like slog and logrus.
To see the full example of the code, checkout this link: https://github.com/jidalii/go-playground/tree/main/log