自定义日志打印
logger会根据定义好的 Settings 对象来指定文件的存储路径文件名称、文件后缀以及文件的时间等进行操作
logger.go
// Settings stores config for logger
type Settings struct {
Path string `yaml:"path"` //文件路径
Name string `yaml:"name"` //文件名称
Ext string `yaml:"ext"` //文件后缀
TimeFormat string `yaml:"time-format"` //时间格式方式,默认输入 2006-01-02 15:04:05 会显示当前时间
}
var (
logFile *os.File //日志文件
defaultPrefix = "" //默认的前缀
defaultCallerDepth = 2 //追溯到调用函数的上一层的栈帧数,0:当前函数调用的行数,1:上一层函数的调用函数,2:上两层函数的调用位置
logger *log.Logger //日志打印器
mu sync.Mutex //锁
logPrefix = "" //日志的前缀
levelFlags = []string{"DEBUG", "INFO", "WARN", "ERROR", "FATAL"} //日志等级标识
)
//定义日志等级int类型
type logLevel int
// 日志等级,根据默认值后续自增
const (
DEBUG logLevel = iota
INFO
WARNING
ERROR
FATAL
)
//初始化日志标识
const flags = log.LstdFlags
//初始化一个 logger 日志器,设置默认前缀,日志标识
func init() {
logger = log.New(os.Stdout, defaultPrefix, flags)
}
// Setup 根据设置对象进行初始化
func Setup(settings *Settings) {
var err error
dir := settings.Path
//拼接文件名称 名称-时间.Ext
fileName := fmt.Sprintf("%s-%s.%s",
settings.Name,
time.Now().Format(settings.TimeFormat),
settings.Ext)
//判断路径是否可以打开,并且打开文件对象
logFile, err = mustOpen(fileName, dir)
if err != nil {
log.Fatalf("logging.Setup err: %s", err)
}
//获取到文件的写入器
mw := io.MultiWriter(os.Stdout, logFile)
//创建logger
logger = log.New(mw, defaultPrefix, flags)
}
//setPrefix 根据日志等级设置打印日志的前缀
func setPrefix(level logLevel) {
//获取到当前程序调用执行函数的行号信息,如果想要获取到打印日志的函数日志位置就需要设置为2
_, file, line, ok := runtime.Caller(defaultCallerDepth)
if ok {
logPrefix = fmt.Sprintf("[%s][%s:%d] ", levelFlags[level], filepath.Base(file), line)
} else {
logPrefix = fmt.Sprintf("[%s] ", levelFlags[level])
}
//设置logger的前缀
logger.SetPrefix(logPrefix)
}
// Debug 打印debug日志
func Debug(v ...interface{}) {
mu.Lock()
defer mu.Unlock()
setPrefix(DEBUG)
logger.Println(v...)
}
// Info 打印info日志
func Info(v ...interface{}) {
mu.Lock()
defer mu.Unlock()
setPrefix(INFO)
logger.Println(v...)
}
// Warn 打印warn日志
func Warn(v ...interface{}) {
mu.Lock()
defer mu.Unlock()
setPrefix(WARNING)
logger.Println(v...)
}
// Error 打印Error日志
func Error(v ...interface{}) {
mu.Lock()
defer mu.Unlock()
setPrefix(ERROR)
logger.Println(v...)
}
// Errorf 打印Error格式化
func Errorf(format string, v ...interface{}) {
mu.Lock()
defer mu.Unlock()
setPrefix(ERROR)
logger.Println(fmt.Sprintf(format, v...))
}
// Fatal prints error log then stop the program
func Fatal(v ...interface{}) {
mu.Lock()
defer mu.Unlock()
setPrefix(FATAL)
logger.Fatalln(v...)
}
files.go
当前文件主要是用于操作对应的日志文件是否存储,并且创建等操作
// checkNotExist 检查文件是否存在
func checkNotExist(src string) bool {
_, err := os.Stat(src)
return os.IsNotExist(err)
}
// checkPermission 检查文件的权限
func checkPermission(src string) bool {
_, err := os.Stat(src)
return os.IsPermission(err)
}
// isNotExistMkDir 如果文件不存在就创建文件
func isNotExistMkDir(src string) error {
if checkNotExist(src) {
return mkDir(src)
}
return nil
}
// mkDir 创建文件
func mkDir(src string) error {
return os.MkdirAll(src, os.ModePerm)
}
// mustOpen 打开文件
func mustOpen(fileName, dir string) (*os.File, error) {
//检查文件权限
if checkPermission(dir) {
return nil, fmt.Errorf("permission denied dir: %s", dir)
}
//如果文件不存在并且也创建失败了
if err := isNotExistMkDir(dir); err != nil {
return nil, fmt.Errorf("error during make dir %s, err: %s", dir, err)
}
//通过路径开启文件
f, err := os.OpenFile(dir+string(os.PathSeparator)+fileName, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0644)
if err != nil {
return nil, fmt.Errorf("fail to open file, err: %s", err)
}
return f, nil
}