Golang 记一次log日志不能输出的问题

Golang 记一次log日志不能输出的问题

由于线上运行程序过程中出现了log日志打印不出来的情况,分析代码模拟测试复现问题。由于没有锁住log变量的问题

目录结构如下

.
├── ccc
│   └── ccc.go
├── go.mod
├── go.sum
├── hhh
│   └── hhh.go
├── log
│   └── log.go
├── main.go
└── network-agent
    ├── network-agent.log -> network-agent.log.2020-11-06
    └── network-agent.log.2020-11-06

4 directories, 8 files

ccc.go

package ccc

import (
        "demo/log"
        "sync"
)

var logger = log.GetNetworkLog("ccc")

//var logger = capnslog.NewPackageLogger("network", "ccc")

//var logger = log.GetNetworkLog()

func PrintI(i int, wg *sync.WaitGroup) {
        defer wg.Done()
        //fmt.Printf("%p, %p \n", &logger, &logger.Mutex)
        logger.Infof("INFO :CCC", i)
}

func PrintE(i int, wg *sync.WaitGroup) {
        defer wg.Done()
        //fmt.Printf("%p, %p \n", &logger, &logger.Mutex)
        logger.Errorf("ERROR :CCC", i)
}
func PrintII(i int) {
        //fmt.Printf("%p, %p \n", &logger, &logger.Mutex)
        logger.Infof("INFO :CCC", i)
}

func PrintEE(i int) {
        //fmt.Printf("%p, %p \n", &logger, &logger.Mutex)
        logger.Errorf("ERROR :CCC", i)
}

hhh.go

package hhh

import (
        "demo/log"
        "sync"
)

var logger = log.GetNetworkLog("hhh")

//var logger = capnslog.NewPackageLogger("network", "hhh")

//var logger = log.GetNetworkLog()

func PrintI(i int, wg *sync.WaitGroup) {
        defer wg.Done()
        //fmt.Printf("%p, %p \n", &logger, &logger.Mutex)
        logger.Infof("INFO :HHH", i)
}
func PrintE(i int, wg *sync.WaitGroup) {
        defer wg.Done()
        //fmt.Printf("%p, %p \n", &logger, &logger.Mutex)
        logger.Errorf("ERROR :HHH", i)
}
func PrintII(i int) {
        //fmt.Printf("%p, %p \n", &logger, &logger.Mutex)
        logger.Infof("INFO :HHH", i)
}
func PrintEE(i int) {
        //fmt.Printf("%p, %p \n", &logger, &logger.Mutex)
        logger.Errorf("ERROR :HHH", i)
}

log.go

package log

import (
        "fmt"
        "io"
        "os"
        "path/filepath"
        "sync"
        "time"

        "github.com/coreos/pkg/capnslog"
        rotatelogs "github.com/lestrrat-go/file-rotatelogs"
)

type NetworkLog struct {
        sync.Mutex
        pkg    string
        format capnslog.Formatter
}

var networkLog = new(NetworkLog)

func init() {
        SetupLogging()
}

type stop struct {
        error
}

func Retry(attempts int, sleep time.Duration, fn func() error) error {
        if err := fn(); err != nil {
                if e, ok := err.(stop); ok {
                        return e.error
                }
                if attempts--; attempts > 0 {
                        fmt.Printf("retry func error: %s. attempts #%d after %s.", err.Error(), attempts, sleep)
                        time.Sleep(sleep)
                        return Retry(attempts, sleep, fn)
                }
                return err
        }
        return nil
}

func IsPathExist(path string) bool {
        _, err := os.Stat(path)
        if err == nil {
                return true
        }
        if os.IsNotExist(err) {
                return false
        }
        return false
}

func EnsureTree(path string) error {
        err := Retry(10, 2*time.Second, func() error {
                if !IsPathExist(path) {
                        err := os.MkdirAll(path, os.ModePerm)
                        if err != nil {
                                return err
                        }
                        return nil
                }
                return nil
        })
        return err
}

func GetWriter(filename string) io.Writer {
        writer, err := rotatelogs.New(
                filename+".%Y-%m-%d",
                rotatelogs.WithLinkName(filename),         // 生成软链,指向最新日志文
                rotatelogs.WithMaxAge(365*24*time.Hour),   // 文件最大保存时间
                rotatelogs.WithRotationTime(24*time.Hour), // 日志切割时间间隔
        )

        if err != nil {
                //ulog.Fatalf("config local file system logger error. %+v", errors.WithStack(err))
                fmt.Println("config local file system logger error")
        }
        return writer
}

func SetupLogging() {
        logfile := "/home/neil/go/src/demo/network-agent/network-agent.log"
        dirname := filepath.Dir(logfile)
        EnsureTree(dirname)
        //capnslog.SetFormatter(capnslog.NewPrettyFormatter(writer, true))
        networkLog.format = capnslog.NewPrettyFormatter(GetWriter(logfile), true)
        //capnslog.SetFormatter(capnslog.NewPrettyFormatter(GetWriter(logfile), true))
}

func (log *NetworkLog) Infof(format string, args ...interface{}) {
        fmt.Println("begin lock")
        log.Lock()
        fmt.Println("locked")
        defer func() {
                fmt.Println("begin unlock")
                log.Unlock()
                fmt.Println("unlocked")
        }()
        if log.format != nil {
                log.format.Format(log.pkg, capnslog.INFO, 2, fmt.Sprintf(format, args...))
        }
        //time.Sleep(time.Duration(2) * time.Second)
}

func (log *NetworkLog) Errorf(format string, args ...interface{}) {
        fmt.Println("begin lock")
        log.Lock()
        fmt.Println("locked")
        defer func() {
                fmt.Println("begin unlock")
                log.Unlock()
                fmt.Println("unlocked")
        }()
        if log.format != nil {
                log.format.Format(log.pkg, capnslog.ERROR, 2, fmt.Sprintf(format, args...))
        }
        //time.Sleep(time.Duration(2) * time.Second)
}

func (log *NetworkLog) Fatalf(format string, args ...interface{}) {
        fmt.Println(fmt.Sprintf(format, args...))
}

// 正确
func GetNetworkLog(pkg string) *NetworkLog {
        networkLog.pkg = pkg
        return networkLog
}

// 错误
//func GetNetworkLog() NetworkLog {
//return *networkLog
//}

main.go

package main

import (
        "demo/ccc"
        "demo/hhh"
        "fmt"
        "sync"
)

func main() {
        var wg sync.WaitGroup
        fmt.Println("---")

        //log.SetupLogging()
        for j := 0; j < 100; j++ {
                for i := 0; i < 4; i++ {
                        wg.Add(1)
                        switch i {
                        case 0:
                                fmt.Println("---", i)
                                go ccc.PrintI(i, &wg)
                        case 1:
                                fmt.Println("---", i)
                                go hhh.PrintI(i, &wg)
                        case 2:
                                fmt.Println("---", i)
                                go ccc.PrintE(i, &wg)
                        case 3:
                                fmt.Println("---", i)
                                go hhh.PrintE(i, &wg)
                        }
                }
        }

        wg.Wait()
}