纪念我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
}