登录

纪念我2026年废弃的第一个封装,zap替换自开发异步写入,留念一下

Passion Coding GooseForum
2026-01-01 00:59:25

之前写的一个异步日志。准备使用zap代替。

package asyncwrite

import (
	"bufio"
	"context"
	"fmt"
	"time"

	"gopkg.in/natefinch/lumberjack.v2"
)

type AsyncW struct {
	dataChan    chan []byte
	closeFinish chan bool
}

func AsyncLumberjackBufIo(io *lumberjack.Logger) *AsyncW {
	r := &AsyncW{
		dataChan:    make(chan []byte, 256),
		closeFinish: make(chan bool),
	}
	w := bufio.NewWriterSize(io, 1024*16)
	go func() {
		defer func() {
			if err := w.Flush(); err != nil {
				fmt.Println(err)
			}
			r.closeFinish <- true
		}()
		ticker := time.NewTicker(time.Millisecond * 100)
		defer ticker.Stop()
		for {
			select {
			case val, ok := <-r.dataChan:
				if !ok {
					return
				}
				_, err := w.Write(val)
				if err != nil {
					fmt.Println(err)
				}
			case <-ticker.C:
				err := w.Flush()
				if err != nil {
					fmt.Println(err)
				}
			}
		}
	}()
	return r
}

func (itself *AsyncW) Write(p []byte) (n int, err error) {
	data := make([]byte, len(p))
	copy(data, p)
	itself.dataChan <- data
	return len(p), nil
}

func (itself *AsyncW) Stop(ctx context.Context) {
	close(itself.dataChan)
	select {
	case <-ctx.Done():
		return
	case <-itself.closeFinish:
		return
	}
}

package logging

import (
	"context"
	"fmt"
	"io"
	"log/slog"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/leancodebox/GooseForum/app/bundles/asyncwrite"
	"github.com/leancodebox/GooseForum/app/bundles/fileopt"
	"github.com/leancodebox/GooseForum/app/bundles/preferences"
	"github.com/leancodebox/GooseForum/app/bundles/setting"
)

const (
	LogTypeStdout = "stdout"
	LogTypeFile   = "file"
)

func ErrIf(err error) bool {
	if err != nil {
		slog.Error(err.Error())
		return true
	}
	return false
}

var (
	debug      = setting.IsDebug()
	logType    = preferences.Get("log.type", LogTypeStdout)
	logPath    = preferences.Get("log.path", "./storage/logs/run.log")
	rolling    = preferences.GetBool("log.rolling", false)
	maxAge     = preferences.GetInt("log.maxAge", 30)
	maxSize    = preferences.GetInt("log.maxSize", 1024)
	maxBackUps = preferences.GetInt("log.maxBackUps", 1024)
)

var aw *asyncwrite.AsyncW
var once = new(sync.Once)

func init() {
	Init()
}

func Init() {
	once.Do(func() {
		var logOut io.Writer
		logOut = os.Stdout
		switch logType {
		default:
			slog.Info("Unknown Log Output Type")
		case LogTypeStdout:
		case LogTypeFile:
			if rolling {
				logOut = getAsyncFileIoRolling()
			} else {
				logOut = getFileIo()
			}
		}
		logLevel := slog.LevelInfo
		if debug {
			logLevel = slog.LevelDebug
		}
		slog.SetDefault(slog.New(slog.NewJSONHandler(logOut, &slog.HandlerOptions{
			AddSource:   true,
			ReplaceAttr: replace,
			Level:       logLevel,
		})))
	})
}

func getFileIo() *os.File {
	logOut := os.Stdout
	if err := fileopt.IsExistOrCreate(logPath, ""); err != nil {
		slog.Info("Create log file error", "err", err)

	}
	logOut, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		slog.Info("Failed to log to file, using default stderr", "err", err)
	}
	return logOut
}

func getAsyncFileIoRolling() *asyncwrite.AsyncW {
	aw = asyncwrite.AsyncLumberjackBufIo(&lumberjack.Logger{
		Filename:   logPath,
		MaxSize:    maxSize,    // megabytes
		MaxBackups: maxBackUps, // maxBackUps
		MaxAge:     maxAge,     //days
		LocalTime:  true,
		Compress:   false, // disabled by default
	})
	return aw
}

var rootDir = getRootDir()

func getRootDir() string {
	_, file, _, ok := runtime.Caller(1)
	if !ok {
		return "???"
	}

	for i := 0; i < 3; i++ {
		file = filepath.Dir(file)
	}
	return strings.ReplaceAll(file, "\\", "/")
}

func replace(groups []string, a slog.Attr) slog.Attr {
	switch {
	case a.Key == slog.SourceKey:
		if source, ok := a.Value.Any().(*slog.Source); ok {
			source.File = strings.TrimPrefix(source.File, rootDir+"/")
			a.Value = slog.StringValue(fmt.Sprintf("%v:%v %v", source.File, source.Line, source.Function))
		}
	case a.Key == slog.TimeKey:
		if item, ok := a.Value.Any().(time.Time); ok {
			a.Value = slog.StringValue(item.Format("2006-01-02 15:04:05.999999999"))
		}
	}
	return a
}

func Shutdown() {
	if aw == nil {
		return
	}
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	aw.Stop(ctx)
	slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
		AddSource:   true,
		ReplaceAttr: replace,
	})))
	slog.Info("logging 👋")
	aw = nil
}
abandon1a2b 楼主
2026-01-01 02:50:26

mark

加入讨论

登录或注册以发表评论