本小结为日志器基础组件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