Golang - 日志库实战

博客提及了目录结构,包含调用文件和日志库文件,还列举了日志库相关的文件,如 mylogger.go、mylogger_output.go、mylogger_util.go 等,与信息技术中的文件调用和日志库相关。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


目录结构


| - myLog
	| - mylog.go
	| - file_log.go
| - 项目目录
	| - main.go 
	
"""
使用:
	设置日志路径, 日志名, 日志切割大小, 日志记录等级
	实例化后, 即可使用
"""

调用文件

package main

import (
	"xx/xx/mylogger"
)

const (
	// 日志文件路径
	filePath string = "./"
	// 日志记录的服务名
	srvName string = "hellen"
	// 文件记录日志的等级
	fileLevel string = "Warning"
	// 终端记录日志的等级
	consoleLevel string = "Debug"
	// 日志文件大小上限
	maxSize int64 = 1 << 10 // 1024B --> 1KB
)

// logger是一个接口类型, 这个接口实现了debug,info,warning,error,fatal,close方法
var logger mylogger.Logger

func main() {
	// 实例化一个日志接口
	logger = mylogger.NewLogSet(filePath, srvName, fileLevel, consoleLevel, maxSize)

	// 教学中举例的时候, 日志记录和终端记录是两个不同的结构体
	// 所以创建一个logger接口, 这样可以保证实例化日志记录和终端记录时, 都可以使用
	// 但是我写的这个, 结构体并没有区分日志记录和终端记录, 所以这个接口也可以不创建, 看官们不必纠结

	// 延迟关闭
	defer logger.Close()

	// 测试用
	logger.Debug("用户 ss 尝试登录 %d 次", 1)   // 输出到屏幕, 不记录到文件
	logger.Info("用户 ss 尝试登录 %d 次", 2)    // 同上
	logger.Warning("用户 ss 尝试登录 %d 次", 3) // 输出到屏幕, 记录到acc_log
	logger.Error("用户 ss 尝试登录 %d 次", 4)   // 输出到屏幕,记录到所有log
	logger.Fatal("用户 ss 尝试登录 %d 次", 5)   // 输出到屏幕,记录到所有log
}

日志库文件

  • mylogger.go
package mylogger

// 我的日志库

// Level 自定义类型 代表日志级别
type Level uint16

const (
	// Debug 代表debug级别
	Debug Level = iota // Debug Level类型
	// Info 代表info级别
	Info
	// Warning 代表warning级别
	Warning
	// Error 代表Error级别
	Error
	// Fatal 代表Critical级别
	Fatal
)

// Logger 定义一个接口
type Logger interface {
	Debug(format string, args ...interface{})
	Info(format string, args ...interface{})
	Warning(format string, args ...interface{})
	Error(format string, args ...interface{})
	Fatal(format string, args ...interface{})
	Close()
}

  • mylogger_output.go
package mylogger

import (
	"fmt"
	"os"
	"path"
	"time"
)

// LogSet 日志文件结构体
type LogSet struct {
	fileLevel Level    // 记录到文件的级别
	conLevel  Level    // 打印到终端的级别
	filePath  string   // 日志文件的路径
	srvName   string   // 服务名
	accFile   *os.File // acclog的文件句柄
	errFile   *os.File // errlog的文件句柄
	maxSize   int64    // 日志文件最大大小
}

// NewLogSet LogSet的构造函数
// 日志存放路径, 服务名称, 文件日志等级, 终端日志等级, 文件日志大小上限
func NewLogSet(filepath, srvName, fileLevelStr, conLevelStr string, maxSize int64) *LogSet {
	// 根据配置文件中填写的Str, 获取对应Level
	fileLevel := strToLevel(fileLevelStr) // 该方法在mylogger_util.go中, 将用户配置的字符串等级转换成自定义的Level类型
	conLevel := strToLevel(conLevelStr)

	l := &LogSet{
		fileLevel: fileLevel,
		conLevel:  conLevel,
		filePath:  filepath,
		srvName:   srvName,
		maxSize:   maxSize,
	}
	l.accFile, l.errFile = l.initFile() // 把文件句柄赋值给对应的结构体
	return l                            // 返回一个完整的结构体
}

