日志项目升级
跟新前的日志项目:https://blog.51cto.com/13812615/2489258
- 升级功能点
- 异步刷盘
- 日志切分
项目结构
```xlog/
├── console.go #console日志输出
├── file.go #文件输出(本次优化点)
├── level.go #日志级别类
├── log.go #日志库
├── log_base.go # 基类库
└── tool.go #工具库,用于获取文件名,函数名,所在行
## 代码拆解
- log.go
package xlog
//定义全局默认的日志类型
var logger XLog = newXLog(XLogTypeFile,XLogLevelDebug,"","default")
type XLog interface {
Init() error //文件初始化
LogDebug(fmt string, args ...interface{})
LogTrace(fmt string, args ...interface{})
LogInfo(fmt string, args ...interface{})
LogWarn(fmt string, args ...interface{})
LogError(fmt string, args ...interface{})
LogFatal(fmt string, args ...interface{})
Close()
SetLevel(level int)
}
func newXLog(logType, level int, filename, module string) XLog {
//定义接口
var logger XLog
switch logType {
case XLogTypeFile:
logger = NewXFile(level,filename, module)
case XLogTypeConsole:
logger = NewXConsole(level, module)
default:
logger = NewXFile(level,filename, module)
}
return logger
}
//封装接口,后期可以直接调用
func Init(logType, level int, filename, module string) (err error){
logger = newXLog(logType,level,filename,module)
return logger.Init()
}
func LogDebug(fmt string, args ...interface{}) {
logger.LogDebug(fmt, args...)
}
func LogTrace(fmt string, args ...interface{}) {
logger.LogTrace(fmt, args...)
}
func LogInfo(fmt string, args ...interface{}) {
logger.LogInfo(fmt, args...)
}
func LogWarn(fmt string, args ...interface{}) {
logger.LogWarn(fmt, args...)
}
func LogError(fmt string, args ...interface{}) {
logger.LogError(fmt, args...)
}
func LogFatal(fmt string, args ...interface{}) {
logger.LogFatal(fmt, args...)
}
func Close() {
logger.Close()
}
func SetLevel(level int) {
logger.SetLevel(level)
}
- log_base.go
package xlog
import (
"fmt"
"os"
"path/filepath"
"time"
)
type LogData struct {
timeStr string
levelStr string
module string
filename string
funcName string
lineNo int
data string
}
type XLogBase struct {
level int
module string
}
func (l XLogBase) writeLog(file os.File,logData *LogData) {
fmt.Fprintf(file,"%s %s %s (%s:%s:%d) %s\n",
logData.timeStr, logData.levelStr, logData.module,
logData.filename, logData.funcName, logData.lineNo, logData.data)
}
func (l XLogBase) formatLogger(level int, module, format string, args ...interface{}) LogData {
now := time.Now()
timeStr := now.Format("2006-01-02 15:04:05.000")
levelStr := getLevelStr(level)
filename, funcName, lineNo := GetLineInfo(5)
filename = filepath.Base(filename)
data := fmt.Sprintf(format, args...)
//fmt.Printf("%s %s %s (%s:%s:%d) %s\n",timeStr,leveStr,module,filename,funcName,lineNo,data)
return &LogData{
timeStr: timeStr,
levelStr: levelStr,
module: module,
filename: filename,
funcName: funcName,
lineNo: lineNo,
data: data,
}
}
- level.go
package xlog
const (
XLogLevelDebug = iota
XLogLevelTrace
XLogLevelInfo
XLogLevelWarn
XLogLevelError
XLogLevelFatal
)
const (
XLogTypeFile = iota
XLogTypeConsole
)
func getLevelStr(level int) string {
switch level {
case XLogLevelDebug:
return "DEBUG"
case XLogLevelTrace:
return "TRACE"
case XLogLevelInfo:
return "INFO"
case XLogLevelWarn:
return "WARN"
case XLogLevelError:
return "ERROR"
case XLogLevelFatal:
return "FATAL"
default:
return "UNKNOWN"
}
}
- console.go 本次无更新
package xlog
import (
"os"
)
type XConsole struct {
*XLogBase //指针实现
}
func NewXConsole(level int, module string) XLog {
logger := &XConsole{}
//初始化指针,防止panic
logger.XLogBase = &XLogBase{
level: level,
module: module,
}
return logger
}
//不需要初始化文件写入
func (c *XConsole)Init() error {
return nil
}
func (c *XConsole) LogDebug(format string, args ...interface{}) {
if c.level > XLogLevelDebug {
return
}
logData := c.formatLogger(XLogLevelDebug, c.module, format, args...)
c.writeLog(os.Stdout,logData)
}
func (c *XConsole) LogTrace(format string, args ...interface{}) {
if c.level > XLogLevelTrace {
return
}
logData := c.formatLogger(XLogLevelTrace, c.module, format, args...)
c.writeLog(os.Stdout,logData)
}
func (c *XConsole) LogInfo(format string, args ...interface{}) {
if c.level > XLogLevelInfo {
return
}
logData := c.formatLogger(XLogLevelInfo, c.module, format, args...)
c.writeLog(os.Stdout,logData)
}
func (c *XConsole) LogWarn(format string, args ...interface{}) {
if c.level > XLogLevelWarn {
return
}
logData := c.formatLogger(XLogLevelWarn, c.module, format, args...)
c.writeLog(os.Stdout,logData)
}
func (c *XConsole) LogError(format string, args ...interface{}) {
if c.level > XLogLevelError {
return
}
logData := c.formatLogger(XLogLevelError, c.module, format, args...)
c.writeLog(os.Stdout,logData)
}
func (c XConsole) LogFatal(format string, args ...interface{}) {
if c.level > XLogLevelFatal {
return
}
logData := c.formatLogger(XLogLevelFatal, c.module, format, args...)
c.writeLog(os.Stdout,logData)
}
func (c XConsole) SetLevel(level int) {
c.level = level
}
func (c *XConsole) Close() {}
- file.go 本次更新异步刷盘&日志切分
package xlog
import (
"fmt"
"os"
"sync"
"time"
)
type XFile struct {
filename string
file os.File
XLogBase
logChan chan LogData
wg sync.WaitGroup
curDay int //也可以按小时去切割,curHour int
}
func (c *XFile) Init() (err error) {
c.file,err = os.OpenFile(c.filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY,0755)
if err != nil {
return
}
return
}
func NewXFile(level int, filename, module string) XLog {
logger := &XFile{
filename: filename,
}
logger.XLogBase = &XLogBase{
module: module,
level: level,
}
logger.curDay = time.Now().Day() // 初始化结构体logger.curHour = time.Now().Hour()
logger.wg = &sync.WaitGroup{} //防止主进程退出,不执行子进程
logger.logChan = make(chan *LogData,10000) //管道初始化
//异步刷日志到磁盘
logger.wg.Add(1)
go logger.syncLog()
return logger
}
func (c *XFile) syncLog() {
//从管道读取日志,然后写入文件
for data := range c.logChan {
c.splitLog() //调用切分日志的操作
c.writeLog(c.file,data)
}
c.wg.Done()
}
func (c *XFile) splitLog() {
now := time.Now()
if now.Day() == c.curDay {
return
}
c.curDay = now.Day() //更新时间 //按小时切分的配置,c.curHour = now.Hour()
c.file.Sync()
c.file.Close()
newFilename := fmt.Sprintf("%s-%04d-%02d-%02d",c.filename,
now.Year(),now.Month(),now.Day())
/*
按小时切分配置
newFilename := fmt.Sprintf("%s-%04d-%02d-%02d-%02d", c.filename,now.Year(), now.Month(), now.Day(), now.Hour())
*/
os.Rename(c.filename,newFilename)
c.Init()
}
func (c *XFile) writeToChan(level int,module string,format string,args ...interface{}) {
logData := c.formatLogger(level, module, format, args...)
select {
case c.logChan <- logData:
default:
}
}
func (c *XFile) LogDebug(format string, args ...interface{}) {
if c.level > XLogLevelDebug {
return
}
c.writeToChan(XLogLevelDebug, c.module, format, args...)
}
func (c *XFile) LogTrace(format string, args ...interface{}) {
if c.level > XLogLevelTrace {
return
}
c.writeToChan(XLogLevelTrace, c.module, format, args...)
}
func (c *XFile) LogInfo(format string, args ...interface{}) {
if c.level > XLogLevelInfo {
return
}
c.writeToChan(XLogLevelInfo, c.module, format, args...)
}
func (c *XFile) LogWarn(format string, args ...interface{}) {
if c.level > XLogLevelWarn {
return
}
c.writeToChan(XLogLevelWarn, c.module, format, args...)
}
func (c *XFile) LogError(format string, args ...interface{}) {
if c.level > XLogLevelError {
return
}
c.writeToChan(XLogLevelError, c.module, format, args...)
}
func (c *XFile) LogFatal(format string, args ...interface{}) {
if c.level > XLogLevelFatal {
return
}
c.writeToChan(XLogLevelFatal, c.module, format, args...)
}
func (c *XFile) SetLevel(level int) {
c.level = level
}
func (c *XFile)Close() {
//管道为空要关闭
if c.logChan != nil {
close(c.logChan)
}
c.wg.Wait()
if c.file != nil {
c.file.Sync() //同步写磁盘
c.file.Close()
}
}
- tool.go
package xlog
import "runtime"
func GetLineInfo(skip int) (filename, funcName string, lineNo int) {
pc, file, line, ok := runtime.Caller(skip)
if ok {
fun := runtime.FuncForPC(pc)
funcName = fun.Name()
}
filename = file
lineNo = line
return
}
## 测试样例
- 注意事项:
- 可以通过xlog.Debug("xxx")直接打印
- 不同的模块与需要初始化操作
打印落盘需要定义好对应的日志路径以及,模块名
err := xlog.Init(logType, xlog.XLogLevelDebug, "./xlog.log", "xlog_example")
if err != nil {
fmt.Printf("logger init failed\n")
return
}
- xlog_example/main.go
package main
import (
"flag"
"fmt"
_"fmt"
"oldBoy/xlog"
)
//写日志测试
func logic() {
for {
xlog.LogDebug("dads1,user_id:%d,username:%s", 12331, "sadsaf")
xlog.LogTrace("dads2")
xlog.LogInfo("dads3")
xlog.LogWarn("dads4")
xlog.LogError("sss1")
xlog.LogFatal("sss2")
}
}
func main() {
var logTypeStr string
flag.StringVar(&logTypeStr, "type", "console", "please input logger type")
flag.Parse()
var logType int
if (logTypeStr == "file") {
logType = xlog.XLogTypeFile
} else {
logType = xlog.XLogTypeConsole
}
xlog.LogDebug("log type is %v", logType)
_ = logType
err := xlog.Init(logType, xlog.XLogLevelDebug, "./xlog.log", "xlog_example")
if err != nil {
fmt.Printf("logger init failed\n")
return
}
logic()
xlog.Close()
fmt.Printf("close return")
}
有疑问加站长微信联系(非本文作者)