zoukankan      html  css  js  c++  java
  • 【原创】go语言学习(十七)接口应用实战--日志库

    目前

    • 日志库需求分析
    • 日志库接口设计
    • 文件日志库开发
    • Console日志开发
    • 日志使用以及测试

    日志库需求分析

    1、日志库需求分析

    A. 程序运行是个黑盒
    B. 而日志是程序运行的外在表现
    C. 通过日志,可以知道程序的健康状态

    2、日志库需求分析

    A. Debug级别:用来调试程序,日志最详细。对程序性能影响比较大。
    B. Trace级别:用来追踪问题。
    C. Info级别:打印程序运行过程中比较重要的信息,比如访问日志
    D. Warn级别:警告日志,说明程序运行出现了潜在的问题
    E. Error级别:错误日志,程序运行发生错误,但不影响程序运行。
    F. Fatal级别:严重错误日志,发生的错误会导致程序退出

    3、日志库需求分析

    A. 直接输出到控制台
    B. 打印到文件里
    C. 直接打印到网络中,比如kafka

    日志库接口设计

    1、为什么使用接口?

    A. 定义日志库的规范或者标准
    B. 易于可扩展性
    C. 利于程序的可维护性

    2、 日志库设计

    A. 打印各个level的日志
    B. 设置级别
    C. 构造函数

    文件日志库开发

    1、关键代码

    package logger
    
    import (
    	"fmt"
    	"os"
    	"strconv"
    	"time"
    )
    
    type FileLogger struct {
    	level    int
    	logPath  string
    	logName  string
    	file     *os.File
    	warnFile *os.File
    	// 异步通道
    	LogDataChan chan *LogData
    	// 日志切分
    	logSplitType  int
    	logSplitSize  int64
    	lastSplitHour int
    }
    
    func NewFileLogger(config map[string]string) (log LogInterface, err error) {
    	logPath, ok := config["log_path"]
    	if !ok {
    		err = fmt.Errorf("not found log_path")
    		return
    	}
    	logName, ok := config["log_name"]
    	if !ok {
    		err = fmt.Errorf("not found log_name")
    		return
    	}
    	logLevel, ok := config["log_level"]
    	if !ok {
    		err = fmt.Errorf("not found log_level")
    		return
    	}
    	level := getLogLevel(logLevel)
    
    	// 异步管道初始化
    	logChanSize, ok := config["log_chan_size"]
    	if !ok {
    		logChanSize = "50000"
    	}
    	//检查logChanSize转数字
    	chanSize, err := strconv.Atoi(logChanSize)
    	if err != nil {
    		chanSize = 50000
    	}
    
    	//日志切片
    	var logSplitType int = LogSplitTypeHour
    	var logSplitSize int64
    	logSplitStr, ok := config["log_plit_type"]
    	if !ok {
    		logSplitStr = "hour"
    	} else {
    		if logSplitStr == "size" {
    			logSplitSizeStr, ok := config["log_split_size"]
    			if !ok {
    				logSplitSizeStr = "104857600"
    			}
    
    			logSplitSize, err = strconv.ParseInt(logSplitSizeStr, 10, 64)
    			if err != nil {
    				logSplitSize = 104857600
    			}
    
    			logSplitType = LogSplitTypeSize
    		} else {
    			logSplitType = LogSplitTypeHour
    		}
    	}
    
    	log = &FileLogger{
    		level:   level,
    		logPath: logPath,
    		logName: logName,
    		// 异步写日志
    		LogDataChan: make(chan *LogData, chanSize),
    		// 日志切分
    		logSplitSize:  logSplitSize,
    		logSplitType:  logSplitType,
    		lastSplitHour: time.Now().Hour(),
    	}
    
    	// 调试日志分割
    	// fmt.Println(logSplitSize, logSplitType, logSplitStr)
    
    	log.Init()
    	return
    }
    
    func (f *FileLogger) SetLevel(level int) {
    	if level < LogLevelDebug || level > LogLevelFatal {
    		level = LogLevelDebug
    	}
    	f.level = level
    }
    
    func (f *FileLogger) Init() {
    	filename := fmt.Sprintf("%s/%s.log", f.logPath, f.logName)
    	file, err := os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0755)
    	if err != nil {
    		panic(fmt.Sprintf("open faile %s failed, err:%v", filename, err))
    	}
    
    	f.file = file
    
    	// 写错误日志和fatal日志文件
    	filename = fmt.Sprintf("%s/%s.log.wf", f.logPath, f.logName)
    	file, err = os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0755)
    	if err != nil {
    		panic(fmt.Sprintf("open faile %s failed, err:%v", filename, err))
    	}
    	f.warnFile = file
    	go f.writeLogBackground()
    }
    
    func (f *FileLogger) splitFileHour(warnFile bool) {
    	now := time.Now()
    	hour := now.Hour()
    	if hour == f.lastSplitHour {
    		return
    	}
    
    	f.lastSplitHour = hour
    	var backupFilename string
    	var filename string
    
    	if warnFile {
    		backupFilename = fmt.Sprintf("%s/%s.log.wf_%4d%02d%02d%02d",
    			f.logPath, f.logName, now.Year(), now.Month(), now.Day(), f.lastSplitHour)
    
    		filename = fmt.Sprintf("%s/%s.log.wf", f.logPath, f.logName)
    	} else {
    		backupFilename = fmt.Sprintf("%s/%s.log_%4d%02d%02d%02d",
    			f.logPath, f.logName, now.Year(), now.Month(), now.Day(), f.lastSplitHour)
    		filename = fmt.Sprintf("%s/%s.log", f.logPath, f.logName)
    	}
    
    	file := f.file
    	if warnFile {
    		file = f.warnFile
    	}
    
    	file.Close()
    	os.Rename(filename, backupFilename)
    
    	file, err := os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0755)
    	if err != nil {
    		return
    	}
    
    	if warnFile {
    		f.warnFile = file
    	} else {
    		f.file = file
    	}
    }
    
    func (f *FileLogger) splitFileSize(warnFile bool) {
    
    	file := f.file
    	if warnFile {
    		file = f.warnFile
    	}
    
    	statInfo, err := file.Stat()
    	if err != nil {
    		return
    	}
    
    	fileSize := statInfo.Size()
    	if fileSize <= f.logSplitSize {
    		return
    	}
    
    	var backupFilename string
    	var filename string
    
    	now := time.Now()
    	if warnFile {
    		backupFilename = fmt.Sprintf("%s/%s.log.wf_%4d%02d%02d%02d%02d%02d",
    			f.logPath, f.logName, now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second())
    
    		filename = fmt.Sprintf("%s/%s.log.wf", f.logPath, f.logName)
    	} else {
    		backupFilename = fmt.Sprintf("%s/%s.log_%4d%02d%02d%02d",
    			f.logPath, f.logName, now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second())
    		filename = fmt.Sprintf("%s/%s.log", f.logPath, f.logName)
    	}
    
    	file.Close()
    	os.Rename(filename, backupFilename)
    
    	file, err = os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0755)
    	if err != nil {
    		return
    	}
    
    	if warnFile {
    		f.warnFile = file
    	} else {
    		f.file = file
    	}
    }
    
    func (f *FileLogger) checkSplitFile(warnFile bool) {
    	if f.logSplitType == LogSplitTypeHour {
    		f.splitFileHour(warnFile)
    		return
    	}
    
    	f.splitFileSize(warnFile)
    }
    
    func (f *FileLogger) writeLogBackground() {
    	for data := range f.LogDataChan {
    		var file *os.File = f.file
    		if data.WarnAndFatal {
    			file = f.warnFile
    		}
    
    		f.checkSplitFile(data.WarnAndFatal)
    
    		fmt.Fprintf(file, "%s %s (%s:%s:%d) %s
    ",
    			data.LevelStr, data.FileName, data.FuncName, data.LineNo,
    			data.Message)
    	}
    }
    
    func (f *FileLogger) Debug(format string, args ...interface{}) {
    	if f.level > LogLevelDebug {
    		return
    	}
    	logData := writeLog(LogLevelDebug, format, args...)
    	// 判断队列有没有满
    	select {
    	case f.LogDataChan <- logData:
    	default:
    	}
    }
    
    func (f *FileLogger) Trace(format string, args ...interface{}) {
    	if f.level > LogLevelTrace {
    		return
    	}
    	logData := writeLog(LogLevelTrace, format, args...)
    	// 判断队列有没有满
    	select {
    	case f.LogDataChan <- logData:
    	default:
    	}
    
    }
    
    func (f *FileLogger) Info(format string, args ...interface{}) {
    	if f.level > LogLevelInfo {
    		return
    	}
    	logData := writeLog(LogLevelInfo, format, args...)
    	// 判断队列有没有满
    	select {
    	case f.LogDataChan <- logData:
    	default:
    	}
    
    }
    
    func (f *FileLogger) Warn(format string, args ...interface{}) {
    	if f.level > LogLevelWarn {
    		return
    	}
    	logData := writeLog(LogLevelWarn, format, args...)
    	// 判断队列有没有满
    	select {
    	case f.LogDataChan <- logData:
    	default:
    	}
    }
    
    func (f *FileLogger) Fatal(format string, args ...interface{}) {
    	if f.level > LogLevelFatal {
    		return
    	}
    	logData := writeLog(LogLevelFatal, format, args...)
    	// 判断队列有没有满
    	select {
    	case f.LogDataChan <- logData:
    	default:
    	}
    
    }
    
    func (f *FileLogger) Error(format string, args ...interface{}) {
    	if f.level > LogLevelError {
    		return
    	}
    	logData := writeLog(LogLevelError, format, args...)
    	// 判断队列有没有满
    	select {
    	case f.LogDataChan <- logData:
    	default:
    	}
    
    }
    
    func (f *FileLogger) Close() {
    	f.file.Close()
    	f.warnFile.Close()
    }
    

    Console日志开发

    1、关键代码

    package logger
    
    import (
    	"fmt"
    	"os"
    )
    
    type ConsoleLogger struct {
    	level int
    }
    
    func NewConsoleLogger(config map[string]string) (log LogInterface, err error) {
    	logLevel, ok := config["log_level"]
    	if !ok {
    		err = fmt.Errorf("not found log_level")
    		return
    	}
    	level := getLogLevel(logLevel)
    	log = &ConsoleLogger{
    		level: level,
    	}
    	return
    }
    
    func (c *ConsoleLogger) Init() {
    
    }
    
    func (c *ConsoleLogger) SetLevel(level int) {
    	if level < LogLevelDebug || level > LogLevelFatal {
    		level = LogLevelDebug
    	}
    	c.level = level
    }
    
    func (c *ConsoleLogger) Debug(format string, args ...interface{}) {
    	if c.level > LogLevelDebug {
    		return
    	}
    	logData := writeLog(LogLevelDebug, format, args...)
    	fmt.Fprintf(os.Stdout, "%s %s (%s:%s:%d) %s
    ",
    		logData.LevelStr, logData.FileName, logData.FuncName, logData.LineNo,
    		logData.Message)
    }
    
    func (c *ConsoleLogger) Trace(format string, args ...interface{}) {
    	if c.level > LogLevelTrace {
    		return
    	}
    	logData := writeLog(LogLevelTrace, format, args...)
    	fmt.Fprintf(os.Stdout, "%s %s (%s:%s:%d) %s
    ",
    		logData.LevelStr, logData.FileName, logData.FuncName, logData.LineNo,
    		logData.Message)
    
    }
    
    func (c *ConsoleLogger) Info(format string, args ...interface{}) {
    	if c.level > LogLevelInfo {
    		return
    	}
    	logData := writeLog(LogLevelInfo, format, args...)
    	fmt.Fprintf(os.Stdout, "%s %s (%s:%s:%d) %s
    ",
    		logData.LevelStr, logData.FileName, logData.FuncName, logData.LineNo,
    		logData.Message)
    
    }
    
    func (c *ConsoleLogger) Warn(format string, args ...interface{}) {
    	if c.level > LogLevelWarn {
    		return
    	}
    	logData := writeLog(LogLevelWarn, format, args...)
    	fmt.Fprintf(os.Stdout, "%s %s (%s:%s:%d) %s
    ",
    		logData.LevelStr, logData.FileName, logData.FuncName, logData.LineNo,
    		logData.Message)
    
    }
    
    func (c *ConsoleLogger) Fatal(format string, args ...interface{}) {
    	if c.level > LogLevelFatal {
    		return
    	}
    	logData := writeLog(LogLevelFatal, format, args...)
    	fmt.Fprintf(os.Stdout, "%s %s (%s:%s:%d) %s
    ",
    		logData.LevelStr, logData.FileName, logData.FuncName, logData.LineNo,
    		logData.Message)
    
    }
    
    func (c *ConsoleLogger) Error(format string, args ...interface{}) {
    	if c.level > LogLevelError {
    		return
    	}
    	logData := writeLog(LogLevelError, format, args...)
    	fmt.Fprintf(os.Stdout, "%s %s (%s:%s:%d) %s
    ",
    		logData.LevelStr, logData.FileName, logData.FuncName, logData.LineNo,
    		logData.Message)
    
    }
    
    func (c *ConsoleLogger) Close() {
    }
    

      

    日志使用以及测试

    1、关键代码

    package logger
    
    import "testing"
    
    // run test| debug test
    func TestFileLogger(t *testing.T) {
    	logger := NewFileLogger(LogLevelDebug, "d:/logs/", "test")
    	logger.Debug("user id[%d] is come from chaina", 3242342)
    	logger.Warn("test warn log")
    	logger.Fatal("test fatal log")
    	logger.Close()
    }
    
    // run test| debug test
    func TestConsoleLogger(t *testing.T) {
    	logger := NewConsoleLogger(LogLevelDebug)
    	logger.Debug("user id[%d] is come from chaina", 3242342)
    	logger.Warn("test warn log")
    	logger.Fatal("test fatal log")
    	logger.Close()
    }
    

      

  • 相关阅读:
    web.xml配置详解(转载)
    http协议无状态中的 "状态" 到底指的是什么?!(转载)
    sublime配置python运行环境
    Eclipse下载安装教程
    局域网访问文件共享了访问不了解决方法
    ThreadLocal实现线程级上下文
    Spring中ApplicationEvent和ApplicationListener封装
    MySQL根据出生日期计算年龄的五种方法比较
    常用正则
    java.lang.OutOfMemoryError: PermGen space有效解决方法
  • 原文地址:https://www.cnblogs.com/wangshuyang/p/11815273.html
Copyright © 2011-2022 走看看