// 创建文件句柄
func (l *LogSet) initFile() (*os.File, *os.File) {
	// logName := path.Join(f.filePath,f.srvName) // 同python, 用分隔线,将前后两个str连接
	accLogName := fmt.Sprintf("%s/%s_acc.log", l.filePath, l.srvName)
	f1, err1 := os.OpenFile(accLogName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)

	if err1 != nil {
		panic(fmt.Errorf("open file %s failed, %v", accLogName, err1))
	}

	errLogName := fmt.Sprintf("%s/%s_err.log", l.filePath, l.srvName)
	f2, err2 := os.OpenFile(errLogName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)

	if err2 != nil {
		panic(fmt.Errorf("open file %s failed, %v", errLogName, err2))
	}

	return f1, f2
}

// 检查log大小, 超过大小就切分
func (l *LogSet) checkLogSize(f *os.File, mod string) {
	// 判断文件大小, 超过size重建日志文件
	LogInfo, err := f.Stat()

	if err != nil {
		return
	}

	now := time.Now().Format("20060102150405")
	LogSize := LogInfo.Size()

	if LogSize > l.maxSize {
		// 关闭文件句柄
		f.Close()

		// /xx/xx/xx/srvName_mod.log, /xx/xx/xx/srvName_mod.now.log
		a := path.Join(l.filePath, l.srvName)
		oldName := fmt.Sprintf("%s_%s.log", a, mod)
		newName := fmt.Sprintf("%s_%s.%s.log", a, mod, now)
		os.Rename(oldName, newName) // 重命名

		f, err := os.OpenFile(oldName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)

		if err != nil {
			panic(fmt.Errorf("open file %s failed, %v", oldName, err))
		}

		switch mod {
		case "acc":
			l.accFile = f
		case "err":
			l.errFile = f
		}
	}
}

// 格式化日志
func (l *LogSet) logFormat(level Level, format string, args ...interface{}) {
	// 日志格式: [时间][文件:行号][函数名][级别] 日志信息
	now := time.Now().Format("2006-01-02 15:04:05.000") // 时间
	fileName, funcName, line := getCallerInfo(3)
	levelStr := getLevelStr(level)
	logMsg := fmt.Sprintf("[%s][%s:%d][%s][%v] %s\n", now, fileName, line, funcName, levelStr, format)

	// 如果 conlevel > 日志等级, 打印到终端
	if l.conLevel <= level {
		fmt.Fprintf(os.Stdout, logMsg, args...)
	}

	if l.fileLevel <= level {
		// 判断acc_log是否过大
		l.checkLogSize(l.accFile, "acc")
		// 使用Fprintf将日志写入到accesslog中
		fmt.Fprintf(l.accFile, logMsg, args...)

		// 判断err_log是否过大
		l.checkLogSize(l.errFile, "err")
		// 如果是Error以上的日志还需要记录到err_log中
		if level > Warning {
			fmt.Fprintf(l.errFile, logMsg, args...)
		}
	}

}

// Debug 处理debug日志
func (l *LogSet) Debug(format string, args ...interface{}) {
	l.logFormat(Debug, format, args...)
}

// Info 处理info日志
func (l *LogSet) Info(format string, args ...interface{}) {
	l.logFormat(Info, format, args...)
}

// Warning 处理warning日志
func (l *LogSet) Warning(format string, args ...interface{}) {
	l.logFormat(Warning, format, args...)
}

// Error 处理error日志
func (l *LogSet) Error(format string, args ...interface{}) {
	l.logFormat(Error, format, args...)
}

// Fatal 处理Fatal日志
func (l *LogSet) Fatal(format string, args ...interface{}) {
	l.logFormat(Fatal, format, args...)
}

// Close 关闭文件句柄
func (l *LogSet) Close() {
	l.accFile.Close()
	l.errFile.Close()
}

  • mylogger_util.go
package mylogger

import (
	"path"
	"runtime"
	"strings"
)

func getCallerInfo(skip int) (fileName, funcName string, line int) {
	pc, file, line, ok := runtime.Caller(skip)

	if !ok {
		return
	}

	fileName = path.Base(file)
	funcName = runtime.FuncForPC(pc).Name()

	return fileName, funcName, line
}

// 根据程序传来的用户级别返回字符串
func getLevelStr(level Level) string {
	switch level {
	case Debug:
		return "Debug"
	case Info:
		return "Info"
	case Warning:
		return "Warning"
	case Error:
		return "Error"
	case Fatal:
		return "Fatal"
	default:
		return "Info"
	}
}

// 根据用户传入的字符串, 解析出对应日志级别
func strToLevel(levelStr string) Level {
	levelStr = strings.ToLower(levelStr) // 全转小写

	switch levelStr {
	case "debug":
		return Debug
	case "info":
		return Info
	case "warning":
		return Warning
	case "error":
		return Error
	case "fatal":
		return Fatal
	default:
		return Info
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值