用Go编写封装自己的日志模块

6 minute read

今天为大家带来一篇用Go如何封装自己的日志模块。orz~

既然是日志模块,第一步肯定是定义日志级别,不然什么消息都打印,对资源消耗很大。 这里使用iota常量计数器定义了四个级别,使用iota能简化定义,在定义枚举时很有用。

 1### 定义日志级别
 2const (
 3	LevelError = iota
 4	LevelWarning
 5	LevelInfo
 6	LevelDebug
 7)
 8//定义日志结构体
 9type logging struct {
10	level int
11	file  *os.File
12}
13
14//定义logging类型变量
15var logFile logging

等级设置我们使用了一个函数.参数必须为int类型

1func setLevel(level int) {
2	logFile.level = level
3}

初始化

 1//Logger类型表示一个活动状态的记录日志的对象,它会生成一行行的输出写入一个io.Writer接口。
 2//每一条日志操作会调用一次io.Writer接口的Write方法。Logger类型的对象可以被多个线程安全的同时使用,它会保证对io.Writer接口的顺序访问。
 3var loggerf *log.Logger
 4//初始化
 5func init() {
 6	//初始化级别为LevelDebug
 7	logFile.level = LevelDebug
 8	//这块直接 log.New
 9	//创建一个Logger。参数out设置日志信息写入的目的地。参数prefix会添加到生成的每一条日志前面。参数flag定义日志的属性(时间、文件等等)
10	//`prefix`在这块留空,下面输出函数可以设置为级别,减少了代码的冗杂
11	loggerf = log.New(os.Stdout, "", log.Llongfile|log.Ltime|log.Ldate)
12}

输出函数定义

 1//获取当前函数所在的路径,文件名和行号
 2// func CallerFunc() string {
 3// 	filename, file, line, ok := runtime.Caller(0)
 4// 	if !ok {
 5// 		return ""
 6// 	}
 7// 	return "FileName:" + runtime.FuncForPC(filename).Name() + " Path:" + file + " Line:" + strconv.Itoa(line)
 8// }
 9// 当时`info`级别想要输出文件名,路径,错误行号等用了上面的函数,结果周师傅说,`log`包里面有个New函数,里面可以直接定义上面需要的信息
10//按照格式打印日志
11func Debugf(format string, v ...interface{}) {
12	if logFile.level >= LevelDebug {
13		//log里的函数都自带有mutex
14		//这里获取一次锁
15		loggerf.Printf("[DEBUG] "+format, v...)
16	}
17}
18
19//Infof
20func Infof(format string, v ...interface{}) {
21	if logFile.level >= LevelInfo {
22		loggerf.Printf("[INFO] "+format, v...)
23	}
24}
25
26//Warningf
27func Warningf(format string, v ...interface{}) {
28	if logFile.level >= LevelWarning {
29		loggerf.Printf("[WARNING] "+format, v...)
30	}
31}
32
33//Errorf
34func Errorf(format string, v ...interface{}) {
35	if logFile.level >= LevelError {
36		loggerf.Printf("[ERROR] "+format, v...)
37	}
38}
39
40//标准输出
41//Println存在输出会换行
42//这里采用Print输出方式
43func Debug(v ...interface{}) {
44	if logFile.level >= LevelDebug {
45		loggerf.Print("[DEBUG] " + fmt.Sprintln(v...))
46	}
47}
48
49//Info
50func Info(v ...interface{}) {
51	if logFile.level >= LevelInfo {
52		loggerf.Print("[INFO] " + fmt.Sprintln(v...))
53	}
54}
55
56//Warning
57func Warning(v ...interface{}) {
58	if logFile.level >= LevelWarning {
59		loggerf.Print("[DEBUG] " + fmt.Sprintln(v...))
60	}
61}
62
63//Error
64func Error(v ...interface{}) {
65	if logFile.level >= LevelError {
66		loggerf.Print("[DEBUG] " + fmt.Sprintln(v...))
67	}
68}

输出到文件

 1//获取当前程序所在的路径
 2func getwd() (string, error) {
 3	path, err := os.Getwd()
 4	if err != nil {
 5		return "", errors.New(fmt.Sprintln("Getwd Error:", err))
 6	}
 7	filepathstr := filepath.Join(path, "logs")
 8	err = os.MkdirAll(filepathstr, 0755)
 9	if err != nil {
10		return "", errors.New(fmt.Sprintln("Create Dirs Error:", err))
11	}
12	return filepathstr, nil
13}
14//创建日志文件
15//制定创建路径和日志名
16func Init(path, logname string, level int) error {
17	if path == "" {
18		filePath, _ := getwd()
19		filepathstr := filepath.Join(filePath, logname)
20		logfile, err := os.Create(filepathstr)
21		if err != nil {
22			return errors.New(fmt.Sprintln("Create File Error:", err))
23		}
24		logFile.file = logfile
25		setLevel(level)
26	} else if logname == "" {
27		filepathstr := path
28		err := os.MkdirAll(filepathstr, 0644)
29		if err != nil {
30			return errors.New(fmt.Sprintln("Create Dirs Error:", err))
31		}
32		logname = filepath.Join(filepathstr, "logs.log")
33		logfile, err := os.Create(logname)
34		if err != nil {
35			return errors.New(fmt.Sprintln("Create File Error:", err))
36		}
37		logFile.file = logfile
38		setLevel(level)
39	} else {
40		filepathstr := path
41		err := os.MkdirAll(filepathstr, 0644)
42		if err != nil {
43			return errors.New(fmt.Sprintln("Create Dirs Error:", err))
44		}
45		logname = filepath.Join(filepathstr, logname)
46		logfile, err := os.Create(logname)
47		if err != nil {
48			return errors.New(fmt.Sprintln("Create File Error:", err))
49		}
50		logFile.file = logfile
51		setLevel(level)
52	}
53	//log.Llongfile|log.Ltime|log.Ldata 返回文件信息,时间和代码信息不需要重新写函数获取
54	//初始化一个*log.Logger
55	loggerf = log.New(logFile.file, "", log.Llongfile|log.Ltime|log.Ldate)
56	return nil
57}

以上代码大家可以再整合或者再次封装,可能会更方便快捷。我这里只是提供了一个我自己在封装日志模块所考虑的地方与最后的思路. 详细代码地址