目前

  • 日志库需求分析
  • 日志库接口设计
  • 文件日志库开发
  • 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\n",
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\n",
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\n",
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\n",
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\n",
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\n",
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\n",
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()
}

  

【原创】go语言学习(十七)接口应用实战--日志库的更多相关文章

  1. GO学习-(14) Go语言基础之接口

    Go语言基础之接口 接口(interface)定义了一个对象的行为规范,只定义规范不实现,由具体的对象来实现规范的细节. 接口 接口类型 在Go语言中接口(interface)是一种类型,一种抽象的类 ...

  2. Go语言学习之11 日志收集系统kafka库实战

    本节主要内容: 1. 日志收集系统设计2. 日志客户端开发 1. 项目背景    a. 每个系统都有日志,当系统出现问题时,需要通过日志解决问题    b. 当系统机器比较少时,登陆到服务器上查看即可 ...

  3. [原创]java WEB学习笔记75:Struts2 学习之路-- 总结 和 目录

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  4. 20165315 C语言学习情况与Java学习目标

    20165315 C语言学习情况与Java学习目标 一.出色技能的获取经验 我从小便是一个中规中矩的人,在很多方面都是让成绩尽量保持在前百分之二十到三十这个范围内,比如我比较擅长的唱歌和乒乓球,但也不 ...

  5. 【转】朱兆祺教你如何攻破C语言学习、笔试与机试的难点(连载)

    原文网址:http://bbs.elecfans.com/jishu_354666_1_1.html 再过1个月又是一年应届毕业生应聘的高峰期了,为了方便应届毕业生应聘,笔者将大学四年C语言知识及去年 ...

  6. 使用C语言调用mysql数据库编程实战以及技巧

    今天编写使用C语言调用mysql数据库编程实战以及技巧.为其它IT同行作为參考,当然有错误能够留言,共同学习. 一.mysql数据库的C语言经常使用接口API 1.首先当然是链接数据库mysql_re ...

  7. (转)跟着老男孩一步步学习Shell高级编程实战

    原文:http://oldboy.blog.51cto.com/2561410/1264627/  跟着老男孩一步步学习Shell高级编程实战 原创作品,允许转载,转载时请务必以超链接形式标明文章 原 ...

  8. 全网最全postman接口测试教程和接口项目实战~从入门到精通!!!

    Postman实现接口测试内容大纲一览: ​ 一.什么是接口?为什么需要接口? ​ 接口指的是实体或者软件提供给外界的一种服务. 因为接口能使我们的实体或者软件的内部数据能够被外部进行修改.从而使得内 ...

  9. 【C语言学习】《C Primer Plus》第1章 概览

    学习总结 1.C语言于1972年由贝尔实验室的Dennis Ritchie在与Ken Thompson一起设计UNIX操作系统的时候开发的.的的设计构想来源于Ken Thompson的B语言.Anyw ...

随机推荐

  1. 在论坛中出现的比较难的sql问题:26(动态行专列+合并字符串、补足行数)

    原文:在论坛中出现的比较难的sql问题:26(动态行专列+合并字符串.补足行数) 最近,在论坛中,遇到了不少比较难的sql问题,虽然自己都能解决,但发现过几天后,就记不起来了,也忘记解决的方法了. 所 ...

  2. SQL SERVER 中如何获取日期(一个月的最后一日、一年的第一日等等)

    https://blog.csdn.net/deepwishly/article/details/9101307 这是计算一个月第一天的SQL 脚本:   SELECT DATEADD(mm, DAT ...

  3. 图片预先加载 preloadjs

    <body><div class="loading"> <div class="progress"></div> ...

  4. vue-resource发送请求

    <!DOCTYPE html> <html> <head> <title>vue-resource</title> <meta cha ...

  5. Linux 非互联网环境安装依赖包

    1 介绍 有的生产环境是没有网络的,我们部署rpm包的时候会出现缺少很多rpm包的依赖问题,都去网上下载实在太麻烦,今天介绍一个办法可以解决这一问题. 2 解决方案 找一台可以联网的机器,在上边下载相 ...

  6. 【Distributed】分布式解决方案【汇总】

    一.问题引出 二.分布式Session问题 三.网站跨域问题 四.分布式任务调度平台 五.分布式配置中心 六.分布式锁解决方案 七.缓存技术 一.问题引出 [Distributed]分布式系统中遇到的 ...

  7. CentOS7安装CDH 第八章:CDH中对服务和机器的添加与删除操作

    相关文章链接 CentOS7安装CDH 第一章:CentOS7系统安装 CentOS7安装CDH 第二章:CentOS7各个软件安装和启动 CentOS7安装CDH 第三章:CDH中的问题和解决方法 ...

  8. Python面向对象Day2

    一.组合 给一个类的对象分组一个属性,这个属性是另一个类的对象 意义:让类的对象与另一个类的对象产生关系,也叫类与类之间产生关系(继承也能) 好处: ① 让两个类之间产生关系 ② 某一个对象是独立存在 ...

  9. 现在记录几款好用的vscode的插件

    vscode 插件 Rainbow Brackets编码过程中,尤其在我们使用js进行函数式编程时,代码里会有很多的花括号,想要保证它们对称十分困难,所以就出现了上面小粉同学的尴尬局面,相信很多人都遇 ...

  10. VMware15 桥接模式无法上网

    1. 检查宿主机网络连接是否成功 2. 检查宿主机网络适配器列表是否有多余的 loop 等回环类型的适配器(楼主在安装npcap程序后系统出现回环类型的适配器,即把包发回本地,所有的虚拟机的桥接模式都 ...