golang 标准库log的封装并实现日志切分

都知道golang的log库可以拿来就用,但对于生产来说还不够,需要基于log库做点封装。除了这些还有其他问题,比如打印字段定制化,性能问题。本文不讨论太多,只对log进行简单封装,实现log的持久化、loglevel、日志切分等功能。

参考:

代码结构

具体代码

main入口

package main

import (
	lu "go-stdLog/log_utils"
)

func main()  {
	lgfile, err := lu.MustOpen("log.txt", "logs")
	if err != nil {
		lu.Error("Failed to open log file:" + err.Error())
	}

	// output to file
	lu.Config(lu.DEBUG, lgfile)
	// with rotate 
	// lu.ConfigWithLumberjack(lu.DEBUG, "log.txt")

	// balabala
	lu.Debug("msg")
	lu.Info("msg")
	lu.Warning("msg")
	lu.Error("msg")

}

log_utils.log

package log_utils

import (
	"fmt"
	"io/ioutil"
	"log"
	"mime/multipart"
	"os"
	"path"
	"runtime"
	"strings"
)

/*
 * reference: go原生log模块的简易封装 https://cloud.tencent.com/developer/article/1673063
 */
type Level int

const (
	DEBUG Level = iota
	INFO
	WARNING
	ERROR
	FATAL
)

var (
	logPrefix = ""
	levelFlags = []string{"DEBG", "INFO", "WARN", "ERRO", "FATL"}

	logger *log.Logger
	loggerf *log.Logger

	curLevel Level
	logfile *os.File
)

func init()  {
	curLevel = DEBUG
	logger = log.New(os.Stdout, "[Default] ", log.LstdFlags)
	logger.SetFlags(log.Ldate | log.Lmicroseconds | log.Llongfile)
}

func Println(l *log.Logger, v ...interface{})  {
	if l != nil {
		l.Output(3, fmt.Sprintln(v...))
	}
}

func Fatalln(l *log.Logger, v ...interface{})  {
	if l != nil {
		l.Output(3, fmt.Sprintln(v...))
		os.Exit(1)
	}
}

func setPrefix(level Level)  {
	// stdout
	logPrefix = fmt.Sprintf("[%s] ", levelFlags[level])
	logger.SetPrefix(logPrefix)
	// log file
	if loggerf != nil {
		loggerf.SetPrefix(logPrefix)
	}
}

func Debug(v ...interface{})  {
	setPrefix(DEBUG)
	if DEBUG >= curLevel {
		Println(logger, v)
		Println(loggerf, v)
	}

}

func Info(v ...interface{})  {
	setPrefix(INFO)
	if INFO >= curLevel {
		Println(logger, v)
		Println(loggerf, v)
	}

}

func Warning(v ...interface{})  {
	setPrefix(WARNING)
	if WARNING >= curLevel {
		Println(logger, v)
		Println(loggerf, v)
	}

}

func Error(v ...interface{})  {
	setPrefix(ERROR)
	if ERROR >= curLevel {
		Println(logger, v)
		Println(loggerf, v)
	}

}

func Fatal(v ...interface{})  {
	setPrefix(FATAL)
	if FATAL >= curLevel {
		Println(logger, v)
		Println(loggerf, v)
	}

}

// output to file
func Config(level Level, lfile *os.File)  {
	curLevel = level
	loggerf = log.New(lfile, "[default] ", log.LstdFlags)
	loggerf.SetFlags(log.Ldate | log.Lmicroseconds | log.Llongfile)
}

// output log && rotate
func ConfigWithLumberjack(level Level, filename string) {
	curLevel = level
	loggerf = log.New(getLumberjackLogger(filename), "[default] ", log.LstdFlags)
	loggerf.SetFlags(log.Ldate | log.Lmicroseconds | log.Llongfile)
}

func getLumberjackLogger(fielname string) *lumberjack.Logger {
	lumLogger := &lumberjack.Logger{
		Filename:   fielname,
		MaxSize:    1, // MB
		MaxBackups: 7, // backup cnt
		MaxAge:     7, // days
		Compress:   true,
	}

	return lumLogger
}

// support file log
func getSize(f multipart.File) (int, error) {
	content, err := ioutil.ReadAll(f)

	return len(content), err
}

func getExt(filename string) string {
	return path.Ext(filename)
}

func checkIsExist(src string) bool {
	_, err := os.Stat(src)
	return os.IsNotExist(err)
}

func checkPermission(src string) bool {
	_, err := os.Stat(src)
	return os.IsPermission(err)
}

func mkdir(src string) error {
	err := os.MkdirAll(src, os.ModePerm)
	if err != nil {
		return err
	}

	return nil
}

func NotExistMkDir(src string) error {
	if exist := checkIsExist(src); exist {
		if err := mkdir(src); err != nil {
			return err
		}
	}

	return nil
}

func lastDir(dir string) string {

	return ""
}

func compareString(s1, s2 string) bool {
	if len(s1) != len(s2) {
		return false
	}

	for i, _ := range s1 {
		if s1[i] != s2[i] {
			return false
		}
	}

	return true
}

func Open(name string, flag int, perm os.FileMode) (*os.File, error) {
	f, err := os.OpenFile(name, flag, perm)
	if err != nil {
		return nil, err
	}

	return f, nil
}

func MustOpen(filename, filepath string) (*os.File, error) {
	dir, err := os.Getwd()
	if err != nil {
		return nil, fmt.Errorf("os.Getwd err: %v", err)
	}

	osTyp := runtime.GOOS
	if compareString(osTyp, "windows") {
		dirs := strings.Split(dir, "\\")
		dir = strings.Join(dirs[:], "\\")
	}

	var src string
	var perm bool
	if compareString(osTyp, "windows") {
		src = dir + "\\" + filepath
		perm = checkPermission(src)
	} else {
		src = path.Join(dir, filepath)
		perm = checkPermission(src)
	}

	if perm {
		return nil, fmt.Errorf("file.CheckPermission Permission denied src: %s", src)
	}

	err = NotExistMkDir(src)
	if err != nil {
		return nil, fmt.Errorf("file.IsNotExistMkDir src: %s, err: %v", src, err)
	}
	// default windows system
	f, err := Open(src + "\\" + filename, os.O_APPEND | os.O_CREATE | os.O_RDWR, 0644)
	if err != nil {
		return nil, fmt.Errorf("Fail to OpenFile :%v", err)
	}

	return f, nil
}

日志切分用到lumberjack,以下是go.mod:

module go-stdLog

go 1.19

require (
	gopkg.in/natefinch/lumberjack.v2 v2.0.0
)

require (
	github.com/BurntSushi/toml v1.2.1 // indirect
	gopkg.in/yaml.v2 v2.4.0 // indirect
)

测试结果

console

log文件

以上就是基于标准库log的简单实践,一般应用或不考虑性能情况下,可以试试上log库。

posted on 2022-12-01 12:14  进击的davis  阅读(476)  评论(0编辑  收藏  举报

导航