基于go的日志库开发源码
只有源码,详解请看 《基于go的日志库开发详解》
mylogger.go
package mylogger
import (
"errors"
"fmt"
"path"
"runtime"
"strings"
)
// LogLever 日志等级类型
type LogLever uint16
// 日志类型 等级变量
const (
UNKNOWN LogLever = iota
DEBUG
TRACE
INFO
WARNING
ERROR
FATAL
)
// 将字符串转换成为日志等级对象 "INFO" --> INFO(LogLever)
func parseLogLever(s string) (LogLever, error) {
s = strings.ToUpper(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 UNKNOWN, err
}
}
// 将日志等级对象转为字符串对象 INFO(LogLever) --> "INFO"
func getLogString(lv LogLever) string {
switch lv {
case DEBUG:
return "DEBUG"
case TRACE:
return "TRACE"
case INFO:
return "INFO"
case ERROR:
return "ERROR"
case WARNING:
return "WARNING"
case FATAL:
return "FATAL"
default:
return "DEBUG"
}
}
// 获取文件信息
func getInfo(skip int) (funcName, fileName string, lineNo int) {
pc, file, line, ok := runtime.Caller(skip)
if !ok {
fmt.Println("runtime.Caller failed")
return
}
funcName = runtime.FuncForPC(pc).Name()
fileName = path.Base(file)
funcName = strings.Split(funcName, ".")[1]
lineNo = line
return
}
// Logger 接口
type Logger interface {
Debug(format string, a ...interface{})
Trace(format string, a ...interface{})
Info(format string, a ...interface{})
Warning(format string, a ...interface{})
Error(format string, a ...interface{})
Fatal(format string, a ...interface{})
}
console.go
package mylogger
import (
"fmt"
"time"
)
// Logger 日志结构体
type ConsoleLogger struct {
Lever LogLever
}
// NewLog 构造函数
func NewConsoleLog(leverStr string) ConsoleLogger {
lever, err := parseLogLever(leverStr)
if err != nil {
panic(err)
}
return ConsoleLogger{
Lever: lever,
}
}
func (c ConsoleLogger) log(lv LogLever, format string, a ...interface{}) {
if c.enable(lv) {
msg := fmt.Sprintf(format, a...)
nowTime := time.Now().Format("2006-01-02 03:04:05")
funcName, fileName, lineNo := getInfo(3)
fmt.Printf("[%s] [%s] [%s:%s:%d] %s\n", nowTime, getLogString(lv), fileName, funcName, lineNo, msg)
}
}
// 打印日志的条件控制
// 打印日志等级为设置日志之下的
func (c ConsoleLogger) enable(loglever LogLever) bool {
return loglever >= c.Lever
}
func (c ConsoleLogger) Debug(format string, a ...interface{}) {
c.log(DEBUG, format, a...)
}
func (c ConsoleLogger) Info(format string, a ...interface{}) {
c.log(INFO, format, a...)
}
func (c ConsoleLogger) Trace(format string, a ...interface{}) {
c.log(TRACE, format, a...)
}
func (c ConsoleLogger) Warning(format string, a ...interface{}) {
c.log(WARNING, format, a...)
}
func (c ConsoleLogger) Error(format string, a ...interface{}) {
c.log(ERROR, format, a...)
}
func (c ConsoleLogger) Fatal(format string, a ...interface{}) {
c.log(FATAL, format, a...)
}
file.go
package mylogger
import (
"fmt"
"os"
"path"
"time"
)
// 往文件里面写日志相关代码
type FileLogger struct {
Level LogLever
filePath string // 日志文件保存的路径
fileName string // 日志文件保存的文件名
fileObj *os.File
errFileObj *os.File
maxFileSize int64 // 日志文件保存的最大内存
}
// NewFileLogger 生成日志文件构造器
func NewFileLogger(leverStr, fp, fn string, maxSize int64) *FileLogger {
logLever, err := parseLogLever(leverStr)
if err != nil {
panic(err)
}
f1 := &FileLogger{
Level: logLever,
filePath: fp,
fileName: fn,
maxFileSize: maxSize,
}
err1 := f1.initFile()
if err != nil {
panic(err1)
}
return f1
}
// 初始化保存的日志文件和错误日志文件
func (f *FileLogger) initFile() error {
fullFileName := path.Join(f.filePath, f.fileName)
fileObj, err1 := os.OpenFile(fullFileName, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
if err1 != nil {
fmt.Printf("open log file failed! err:%v", err1)
return err1
}
errFileObj, err2 := os.OpenFile(fullFileName+".err", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err2 != nil {
fmt.Printf("open err log file failed! err:%v", err2)
return err2
}
f.fileObj = fileObj
f.errFileObj = errFileObj
return nil
}
// 关闭文件操作
func (f *FileLogger) Close() {
f.fileObj.Close()
f.errFileObj.Close()
}
// 判断当前文件是否需要进行切割
func (f *FileLogger) checkSize(file *os.File) bool {
fileInfo, err := file.Stat()
if err != nil {
fmt.Printf("get file info failed! err:%v", err)
return false
}
// 看下当前的日志文件是否 大于或等于 日志文件规定的max大小
return fileInfo.Size() >= f.maxFileSize
}
// 对文件进行分割
func (f *FileLogger) splitFile(file *os.File) (*os.File, error) {
// 需要进行文件分割
nowStr := time.Now().Format("20060102150405.000")
fileInfo, err := file.Stat()
if err != nil {
fmt.Printf("get file info failed! err:%v\n", err)
return nil, err
}
logName := path.Join(f.filePath, fileInfo.Name()) // 获取老的文件名
newLogName := fmt.Sprintf("%s.bak%s", logName, nowStr)
// 1. 关闭当前文件
f.fileObj.Close()
// 2. 对当前文件进行重命名 rename 就是备份一下 xx.log ->xx.log.bak202110241409
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 log file failed! err:%v\n", err)
return nil, err
}
// 4. 将打开的文件重新赋值给f.fileObj
return fileObj, nil
}
// 记录日志的方法
func (f *FileLogger) log(lv LogLever, format string, a ...interface{}) {
if f.enable(lv) {
msg := fmt.Sprintf(format, a...)
nowTime := time.Now().Format("2006-01-02 03:04:05")
funcName, fileName, lineNo := getInfo(3)
if f.checkSize(f.fileObj) { // 判断下是否超了,超了就要进行文件分割
newFile, err := f.splitFile(f.fileObj) // 切割日志文件
if err != nil {
fmt.Printf("split file failed! err:%v\n", err)
return
}
f.fileObj = newFile
}
fmt.Fprintf(f.fileObj, "[%s] [%s] [%s:%s:%d] %s\n", nowTime, getLogString(lv), fileName, funcName, lineNo, msg)
if lv >= ERROR {
// 源文件过大就要进行切割
if f.checkSize(f.errFileObj) {
newFile, err := f.splitFile(f.errFileObj) // 切割日志文件
if err != nil {
fmt.Printf("split file failed! err:%v\n", err)
return
}
f.errFileObj = newFile
}
//如果要记录的日志大于ERROR了,我还要再错误日志里面记录一次
fmt.Fprintf(f.errFileObj, "[%s] [%s] [%s:%s:%d] %s\n", nowTime, getLogString(lv), fileName, funcName, lineNo, msg)
}
}
}
// 打印日志的条件控制
// 打印日志等级为设置日志之下的
func (f *FileLogger) enable(loglever LogLever) bool {
return loglever >= f.Level
}
func (f *FileLogger) Debug(format string, a ...interface{}) {
f.log(DEBUG, format, a...)
}
func (f *FileLogger) Info(format string, a ...interface{}) {
f.log(INFO, format, a...)
}
func (f *FileLogger) Trace(format string, a ...interface{}) {
f.log(TRACE, format, a...)
}
func (f *FileLogger) Warning(format string, a ...interface{}) {
f.log(WARNING, format, a...)
}
func (f *FileLogger) Error(format string, a ...interface{}) {
f.log(ERROR, format, a...)
}
func (f *FileLogger) Fatal(format string, a ...interface{}) {
f.log(FATAL, format, a...)
}