本小结为日志器基础组件loggor
它主要实现:
1.日志器配置信息配置(调试模式、日志类型编码、日志切分模式)
输出日志格式:
TIMEFORMAT LOG_TYPE BODY
2006/01/02 15:04:05 1日志信息
下载页面
http://download.csdn.net/download/hansewolf/9902133
- //@description 日志记录工具类
- /*
- 日志格式: 时间(系统时间) 日志类型(方法设置) 日志内容(动态输入)
- 日志类包含两个同步锁: 缓冲区锁-mu_buf 文件锁-mu_file
- 日志输入操作 Printf Println
- 1.获取缓冲区锁
- 2.写入缓冲区
- 3.释放缓冲区锁
- 4.A.调用bufWrite,B.等待定时调用bufWrite
- 日志输出操作 bufWrite
- 1.获取文件锁
- 2.判断缓冲区,不需写入则返回
- 3.获取缓冲区锁
- 4.写入缓冲区
- 5.释放缓冲区锁
- 日志监听操作 fileMonitor
- A.文件监听定时器到期fileCheck
- 1.判断是否需要文件重名,并后续操作
- 1.1.获取文件锁
- 1.2.再次判断文件是否需要重名
- 1.3.重命名文件
- 1.4.释放文件锁
- B.定时写入定时器到期bufWrite
- 文件定时写入bufWrite与文件监听fileMonitor时间间隔 t1,t2
- 防止文件碰撞(秒为单位时)需要满足 (n-1)t1%60 != (n-1)t2%60
- 顺序获取锁:缓冲锁-->文件锁
- */
- //@author chenbintao
- //@data 2016-08-04 17:47 调试代码无报错
- // 2017-07-07 18:59 优化定时检测线程计算公式
- package loggor
- import (
- "bytes"
- "encoding/json"
- "fmt"
- "io"
- "log"
- "math"
- "os"
- "path"
- "runtime/debug"
- "strings"
- "sync"
- "time"
- )
- const (
- _VERSION_ = "1.0.1" //版本
- DATEFORMAT = "2006-01-02" //日期格式(用于文件命名)
- TIMEFORMAT = "2006/01/02 15:04:05" //时间格式(日志时间格式)
- _SPACE = " " //参数分割
- _TABLE = "\t" //日志文件行分隔符
- _JOIN = "&" //参数连接符
- _FILE_OPERAT_MODE_ = 0644 //文件操作权限模式
- _FILE_CREAT_MODE_ = 0666 //文件建立权限模式
- _LABEL_ = "[_loggor_]" //标签
- )
- const (
- //日志文件存储模式
- LOG_FILE_SAVE_USUAL = 1 //普通模式,不分割
- LOG_FILE_SAVE_SIZE = 2 //大小分割
- LOG_FILE_SAVE_DATE = 3 //日期分割
- )
- const (
- //文件大小单位
- _ = iota
- KB int64 = 1 << (iota * 10)
- MB
- GB
- TB
- )
- const (
- _EXTEN_NAME_ = ".log" //日志文件后缀名
- _CHECK_TIME_ time.Duration = 900 * time.Millisecond //定时检测是否分割检测周期
- _WRITE_TIME_ time.Duration = 1300 * time.Millisecond //定时写入文件周期
- )
- var (
- IS_DEBUG = false //调试模式
- TIMEER_WRITE = false //定时写入文件
- )
- type LOGGER interface {
- SetDebug(bool) //设置日志文件路径及名称
- SetType(uint) //设置日志类型
- SetRollingFile(string,string,int32,int64,int64) //按照文件大小分割
- SetRollingDaily(string,string) //按照日期分割
- SetRollingNormal(string,string) //设置普通模式
- Close() //关闭
- Println(a ...interface{}) //打印日志
- Printf(format string,a ...interface{}) //格式化输出
- }
- //==================================================================日志记录器
- type Logger struct {
- log_type uint //日志类型
- path string //日志文件路径
- dir string //目录
- filename string //文件名
- maxFileSize int64 //文件大小
- maxFileCount int32 //文件个数
- dailyRolling bool //日分割
- sizeRolling bool //大小分割
- nomalRolling bool //普通模式(不分割)
- _suffix int //大小分割文件的当前序号
- _date *time.Time //文件时间
- mu_buf *sync.Mutex //缓冲锁
- mu_file *sync.Mutex //文件锁
- logfile *os.File //文件句柄
- timer *time.Timer //监视定时器
- writeTimer *time.Timer //批量写入定时器
- buf *bytes.Buffer //缓冲区(公用buf保证数据写入的顺序性)
- }
- /**获取日志对象**/
- func New() *Logger {
- this := &Logger{}
- this.buf = &bytes.Buffer{}
- this.mu_buf = new(sync.Mutex)
- this.mu_file = new(sync.Mutex)
- return this
- }
- /**格式行输出**/
- func (this *Logger) Printf(format string,a ...interface{}) {
- defer func() {
- if !TIMEER_WRITE {
- this.bufWrite()
- }
- }()
- tp := fmt.Sprintf(format,a...)
- //tp = ToLineString(tp)
- this.mu_buf.Lock()
- defer this.mu_buf.Unlock()
- this.buf.WriteString(
- fmt.Sprintf(
- "%s\t%d\t%s\n",time.Now().Format(TIMEFORMAT),this.log_type,tp,),)
- }
- /**逐行输出**/
- func (this *Logger) Println(a ...interface{}) {
- defer func() {
- if !TIMEER_WRITE {
- this.bufWrite()
- }
- }()
- tp := fmt.Sprint(a...)
- //tp = ToLineString(tp)
- this.mu_buf.Lock()
- defer this.mu_buf.Unlock()
- this.buf.WriteString(
- fmt.Sprintf(
- "%s\t%d\t%s\n",)
- }
- /**测试模式**/
- func (this *Logger) SetDebug(is_debug bool) {
- IS_DEBUG = is_debug
- }
- /**定时写入**/
- func (this *Logger) SetTimeWrite(time_write bool) *Logger {
- TIMEER_WRITE = time_write
- return this
- }
- /**日志类型**/
- func (this *Logger) SetType(tp uint) {
- this.log_type = tp
- }
- /**大小分割**/
- func (this *Logger) SetRollingFile(dir,_file string,maxn int32,maxs int64,_u int64) {
- //0.输入合法性
- if this.sizeRolling ||
- this.dailyRolling ||
- this.nomalRolling {
- log.Println(_LABEL_,"mode can't be changed!")
- return
- }
- //1.设置各模式标志符
- this.sizeRolling = true
- this.dailyRolling = false
- this.nomalRolling = false
- //2.设置日志器各参数
- this.maxFileCount = maxn
- this.maxFileSize = maxs * int64(_u)
- this.dir = dir
- this.filename = _file
- for i := 1; i <= int(maxn); i++ {
- sizeFile := fmt.Sprintf(
- dir,_file,_EXTEN_NAME_,".",fmt.Sprintf("%05d",i),)
- if isExist(sizeFile) {
- this._suffix = i
- } else {
- break
- }
- }
- //3.实时文件写入
- this.path = fmt.Sprint(
- dir,)
- this.startLogger(this.path)
- }
- /**日期分割**/
- func (this *Logger) SetRollingDaily(dir,_file string) {
- //0.输入合法性
- if this.sizeRolling ||
- this.dailyRolling ||
- this.nomalRolling {
- log.Println(_LABEL_,"mode can't be changed!")
- return
- }
- //1.设置各模式标志符
- this.sizeRolling = false
- this.dailyRolling = true
- this.nomalRolling = false
- //2.设置日志器各参数
- this.dir = dir
- this.filename = _file
- this._date = getNowFormDate(DATEFORMAT)
- this.startLogger(
- fmt.Sprint(
- this.dir,this.filename,this._date.Format(DATEFORMAT),)
- }
- /**普通模式**/
- func (this *Logger) SetRollingNormal(dir,"mode can't be changed!")
- return
- }
- //1.设置各模式标志符
- this.sizeRolling = false
- this.dailyRolling = false
- this.nomalRolling = true
- //2.设置日志器各参数
- this.dir = dir
- this.filename = _file
- this.startLogger(
- fmt.Sprint(
- dir,)
- }
- /**关闭日志器**/
- func (this *Logger) Close() {
- //0.获取锁
- this.mu_buf.Lock()
- defer this.mu_buf.Unlock()
- this.mu_file.Lock()
- defer this.mu_file.Unlock()
- //1.关闭
- if nil != this.timer {
- this.timer.Stop()
- }
- if nil != this.writeTimer {
- this.writeTimer.Stop()
- }
- if this.logfile != nil {
- err := this.logfile.Close()
- if err != nil {
- log.Println(_LABEL_,"file close err",err)
- }
- } else {
- log.Println(_LABEL_,"file has been closed!")
- }
- //2.清理
- this.sizeRolling = false
- this.dailyRolling = false
- this.nomalRolling = false
- }
- //==================================================================内部工具方法
- //初始日志记录器(各日志器统一调用)
- func (this *Logger) startLogger(tp string) {
- defer func() {
- if e,ok := recover().(error); ok {
- log.Println(_LABEL_,"WARN: panic - %v",e)
- log.Println(_LABEL_,string(debug.Stack()))
- }
- }()
- //1.初始化空间
- var err error
- this.buf = &bytes.Buffer{}
- this.mu_buf = new(sync.Mutex)
- this.mu_file = new(sync.Mutex)
- this.path = tp
- checkFileDir(tp)
- this.logfile,err = os.OpenFile(
- tp,os.O_RDWR|os.O_APPEND|os.O_CREATE,_FILE_OPERAT_MODE_,)
- if nil != err {
- log.Println(_LABEL_,"OpenFile err!")
- }
- //2.开启监控线程
- go func() {
- this.timer = time.NewTimer(_CHECK_TIME_)
- this.writeTimer = time.NewTimer(_WRITE_TIME_)
- if !TIMEER_WRITE {
- this.writeTimer.Stop()
- }
- for {
- select {
- //定时检测是否分割
- case <-this.timer.C:
- this.fileCheck()
- if IS_DEBUG && false {
- log.Printf("*") //心跳
- }
- break
- //定时写入文件(定时写入,会导致延时)
- case <-this.writeTimer.C:
- this.bufWrite()
- if IS_DEBUG && false {
- log.Printf(".") //心跳
- }
- break
- }
- }
- }()
- if IS_DEBUG {
- jstr,err := json.Marshal(this)
- if nil == err {
- log.Println(_LABEL_,_VERSION_,string(jstr))
- }
- }
- }
- //文件检测(会锁定文件)
- func (this *Logger) fileCheck() {
- //0.边界判断
- if nil == this.mu_file ||
- nil == this.logfile ||
- "" == this.path {
- return
- }
- defer func() {
- if e,string(debug.Stack()))
- }
- }()
- //1.重命名判断
- var RENAME_FLAG bool = false
- var CHECK_TIME time.Duration = _CHECK_TIME_
- this.timer.Stop()
- defer this.timer.Reset(CHECK_TIME)
- if this.dailyRolling {
- //日分割模式
- now := getNowFormDate(DATEFORMAT)
- if nil != now &&
- nil != this._date &&
- now.After(*this._date) {
- //超时重名
- RENAME_FLAG = true
- } else {
- //检测间隔动态调整(1/60分割时间差值+基准检测时长)
- du := this._date.UnixNano() - now.UnixNano()
- abs := math.Abs(float64(du))
- CHECK_TIME += time.Duration(abs / 60)
- }
- } else if this.sizeRolling {
- //文件大小模式
- fileSize := fileSize(this.path)
- if "" != this.path &&
- this.maxFileCount >= 1 &&
- fileSize >= this.maxFileSize {
- //超量重名
- RENAME_FLAG = true
- } else {
- //检测时长(假设磁盘写入带宽100M/S)
- du := fileSize - this.maxFileSize
- abs := math.Abs(float64(du))
- CHECK_TIME += time.Duration(((uint64(abs) >> 2 * 10) / 100)) * time.Second
- }
- } else if this.nomalRolling {
- //普通模式
- RENAME_FLAG = false
- }
- //2.重名操作
- if RENAME_FLAG {
- this.mu_file.Lock()
- defer this.mu_file.Unlock()
- if IS_DEBUG {
- log.Println(_LABEL_,this.path,"is need rename.")
- }
- this.fileRename()
- }
- return
- }
- //重命名文件
- func (this *Logger) fileRename() {
- //1.生成文件名称
- var err error
- var newName string
- var oldName string
- defer func() {
- if IS_DEBUG {
- log.Println(
- _LABEL_,oldName,"->",newName,":",err,)
- }
- }()
- if this.dailyRolling {
- //日期分割模式(文件不重命名)
- oldName = this.path
- newName = this.path
- this._date = getNowFormDate(DATEFORMAT)
- this.path = fmt.Sprint(
- this.dir,)
- } else if this.sizeRolling {
- //大小分割模式(1,2,3....)
- suffix := int(this._suffix%int(this.maxFileCount) + 1)
- oldName = this.path
- newName = fmt.Sprint(
- this.path,suffix),)
- this._suffix = suffix
- this.path = this.path
- } else if this.nomalRolling {
- //常规模式
- }
- //2.处理旧文件
- this.logfile.Close()
- if "" != oldName && "" != newName && oldName != newName {
- if isExist(newName) {
- //删除旧文件
- err := os.Remove(newName)
- if nil != err {
- log.Println(_LABEL_,"remove file err",err.Error())
- }
- }
- err = os.Rename(oldName,newName)
- if err != nil {
- //重名旧文件
- log.Println(_LABEL_,"rename file err",err.Error())
- }
- }
- //3.创建新文件
- this.logfile,err = os.OpenFile(
- this.path,)
- if err != nil {
- log.Println(_LABEL_,"creat file err",err.Error())
- }
- return
- }
- //缓冲写入文件
- func (this *Logger) bufWrite() {
- //0.边界处理
- if nil == this.buf ||
- "" == this.path ||
- nil == this.logfile ||
- nil == this.mu_buf ||
- nil == this.mu_file ||
- this.buf.Len() <= 0 {
- return
- }
- //1.数据写入
- var WRITE_TIME time.Duration = _WRITE_TIME_
- if nil != this.writeTimer {
- this.writeTimer.Stop()
- defer this.writeTimer.Reset(WRITE_TIME)
- }
- this.mu_file.Lock()
- defer this.mu_file.Unlock()
- this.mu_buf.Lock()
- defer this.mu_buf.Unlock()
- defer this.buf.Reset()
- n,err := io.WriteString(this.logfile,this.buf.String())
- if nil != err {
- //写入失败,校验文件,不存在则创建
- checkFileDir(this.path)
- this.logfile,err = os.OpenFile(
- this.path,)
- if nil != err {
- log.Println(_LABEL_,"log bufWrite() err!")
- }
- }
- //根据缓冲压力进行动态设置写入间隔
- if n == 0 {
- WRITE_TIME = _WRITE_TIME_
- } else {
- WRITE_TIME = WRITE_TIME * time.Duration(n/n)
- }
- }
- //==================================================================辅助方法
- //获取文件大小
- func fileSize(file string) int64 {
- this,e := os.Stat(file)
- if e != nil {
- if IS_DEBUG {
- log.Println(_LABEL_,e.Error())
- }
- return 0
- }
- return this.Size()
- }
- //判断路径是否存在
- func isExist(path string) bool {
- _,err := os.Stat(path)
- return err == nil || os.IsExist(err)
- }
- //检查文件路径文件夹,不存在则创建
- func checkFileDir(tp string) {
- p,_ := path.Split(tp)
- d,err := os.Stat(p)
- if err != nil || !d.IsDir() {
- if err := os.MkdirAll(p,_FILE_CREAT_MODE_); err != nil {
- log.Println(_LABEL_,"CheckFileDir() Creat dir faile!")
- }
- }
- }
- //获取当前指定格式的日期
- func getNowFormDate(form string) *time.Time {
- t,err := time.Parse(form,time.Now().Format(form))
- if nil != err {
- log.Println(_LABEL_,"getNowFormDate()",err.Error())
- t = time.Time{}
- return &t
- }
- return &t
- }
- //字符串安全转义
- func ToLineString(src string) string {
- src = strings.Replace(src,"\n","\\n",-1)
- src = strings.Replace(src,"\r","\\r",-1)
- return src
- }
- //字符串安全反转义
- func FromLineString(src string) string {
- src = strings.Replace(src,-1)
- return src
- }
- //==================================================================测试用例
- func Test() {
- logg := New()
- logg.SetType(1)
- logg.SetRollingNormal("./logs","logg")
- logg.Println("hello world!")
- }
较早前版本可参见:http://studygolang.com/topics/2620