golang环境下的日志记录器-系列之一

前端之家收集整理的这篇文章主要介绍了golang环境下的日志记录器-系列之一前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

本小结为日志器基础组件loggor

它主要实现:

1.日志器配置信息配置(调试模式、日志类型编码、日志切分模式)

2.日志文件的建立与数据输出

输出日志格式:

TIMEFORMAT LOG_TYPE BODY
2006/01/02 15:04:05 1日志信息


下载页面

http://download.csdn.net/download/hansewolf/9902133

点击打开链接


  1. //@description 日志记录工具类
  2. /*
  3. 日志格式: 时间(系统时间) 日志类型(方法设置) 日志内容(动态输入)
  4. 日志类包含两个同步锁: 缓冲区锁-mu_buf 文件锁-mu_file
  5. 日志输入操作 Printf Println
  6. 1.获取缓冲区锁
  7. 2.写入缓冲区
  8. 3.释放缓冲区锁
  9. 4.A.调用bufWrite,B.等待定时调用bufWrite
  10. 日志输出操作 bufWrite
  11. 1.获取文件
  12. 2.判断缓冲区,不需写入则返回
  13. 3.获取缓冲区锁
  14. 4.写入缓冲区
  15. 5.释放缓冲区锁
  16. 日志监听操作 fileMonitor
  17. A.文件监听定时器到期fileCheck
  18. 1.判断是否需要文件重名,并后续操作
  19. 1.1.获取文件
  20. 1.2.再次判断文件是否需要重名
  21. 1.3.重命名文件
  22. 1.4.释放文件
  23. B.定时写入定时器到期bufWrite
  24.  
  25. 文件定时写入bufWrite与文件监听fileMonitor时间间隔 t1,t2
  26. 防止文件碰撞(秒为单位时)需要满足 (n-1)t1%60 != (n-1)t2%60
  27. 顺序获取锁:缓冲锁-->文件
  28. */
  29. //@author chenbintao
  30. //@data 2016-08-04 17:47 调试代码无报错
  31. // 2017-07-07 18:59 优化定时检测线程计算公式
  32.  
  33. package loggor
  34.  
  35. import (
  36. "bytes"
  37. "encoding/json"
  38. "fmt"
  39. "io"
  40. "log"
  41. "math"
  42. "os"
  43. "path"
  44. "runtime/debug"
  45. "strings"
  46. "sync"
  47. "time"
  48. )
  49.  
  50. const (
  51. _VERSION_ = "1.0.1" //版本
  52. DATEFORMAT = "2006-01-02" //日期格式(用于文件命名)
  53. TIMEFORMAT = "2006/01/02 15:04:05" //时间格式(日志时间格式)
  54. _SPACE = " " //参数分割
  55. _TABLE = "\t" //日志文件行分隔符
  56. _JOIN = "&" //参数连接符
  57. _FILE_OPERAT_MODE_ = 0644 //文件操作权限模式
  58. _FILE_CREAT_MODE_ = 0666 //文件建立权限模式
  59. _LABEL_ = "[_loggor_]" //标签
  60. )
  61. const (
  62. //日志文件存储模式
  63. LOG_FILE_SAVE_USUAL = 1 //普通模式,不分割
  64. LOG_FILE_SAVE_SIZE = 2 //大小分割
  65. LOG_FILE_SAVE_DATE = 3 //日期分割
  66. )
  67. const (
  68. //文件大小单位
  69. _ = iota
  70. KB int64 = 1 << (iota * 10)
  71. MB
  72. GB
  73. TB
  74. )
  75. const (
  76. _EXTEN_NAME_ = ".log" //日志文件后缀名
  77. _CHECK_TIME_ time.Duration = 900 * time.Millisecond //定时检测是否分割检测周期
  78. _WRITE_TIME_ time.Duration = 1300 * time.Millisecond //定时写入文件周期
  79. )
  80.  
  81. var (
  82. IS_DEBUG = false //调试模式
  83. TIMEER_WRITE = false //定时写入文件
  84. )
  85.  
  86. type LOGGER interface {
  87. SetDebug(bool) //设置日志文件路径及名称
  88. SetType(uint) //设置日志类型
  89. SetRollingFile(string,string,int32,int64,int64) //按照文件大小分割
  90. SetRollingDaily(string,string) //按照日期分割
  91. SetRollingNormal(string,string) //设置普通模式
  92. Close() //关闭
  93. Println(a ...interface{}) //打印日志
  94. Printf(format string,a ...interface{}) //格式化输出
  95. }
  96.  
  97. //==================================================================日志记录器
  98. type Logger struct {
  99. log_type uint //日志类型
  100. path string //日志文件路径
  101. dir string //目录
  102. filename string //文件
  103. maxFileSize int64 //文件大小
  104. maxFileCount int32 //文件个数
  105. dailyRolling bool //日分割
  106. sizeRolling bool //大小分割
  107. nomalRolling bool //普通模式(不分割)
  108. _suffix int //大小分割文件的当前序号
  109. _date *time.Time //文件时间
  110. mu_buf *sync.Mutex //缓冲锁
  111. mu_file *sync.Mutex //文件
  112. logfile *os.File //文件句柄
  113. timer *time.Timer //监视定时器
  114. writeTimer *time.Timer //批量写入定时器
  115. buf *bytes.Buffer //缓冲区(公用buf保证数据写入的顺序性)
  116. }
  117.  
  118. /**获取日志对象**/
  119. func New() *Logger {
  120. this := &Logger{}
  121. this.buf = &bytes.Buffer{}
  122. this.mu_buf = new(sync.Mutex)
  123. this.mu_file = new(sync.Mutex)
  124.  
  125. return this
  126. }
  127.  
  128. /**格式行输出**/
  129. func (this *Logger) Printf(format string,a ...interface{}) {
  130. defer func() {
  131. if !TIMEER_WRITE {
  132. this.bufWrite()
  133. }
  134. }()
  135.  
  136. tp := fmt.Sprintf(format,a...)
  137. //tp = ToLineString(tp)
  138. this.mu_buf.Lock()
  139. defer this.mu_buf.Unlock()
  140. this.buf.WriteString(
  141. fmt.Sprintf(
  142. "%s\t%d\t%s\n",time.Now().Format(TIMEFORMAT),this.log_type,tp,),)
  143. }
  144.  
  145. /**逐行输出**/
  146. func (this *Logger) Println(a ...interface{}) {
  147. defer func() {
  148. if !TIMEER_WRITE {
  149. this.bufWrite()
  150. }
  151. }()
  152.  
  153. tp := fmt.Sprint(a...)
  154. //tp = ToLineString(tp)
  155. this.mu_buf.Lock()
  156. defer this.mu_buf.Unlock()
  157. this.buf.WriteString(
  158. fmt.Sprintf(
  159. "%s\t%d\t%s\n",)
  160. }
  161.  
  162. /**测试模式**/
  163. func (this *Logger) SetDebug(is_debug bool) {
  164. IS_DEBUG = is_debug
  165. }
  166.  
  167. /**定时写入**/
  168. func (this *Logger) SetTimeWrite(time_write bool) *Logger {
  169. TIMEER_WRITE = time_write
  170.  
  171. return this
  172. }
  173.  
  174. /**日志类型**/
  175. func (this *Logger) SetType(tp uint) {
  176. this.log_type = tp
  177. }
  178.  
  179. /**大小分割**/
  180. func (this *Logger) SetRollingFile(dir,_file string,maxn int32,maxs int64,_u int64) {
  181. //0.输入合法性
  182. if this.sizeRolling ||
  183. this.dailyRolling ||
  184. this.nomalRolling {
  185. log.Println(_LABEL_,"mode can't be changed!")
  186. return
  187. }
  188.  
  189. //1.设置各模式标志符
  190. this.sizeRolling = true
  191. this.dailyRolling = false
  192. this.nomalRolling = false
  193.  
  194. //2.设置日志器各参数
  195. this.maxFileCount = maxn
  196. this.maxFileSize = maxs * int64(_u)
  197. this.dir = dir
  198. this.filename = _file
  199. for i := 1; i <= int(maxn); i++ {
  200. sizeFile := fmt.Sprintf(
  201. dir,_file,_EXTEN_NAME_,".",fmt.Sprintf("%05d",i),)
  202. if isExist(sizeFile) {
  203. this._suffix = i
  204. } else {
  205. break
  206. }
  207. }
  208. //3.实时文件写入
  209. this.path = fmt.Sprint(
  210. dir,)
  211. this.startLogger(this.path)
  212. }
  213.  
  214. /**日期分割**/
  215. func (this *Logger) SetRollingDaily(dir,_file string) {
  216. //0.输入合法性
  217. if this.sizeRolling ||
  218. this.dailyRolling ||
  219. this.nomalRolling {
  220. log.Println(_LABEL_,"mode can't be changed!")
  221. return
  222. }
  223.  
  224. //1.设置各模式标志符
  225. this.sizeRolling = false
  226. this.dailyRolling = true
  227. this.nomalRolling = false
  228.  
  229. //2.设置日志器各参数
  230. this.dir = dir
  231. this.filename = _file
  232. this._date = getNowFormDate(DATEFORMAT)
  233. this.startLogger(
  234. fmt.Sprint(
  235. this.dir,this.filename,this._date.Format(DATEFORMAT),)
  236. }
  237.  
  238. /**普通模式**/
  239. func (this *Logger) SetRollingNormal(dir,"mode can't be changed!")
  240. return
  241. }
  242.  
  243. //1.设置各模式标志符
  244. this.sizeRolling = false
  245. this.dailyRolling = false
  246. this.nomalRolling = true
  247.  
  248. //2.设置日志器各参数
  249. this.dir = dir
  250. this.filename = _file
  251. this.startLogger(
  252. fmt.Sprint(
  253. dir,)
  254. }
  255.  
  256. /**关闭日志器**/
  257. func (this *Logger) Close() {
  258. //0.获取
  259. this.mu_buf.Lock()
  260. defer this.mu_buf.Unlock()
  261. this.mu_file.Lock()
  262. defer this.mu_file.Unlock()
  263.  
  264. //1.关闭
  265. if nil != this.timer {
  266. this.timer.Stop()
  267. }
  268. if nil != this.writeTimer {
  269. this.writeTimer.Stop()
  270. }
  271. if this.logfile != nil {
  272. err := this.logfile.Close()
  273.  
  274. if err != nil {
  275. log.Println(_LABEL_,"file close err",err)
  276. }
  277. } else {
  278. log.Println(_LABEL_,"file has been closed!")
  279. }
  280.  
  281. //2.清理
  282. this.sizeRolling = false
  283. this.dailyRolling = false
  284. this.nomalRolling = false
  285. }
  286.  
  287. //==================================================================内部工具方法
  288. //初始日志记录器(各日志器统一调用)
  289. func (this *Logger) startLogger(tp string) {
  290. defer func() {
  291. if e,ok := recover().(error); ok {
  292. log.Println(_LABEL_,"WARN: panic - %v",e)
  293. log.Println(_LABEL_,string(debug.Stack()))
  294. }
  295. }()
  296.  
  297. //1.初始化空间
  298. var err error
  299. this.buf = &bytes.Buffer{}
  300. this.mu_buf = new(sync.Mutex)
  301. this.mu_file = new(sync.Mutex)
  302. this.path = tp
  303. checkFileDir(tp)
  304. this.logfile,err = os.OpenFile(
  305. tp,os.O_RDWR|os.O_APPEND|os.O_CREATE,_FILE_OPERAT_MODE_,)
  306. if nil != err {
  307. log.Println(_LABEL_,"OpenFile err!")
  308. }
  309.  
  310. //2.开启监控线程
  311. go func() {
  312. this.timer = time.NewTimer(_CHECK_TIME_)
  313. this.writeTimer = time.NewTimer(_WRITE_TIME_)
  314. if !TIMEER_WRITE {
  315. this.writeTimer.Stop()
  316. }
  317.  
  318. for {
  319. select {
  320. //定时检测是否分割
  321. case <-this.timer.C:
  322. this.fileCheck()
  323. if IS_DEBUG && false {
  324. log.Printf("*") //心跳
  325. }
  326. break
  327. //定时写入文件(定时写入,会导致延时)
  328. case <-this.writeTimer.C:
  329. this.bufWrite()
  330. if IS_DEBUG && false {
  331. log.Printf(".") //心跳
  332. }
  333. break
  334. }
  335. }
  336. }()
  337.  
  338. if IS_DEBUG {
  339. jstr,err := json.Marshal(this)
  340. if nil == err {
  341. log.Println(_LABEL_,_VERSION_,string(jstr))
  342. }
  343. }
  344. }
  345.  
  346. //文件检测(会锁定文件)
  347. func (this *Logger) fileCheck() {
  348. //0.边界判断
  349. if nil == this.mu_file ||
  350. nil == this.logfile ||
  351. "" == this.path {
  352.  
  353. return
  354. }
  355. defer func() {
  356. if e,string(debug.Stack()))
  357. }
  358. }()
  359.  
  360. //1.重命名判断
  361. var RENAME_FLAG bool = false
  362. var CHECK_TIME time.Duration = _CHECK_TIME_
  363. this.timer.Stop()
  364. defer this.timer.Reset(CHECK_TIME)
  365. if this.dailyRolling {
  366. //日分割模式
  367. now := getNowFormDate(DATEFORMAT)
  368. if nil != now &&
  369. nil != this._date &&
  370. now.After(*this._date) {
  371. //超时重名
  372. RENAME_FLAG = true
  373. } else {
  374. //检测间隔动态调整(1/60分割时间差值+基准检测时长)
  375. du := this._date.UnixNano() - now.UnixNano()
  376. abs := math.Abs(float64(du))
  377. CHECK_TIME += time.Duration(abs / 60)
  378. }
  379. } else if this.sizeRolling {
  380. //文件大小模式
  381. fileSize := fileSize(this.path)
  382. if "" != this.path &&
  383. this.maxFileCount >= 1 &&
  384. fileSize >= this.maxFileSize {
  385. //超量重名
  386. RENAME_FLAG = true
  387. } else {
  388. //检测时长(假设磁盘写入带宽100M/S)
  389. du := fileSize - this.maxFileSize
  390. abs := math.Abs(float64(du))
  391. CHECK_TIME += time.Duration(((uint64(abs) >> 2 * 10) / 100)) * time.Second
  392. }
  393. } else if this.nomalRolling {
  394. //普通模式
  395. RENAME_FLAG = false
  396. }
  397.  
  398. //2.重名操作
  399. if RENAME_FLAG {
  400. this.mu_file.Lock()
  401. defer this.mu_file.Unlock()
  402. if IS_DEBUG {
  403. log.Println(_LABEL_,this.path,"is need rename.")
  404. }
  405. this.fileRename()
  406. }
  407.  
  408. return
  409. }
  410.  
  411. //重命名文件
  412. func (this *Logger) fileRename() {
  413. //1.生成文件名称
  414. var err error
  415. var newName string
  416. var oldName string
  417. defer func() {
  418. if IS_DEBUG {
  419. log.Println(
  420. _LABEL_,oldName,"->",newName,":",err,)
  421. }
  422. }()
  423.  
  424. if this.dailyRolling {
  425. //日期分割模式(文件重命名)
  426. oldName = this.path
  427. newName = this.path
  428. this._date = getNowFormDate(DATEFORMAT)
  429. this.path = fmt.Sprint(
  430. this.dir,)
  431. } else if this.sizeRolling {
  432. //大小分割模式(1,2,3....)
  433. suffix := int(this._suffix%int(this.maxFileCount) + 1)
  434. oldName = this.path
  435. newName = fmt.Sprint(
  436. this.path,suffix),)
  437. this._suffix = suffix
  438. this.path = this.path
  439. } else if this.nomalRolling {
  440. //常规模式
  441. }
  442.  
  443. //2.处理旧文件
  444. this.logfile.Close()
  445. if "" != oldName && "" != newName && oldName != newName {
  446. if isExist(newName) {
  447. //删除文件
  448. err := os.Remove(newName)
  449. if nil != err {
  450. log.Println(_LABEL_,"remove file err",err.Error())
  451. }
  452. }
  453. err = os.Rename(oldName,newName)
  454. if err != nil {
  455. //重名旧文件
  456. log.Println(_LABEL_,"rename file err",err.Error())
  457. }
  458. }
  459.  
  460. //3.创建新文件
  461. this.logfile,err = os.OpenFile(
  462. this.path,)
  463. if err != nil {
  464. log.Println(_LABEL_,"creat file err",err.Error())
  465. }
  466.  
  467. return
  468. }
  469.  
  470. //缓冲写入文件
  471. func (this *Logger) bufWrite() {
  472. //0.边界处理
  473. if nil == this.buf ||
  474. "" == this.path ||
  475. nil == this.logfile ||
  476. nil == this.mu_buf ||
  477. nil == this.mu_file ||
  478. this.buf.Len() <= 0 {
  479. return
  480. }
  481.  
  482. //1.数据写入
  483. var WRITE_TIME time.Duration = _WRITE_TIME_
  484. if nil != this.writeTimer {
  485. this.writeTimer.Stop()
  486. defer this.writeTimer.Reset(WRITE_TIME)
  487. }
  488. this.mu_file.Lock()
  489. defer this.mu_file.Unlock()
  490. this.mu_buf.Lock()
  491. defer this.mu_buf.Unlock()
  492. defer this.buf.Reset()
  493. n,err := io.WriteString(this.logfile,this.buf.String())
  494. if nil != err {
  495. //写入失败,校验文件,不存在则创建
  496. checkFileDir(this.path)
  497. this.logfile,err = os.OpenFile(
  498. this.path,)
  499. if nil != err {
  500. log.Println(_LABEL_,"log bufWrite() err!")
  501. }
  502. }
  503. //根据缓冲压力进行动态设置写入间隔
  504. if n == 0 {
  505. WRITE_TIME = _WRITE_TIME_
  506. } else {
  507. WRITE_TIME = WRITE_TIME * time.Duration(n/n)
  508. }
  509. }
  510.  
  511. //==================================================================辅助方法
  512. //获取文件大小
  513. func fileSize(file string) int64 {
  514. this,e := os.Stat(file)
  515. if e != nil {
  516. if IS_DEBUG {
  517. log.Println(_LABEL_,e.Error())
  518. }
  519. return 0
  520. }
  521.  
  522. return this.Size()
  523. }
  524.  
  525. //判断路径是否存在
  526. func isExist(path string) bool {
  527. _,err := os.Stat(path)
  528.  
  529. return err == nil || os.IsExist(err)
  530. }
  531.  
  532. //检查文件路径文件夹,不存在则创建
  533. func checkFileDir(tp string) {
  534. p,_ := path.Split(tp)
  535. d,err := os.Stat(p)
  536. if err != nil || !d.IsDir() {
  537. if err := os.MkdirAll(p,_FILE_CREAT_MODE_); err != nil {
  538. log.Println(_LABEL_,"CheckFileDir() Creat dir faile!")
  539. }
  540. }
  541. }
  542.  
  543. //获取当前指定格式的日期
  544. func getNowFormDate(form string) *time.Time {
  545. t,err := time.Parse(form,time.Now().Format(form))
  546. if nil != err {
  547. log.Println(_LABEL_,"getNowFormDate()",err.Error())
  548. t = time.Time{}
  549.  
  550. return &t
  551. }
  552.  
  553. return &t
  554. }
  555.  
  556. //字符串安全转义
  557. func ToLineString(src string) string {
  558. src = strings.Replace(src,"\n","\\n",-1)
  559. src = strings.Replace(src,"\r","\\r",-1)
  560.  
  561. return src
  562. }
  563.  
  564. //字符串安全反转义
  565. func FromLineString(src string) string {
  566. src = strings.Replace(src,-1)
  567.  
  568. return src
  569. }
  570.  
  571. //==================================================================测试用例
  572. func Test() {
  573. logg := New()
  574. logg.SetType(1)
  575. logg.SetRollingNormal("./logs","logg")
  576. logg.Println("hello world!")
  577. }


较早前版本可参见:http://studygolang.com/topics/2620

猜你在找的Go相关文章