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库。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
2021-12-01 0139-单词拆分
2021-12-01 0279-完全平方数
2021-12-01 0322-零钱兑换