Journal entry
Log library requirements analysis
-
Background generated by the log library
- Program running is a black box
- The log is a manifestation outside the program
- Through the log, you can know the health status of the program
-
Log printing level
- Debug: The log is the most detailed and has a greater impact on the program
- Trace: used to trace the problem
- Info: More important information, such as access logs
- Warn: Warning log indicating that there is a problem with the program
- Error: Error log, errors that occurred while running the program
- Fatal: Fatal error log
-
Log storage location
- Direct output to the console
- Print to file
- Print directly to the network, such as kafka
-
Why use interfaces
- Define the standards or specifications of the log library
- Easy to expand
- Facilitates program maintenance
- Design of log library
- Print logs of various levels
- Set level
- Constructor
Log library interface design
log_base.go base class
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: Get the line where the program is running and the function name
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
}
File log library development
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 log development
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() {}
Log usage and testing
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)
}
Remarks
- Missing features:
- Asynchronous disk write
- Log segmentation