日志项目
日志库需求分析
-
日志库产生的背景
- 程序运行是个黑盒
- 日志是程序之外的表现
- 通过日志,可以知道程序的健康状态
-
日志打印的级别
- Debug:日志最详细,对程序的影响比较大
- Trace: 用来追踪问题
- Info: 比较重要的信息,比如访问日志
- Warn:警告日志,表明程序存在问题
- Error: 错误日志,运行程序时发生的错误
- Fatal: 严重错误日志
-
日志存储的位置
- 直接输出到控制台
- 打印到文件里
- 直接打印到网络中,比如kafka
-
为什么使用接口
- 定义日志库的标准或者规范
- 易于扩展
- 利于程序维护
- 日志库的设计
- 打印各个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(3)
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,
}
}
log.go
package xlog
type XLog interface {
Init() error //文件初始化
LogDebug(format string, args ...interface{})
LogTrace(format string, args ...interface{})
LogInfo(format string, args ...interface{})
LogWarn(format string, args ...interface{})
LogError(format string, args ...interface{})
LogFatal(format 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
}
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"
}
}
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
}
文件日志库开发
package xlog
import (
"os"
)
type XFile struct {
*XLogBase
filename string
file *os.File
}
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,
}
return logger
}
func (c *XFile) LogDebug(format string, args ...interface{}) {
if c.level > XLogLevelDebug {
return
}
logData := c.formatLogger(XLogLevelDebug, c.module, format, args...)
c.writeLog(c.file,logData)
}
func (c *XFile) LogTrace(format string, args ...interface{}) {
if c.level > XLogLevelTrace {
return
}
logData := c.formatLogger(XLogLevelTrace, c.module, format, args...)
c.writeLog(c.file,logData)
}
func (c *XFile) LogInfo(format string, args ...interface{}) {
if c.level > XLogLevelInfo {
return
}
logData := c.formatLogger(XLogLevelInfo, c.module, format, args...)
c.writeLog(c.file,logData)
}
func (c *XFile) LogWarn(format string, args ...interface{}) {
if c.level > XLogLevelWarn {
return
}
logData := c.formatLogger(XLogLevelWarn, c.module, format, args...)
c.writeLog(c.file,logData)
}
func (c *XFile) LogError(format string, args ...interface{}) {
if c.level > XLogLevelError {
return
}
logData := c.formatLogger(XLogLevelError, c.module, format, args...)
c.writeLog(c.file,logData)
}
func (c *XFile) LogFatal(format string, args ...interface{}) {
if c.level > XLogLevelFatal {
return
}
logData := c.formatLogger(XLogLevelFatal, c.module, format, args...)
c.writeLog(c.file,logData)
}
func (c *XFile) SetLevel(level int) {
c.level = level
}
func (c *XFile)Close() {
if c.file != nil {
c.file.Close()
}
}
Console日志开发
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() {}
日志使用以及测试
xlog_example/main.go
package main
import (
"flag"
"fmt"
_"fmt"
"oldBoy/day9/xlog"
)
func logic(logger xlog.XLog) {
logger.LogDebug("dads1,user_id:%d,username:%s",12331,"sadsaf")
logger.LogTrace("dads2")
logger.LogInfo("dads3")
logger.LogWarn("dads4")
logger.LogError("sss")
logger.LogFatal("sss")
}
/*
func testGetLine() {
//skip =2 深度为2的调用位置,也就是main下的22行
filename,funcName,lineNo := xlog.GetLineInfo(2)
fmt.Printf("filename=%s,funcname=%s,linenum=%d\n",filename,funcName,lineNo)
}
*/
func main() {
//testGetLine()
var logTypeStr string
flag.StringVar(&logTypeStr,"type","file","please input a logger type")
flag.Parse()
var logType int
if (logTypeStr == "file") {
logType = xlog.XLogTypeFile
}else {
logType = xlog.XLogTypeConsole
}
logger := xlog.NewXLog(logType,xlog.XLogLevelDebug,"./xlog.log","xlog_example")
err := logger.Init()
if err != nil {
fmt.Printf("init error:%v\n",err)
return
}
logic(logger)
}
备注
- 缺少的功能:
- 异步写盘
- 日志切分
有疑问加站长微信联系(非本文作者)