7、log

Go语言标准库提供了基础的日志功能,通过log包可以快速实现日志记录。标准库log包的主要特点:

然而,标准库日志包存在一些局限性:

简单使用

logger会打印每条日志信息的日期、时间,默认输出到系统的标准错误。

Fatal系列函数会在写入日志信息后调用os.Exit(1)

Panic系列函数会在写入日志信息后panic

package main

import "log"

func main() {
	log.Println("this is a simple log")
	log.Fatalln("this is a fatal log")
	log.Panicln("this is a panic log")
}

配置logger

默认情况下的logger只会提供日志的时间信息,但是很多情况下我们希望得到更多信息,比如记录该日志的文件名和行号等。log标准库中为我们提供了定制这些设置的方法。

log标准库中的Flags函数会返回标准logger的输出配置,而SetFlags函数用来设置标准logger的输出配置。

func Flags() int
func SetFlags(flag int) 

flag选项

控制输出日志信息的细节,不能控制输出的顺序和格式。

const (
    Ldate         = 1 << iota     // 日期:2009/01/23
    Ltime                         // 时间:01:23:23
    Lmicroseconds                 // 微秒级别的时间:01:23:23.123123(用于增强Ltime位)
    Llongfile                     // 文件全路径名+行号: /a/b/c/d.go:23
    Lshortfile                    // 文件名+行号:d.go:23(会覆盖掉Llongfile)
    LUTC                          // 使用UTC时间
    LstdFlags     = Ldate | Ltime // 标准logger的初始值
) 
package main

import "log"

func main() {
	log.SetFlags(log.Llongfile | log.Lmicroseconds | log.Ldate)
	log.Println("this is a simple log")
	log.Fatalln("this is a fatal log")
	log.Panicln("this is a panic log")
}

//2022/12/02 11:15:17.686667 E:/go-project/nginx-log-parse/cmd/main.go:7: this is a simple log
//2022/12/02 11:15:17.695173 E:/go-project/nginx-log-parse/cmd/main.go:8: this is a fatal log

配置日志前缀

func Prefix() string
func SetPrefix(prefix string) 
package main

import "log"

func main() {
	log.SetFlags(log.Llongfile | log.Lmicroseconds | log.Ldate)
	log.SetPrefix("[SIMPLE]")
	log.Println("this is a simple log")
	log.SetPrefix("[FATAL]")
	log.Fatalln("this is a fatal log")
	log.SetPrefix("[PANIC]")
	log.Panicln("this is a panic log")
}

//[SIMPLE]2022/12/02 11:17:05.758862 E:/go-project/nginx-log-parse/cmd/main.go:8: this is a simple log
//[FATAL]2022/12/02 11:17:05.767646 E:/go-project/nginx-log-parse/cmd/main.go:10: this is a fatal log

配置日志输出位置

func SetOutput(w io.Writer)

创建logger

func New(out io.Writer, prefix string, flag int) *Logger

输出到文件

package main

import (
    "log"
    "os"
)

func main() {
    // 默认日志输出到标准错误
    log.Println("这是一条标准日志消息")
    
    // 创建日志文件
    f, err := os.OpenFile("app.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()
    
    // 配置日志输出到文件
    logger := log.New(f, "APP: ", log.Ldate|log.Ltime|log.Lshortfile)
    
    // 使用配置好的logger记录日志
    logger.Println("应用启动成功")
    logger.Printf("配置加载完成: %s", "config.json")
    
    // 记录致命错误并终止程序
    if err != nil {
        logger.Fatalf("无法连接数据库: %v", err)
    }
}

自定义logger

package main

import (
	"fmt"
	"log"
	"os"
	"runtime"
	"strings"
	"time"
)

// CustomLogger 自定义日志格式
type CustomLogger struct {
	logger *log.Logger
}

const (
	LevelDebug   = "DEBUG"
	LevelInfo    = "INFO"
	LevelWarning = "WARNING"
	LevelError   = "ERROR"
	LevelFatal   = "FATAL"
)

// NewCustomLogger 初始化自定义日志
func NewCustomLogger() *CustomLogger {
	return &CustomLogger{
		logger: log.New(os.Stdout, "", 0), // 输出到标准输出,不带前缀
	}
}

// 自定义日志格式
func (l *CustomLogger) formatLog(level, message string) string {
	// 获取当前时间
	now := time.Now().Format("2006-01-02 15:04:05.000")

	// 获取调用栈信息
	pc, file, line, ok := runtime.Caller(3) // 跳过三层调用栈
	caller := "unknown"
	if ok {
		funcName := runtime.FuncForPC(pc).Name()
		caller = fmt.Sprintf("%s:%d", file, line)
		if index := strings.LastIndex(funcName, "."); index != -1 {
			caller = fmt.Sprintf("%s.%s:%d", funcName[:index], funcName[index+1:], line)
		}
	}

	// 构建日志格式
	return fmt.Sprintf("%s [%s] %s - %s",
		now, level, caller, message)
}

// Log 日志输出函数
func (l *CustomLogger) Log(level, message string) {
	logLine := l.formatLog(level, message)
	l.logger.Println(logLine)
}

func (l *CustomLogger) Debug(message string) {
	l.Log(LevelDebug, message)
}

func (l *CustomLogger) Debugf(format string, v ...any) {
	message := fmt.Sprintf(format, v...)
	l.Log(LevelDebug, message)
}

func (l *CustomLogger) Info(message string) {
	l.Log(LevelInfo, message)
}

func (l *CustomLogger) Infof(format string, v ...any) {
	message := fmt.Sprintf(format, v...)
	l.Log(LevelInfo, message)
}

func (l *CustomLogger) Warning(message string) {
	l.Log(LevelWarning, message)
}

func (l *CustomLogger) Warningf(format string, v ...any) {
	message := fmt.Sprintf(format, v...)
	l.Log(LevelWarning, message)
}

func (l *CustomLogger) Error(message string) {
	l.Log(LevelError, message)
}

func (l *CustomLogger) Errorf(format string, v ...any) {
	message := fmt.Sprintf(format, v...)
	l.Log(LevelError, message)
}

func (l *CustomLogger) Fatal(message string) {
	l.Log(LevelFatal, message)
}

func (l *CustomLogger) Fatalf(format string, v ...any) {
	message := fmt.Sprintf(format, v...)
	l.Log(LevelFatal, message)
}