主要涉及
main.go
mylogger.go( 接口文件)
outputconsol.go(输出日志到终端)
outputfile.go (输出 日志到 文件)
mylogger.go( 接口文件)
package main
import (
"mylogger/logger"
"time"
)
var log logger.Logger // 声明一个全局的接口变量
// 测试自己写的日志相关的内容
func main() {
//log = logger.NewConsoleLogger("info")
log = logger.NewFileLogger("error", "./", "baimei.log", 10*1024*1024) // 10M =10*1024*1024
for {
id := 1222
name := "baimeidashu"
log.Debug("这是一条debug日志,id :%d, name: %s,", id, name)
log.Trace("这是一条Trace日志,id :%d, name: %s,", id, name)
log.Info("这是一条 info 日志,id :%d, name: %s,", id, name)
log.Warning("这是一条 Warning 日志,id :%d, name: %s,", id, name)
log.Error("这是一条 Error 日志 ,id :%d, name: %s,", id, name)
log.Fatal("这是一条 Fatal 日志,id :%d, name: %s,", id, name)
time.Sleep(time.Second)
}
}
mylogger.go( 接口文件)
package logger
import (
"errors"
"fmt"
"path"
"runtime"
"strings"
)
//author baimeidashu.com
// 往终端写日志相关内容
type LogLevel uint16
// Logger 接口
type Logger interface {
Debug(fomat string, a ...interface{})
Trace(fomat string, a ...interface{})
Info(fomat string, a ...interface{})
Warning(fomat string, a ...interface{})
Error(fomat string, a ...interface{})
Fatal(fomat string, a ...interface{})
}
const (
UNKONWN LogLevel = iota
DEBUG
TRACE
INFO
WARNING
ERROR
FATAL
)
func parseLogLeve(s string) (LogLevel, error) {
s = strings.ToLower(s)
//fmt.Printf("%s", s)
switch s {
case "debug":
return DEBUG, nil
case "trace":
return TRACE, nil
case "info":
return INFO, nil
case "warning":
return WARNING, nil
case "error":
return ERROR, nil
case "fatal":
return FATAL, nil
default:
err := errors.New("无效的日志级别")
return UNKONWN, err
}
}
// 把 int 转换为 字符串
func getLogString(lv LogLevel) string {
switch lv {
case DEBUG:
return "DEBUG"
case TRACE:
return "TRACE"
case INFO:
return "INFO"
case WARNING:
return "WARNING"
case ERROR:
return "ERROR"
case FATAL:
return "FATAL"
}
return "DEBUG"
}
// 获取执行go 文件的信息
func getInfo(skip int) (funcName string, fileName string, lineNumber int) {
pc, file, line, ok := runtime.Caller(skip)
if !ok {
fmt.Println("runtime.caller() failed\n")
return
}
funcName = runtime.FuncForPC(pc).Name()
fileName = path.Base(file)
funcName = strings.Split(funcName, ".")[1]
lineNumber = line
return
}
outputconsol.go(输出日志到终端)
package logger
import (
"fmt"
"time"
)
//author baimeidashu.com
// 往终端写日志相关内容
// Logger 日志结构体
type ConsoleLogger struct {
Level LogLevel
}
// NewConsoleLogger 构造函数
func NewConsoleLogger(leverStr string) ConsoleLogger {
lever, err := parseLogLeve(leverStr)
fmt.Println("s%", lever)
fmt.Println("s%", lever)
if err != nil {
panic(err)
}
return ConsoleLogger{
Level: lever,
}
}
// 第2版,由 函数 改为 方法 ,这样这个方法 在这个构造体内就可以使用了,
func (c ConsoleLogger) log(lv LogLevel, format string, a ...interface{}) {
if c.enable(lv) {
msg := fmt.Sprintf(format, a...)
now := time.Now()
funcName, fileName, lineNum := getInfo(3)
fmt.Printf("[%s] --[%s] --[%s:%s:%d]%s \n ", now.Format("2006-01-02 15:04:05"), getLogString(lv), fileName, funcName, lineNum, msg)
}
}
// 接收者变量 接收者类型
func (c ConsoleLogger) enable(level LogLevel) bool {
return c.Level >= level
}
// 接收者变量 接收者类型 给 Logger 添加 方法
func (c ConsoleLogger) Debug(fomat string, a ...interface{}) {
c.log(DEBUG, fomat, a)
}
func (c ConsoleLogger) Trace(fomat string, a ...interface{}) {
c.log(TRACE, fomat, a)
}
func (c ConsoleLogger) Info(fomat string, a ...interface{}) {
c.log(INFO, fomat, a)
}
func (c ConsoleLogger) Warning(fomat string, a ...interface{}) {
c.log(WARNING, fomat, a)
}
func (c ConsoleLogger) Error(fomat string, a ...interface{}) {
c.log(ERROR, fomat, a)
}
func (c ConsoleLogger) Fatal(fomat string, a ...interface{}) {
c.log(FATAL, fomat, a)
}
outputfile.go (输出 日志到 文件)
package logger
import (
"fmt"
"os"
"path"
"time"
)
// 向文件中输出日志
type FileLogger struct {
Level LogLevel
filePath string // 日志文件保存路径
fileName string // 日志文件名称
errFileName string // 错误文件名称
maxFileSize int64
fileObj *os.File
errFileObj *os.File
}
// NewFileLogger 构造函数
func NewFileLogger(leverString, fpath, fname string, maxSize int64) *FileLogger {
logLevel, err := parseLogLeve(leverString)
if err != nil {
panic(err)
}
fl := &FileLogger{
Level: logLevel,
filePath: fpath,
fileName: fname,
maxFileSize: maxSize,
}
err = fl.initFile() //初始化文件, 先打开文件
if err != nil {
panic(err)
}
return fl
}
func (f *FileLogger) initFile() error {
fp := path.Join(f.filePath, f.fileName)
fileObj, err := os.OpenFile(fp, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
fmt.Println("open log file failed, err:%v \n", err)
return err
}
errfileObj, err := os.OpenFile(fp+".err", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
fmt.Println("open errlog file failed, err:%v \n", err)
return err
}
//日志文件都打开了
f.fileObj = fileObj
f.errFileObj = errfileObj
return nil
}
// 检测日志文件的大小
func (f *FileLogger) checkSize(file *os.File) bool {
fileInfo, err := file.Stat()
if err != nil {
fmt.Printf("get file info failed, err : %v\n", err)
return false
}
return fileInfo.Size() >= f.maxFileSize
}
// 输出日志
func (f *FileLogger) log(lv LogLevel, format string, a ...interface{}) {
if f.enable(lv) {
msg := fmt.Sprintf(format, a...)
now := time.Now()
funcName, fileName, lineNum := getInfo(3)
if f.checkSize(f.fileObj) {
newFile, err := f.splitFile(f.fileObj)
if err != nil {
fmt.Printf("open new log file faild , err: %v\n", err)
return
}
f.fileObj = newFile
}
fmt.Fprintf(f.fileObj, "[%s] --[%s] --[%s:%s:%d]%s \n ", now.Format("2006-01-02 15:04:05"), getLogString(lv), fileName, funcName, lineNum, msg)
// 如果 logleve 级别大于 EROR 级别, 我还要再 err 日志文件中再记录一遍
if lv >= ERROR {
if f.checkSize(f.errFileObj) {
newFile, err := f.splitFile(f.errFileObj)
if err != nil {
fmt.Printf("open new log file faild , err: %v\n", err)
return
}
f.errFileObj = newFile
}
fmt.Fprintf(f.errFileObj, "[%s] --[%s] --[%s:%s:%d]%s \n ", now.Format("2006-01-02 15:04:05"), getLogString(lv), fileName, funcName, lineNum, msg)
}
}
}
// 切割文件
func (f *FileLogger) splitFile(file *os.File) (*os.File, error) {
//需要切割日志文件
//2. 备份以下 rename xx.lgo -> xx.log.bak20230514
nowStr := time.Now().Format("20060514")
fileInfo, err := file.Stat()
if err != nil {
fmt.Printf("open new log file faild , err: %v\n", err)
return nil, err
}
logName := path.Join(f.filePath, fileInfo.Name()) // 当前日志文件路径
newLogName := fmt.Sprintf("%s.bak%s", logName, nowStr) // 拼接一个日志文件备份的名字
// 1.关闭当前的日志文件
file.Close()
os.Rename(logName, newLogName)
//3. 打开一个新日志文件,
fileObj, err := os.OpenFile(logName, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
fmt.Printf("open new log file faild , err: %v\n", err)
return nil, err
}
//4. 将打开的新日志文件对象赋值给 f.fileObj
return fileObj, err
}
// 接收者变量 接收者类型
func (f *FileLogger) enable(level LogLevel) bool {
return f.Level >= level
}
// 接收者变量 接收者类型 给 Logger 添加 方法
func (f *FileLogger) Debug(fomat string, a ...interface{}) {
f.log(DEBUG, fomat, a)
}
func (f *FileLogger) Trace(fomat string, a ...interface{}) {
f.log(TRACE, fomat, a)
}
func (f *FileLogger) Info(fomat string, a ...interface{}) {
f.log(INFO, fomat, a)
}
func (f *FileLogger) Warning(fomat string, a ...interface{}) {
f.log(WARNING, fomat, a)
}
func (f *FileLogger) Error(fomat string, a ...interface{}) {
f.log(ERROR, fomat, a)
}
func (f *FileLogger) Fatal(fomat string, a ...interface{}) {
f.log(FATAL, fomat, a)
}
func (f *FileLogger) Close() {
f.fileObj.Close()
f.errFileObj.Close()
}