You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

397 lines
9.5 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. // Package zlog 主要提供zinx相关日志记录接口
  2. // 包括:
  3. // stdzlog模块, 提供全局日志方法
  4. // zlogger模块, 日志内部定义协议,均为对象类方法
  5. //
  6. // 当前文件描述:
  7. // @Title zlogger.go
  8. // @Description 基础日志接口,包括Debug、Fatal等
  9. // @Author Aceld - Thu Mar 11 10:32:29 CST 2019
  10. package zlog
  11. /*
  12. 日志类全部方法 API
  13. Add By Aceld(刘丹冰) 2019-4-23
  14. */
  15. import (
  16. "bytes"
  17. "fmt"
  18. "io"
  19. "os"
  20. "runtime"
  21. "sync"
  22. "time"
  23. )
  24. const (
  25. LOG_MAX_BUF = 1024 * 1024
  26. )
  27. //日志头部信息标记位,采用bitmap方式,用户可以选择头部需要哪些标记位被打印
  28. const (
  29. BitDate = 1 << iota //日期标记位 2019/01/23
  30. BitTime //时间标记位 01:23:12
  31. BitMicroSeconds //微秒级标记位 01:23:12.111222
  32. BitLongFile //完整文件名称 /home/go/src/zinx/server.go
  33. BitShortFile //最后文件名 server.go
  34. BitLevel //当前日志级别: 0(Debug), 1(Info), 2(Warn), 3(Error), 4(Panic), 5(Fatal)
  35. BitStdFlag = BitDate | BitTime //标准头部日志格式
  36. BitDefault = BitLevel | BitShortFile | BitStdFlag //默认日志头部格式
  37. )
  38. //日志级别
  39. const (
  40. LogDebug = iota
  41. LogInfo
  42. LogWarn
  43. LogError
  44. LogPanic
  45. LogFatal
  46. )
  47. //日志级别对应的显示字符串
  48. var levels = []string{
  49. "[DEBUG]",
  50. "[INFO]",
  51. "[WARN]",
  52. "[ERROR]",
  53. "[PANIC]",
  54. "[FATAL]",
  55. }
  56. type ZinxLogger struct {
  57. mu sync.Mutex //确保多协程读写文件,防止文件内容混乱,做到协程安全
  58. prefix string //每行log日志的前缀字符串,拥有日志标记
  59. flag int //日志标记位
  60. out io.Writer //日志输出的文件描述符
  61. buf bytes.Buffer //输出的缓冲区
  62. file *os.File //当前日志绑定的输出文件
  63. debugClose bool //是否打印调试debug信息
  64. calldDepth int //获取日志文件名和代码上述的runtime.Call 的函数调用层数
  65. }
  66. /*
  67. 创建一个日志
  68. out: 标准输出的文件io
  69. prefix: 日志的前缀
  70. flag: 当前日志头部信息的标记位
  71. */
  72. func NewZinxLog(out io.Writer, prefix string, flag int) *ZinxLogger {
  73. //默认 debug打开, calledDepth深度为2,ZinxLogger对象调用日志打印方法最多调用两层到达output函数
  74. zlog := &ZinxLogger{out: out, prefix: prefix, flag: flag, file: nil, debugClose: false, calldDepth: 2}
  75. //设置log对象 回收资源 析构方法(不设置也可以,go的Gc会自动回收,强迫症没办法)
  76. runtime.SetFinalizer(zlog, CleanZinxLog)
  77. return zlog
  78. }
  79. /*
  80. 回收日志处理
  81. */
  82. func CleanZinxLog(log *ZinxLogger) {
  83. log.closeFile()
  84. }
  85. /*
  86. 制作当条日志数据的 格式头信息
  87. */
  88. func (log *ZinxLogger) formatHeader(buf *bytes.Buffer, t time.Time, file string, line int, level int) {
  89. //如果当前前缀字符串不为空,那么需要先写前缀
  90. if log.prefix != "" {
  91. buf.WriteByte('<')
  92. buf.WriteString(log.prefix)
  93. buf.WriteByte('>')
  94. }
  95. //已经设置了时间相关的标识位,那么需要加时间信息在日志头部
  96. if log.flag&(BitDate|BitTime|BitMicroSeconds) != 0 {
  97. //日期位被标记
  98. if log.flag&BitDate != 0 {
  99. year, month, day := t.Date()
  100. itoa(buf, year, 4)
  101. buf.WriteByte('/') // "2019/"
  102. itoa(buf, int(month), 2)
  103. buf.WriteByte('/') // "2019/04/"
  104. itoa(buf, day, 2)
  105. buf.WriteByte(' ') // "2019/04/11 "
  106. }
  107. //时钟位被标记
  108. if log.flag&(BitTime|BitMicroSeconds) != 0 {
  109. hour, min, sec := t.Clock()
  110. itoa(buf, hour, 2)
  111. buf.WriteByte(':') // "11:"
  112. itoa(buf, min, 2)
  113. buf.WriteByte(':') // "11:15:"
  114. itoa(buf, sec, 2) // "11:15:33"
  115. //微秒被标记
  116. if log.flag&BitMicroSeconds != 0 {
  117. buf.WriteByte('.')
  118. itoa(buf, t.Nanosecond()/1e3, 6) // "11:15:33.123123
  119. }
  120. buf.WriteByte(' ')
  121. }
  122. // 日志级别位被标记
  123. if log.flag&BitLevel != 0 {
  124. buf.WriteString(levels[level])
  125. }
  126. //日志当前代码调用文件名名称位被标记
  127. if log.flag&(BitShortFile|BitLongFile) != 0 {
  128. //短文件名称
  129. if log.flag&BitShortFile != 0 {
  130. short := file
  131. for i := len(file) - 1; i > 0; i-- {
  132. if file[i] == '/' {
  133. //找到最后一个'/'之后的文件名称 如:/home/go/src/zinx.go 得到 "zinx.go"
  134. short = file[i+1:]
  135. break
  136. }
  137. }
  138. file = short
  139. }
  140. buf.WriteString(file)
  141. buf.WriteByte(':')
  142. itoa(buf, line, -1) //行数
  143. buf.WriteString(": ")
  144. }
  145. }
  146. }
  147. /*
  148. 输出日志文件,原方法
  149. */
  150. func (log *ZinxLogger) OutPut(level int, s string) error {
  151. now := time.Now() // 得到当前时间
  152. var file string //当前调用日志接口的文件名称
  153. var line int //当前代码行数
  154. log.mu.Lock()
  155. defer log.mu.Unlock()
  156. if log.flag&(BitShortFile|BitLongFile) != 0 {
  157. log.mu.Unlock()
  158. var ok bool
  159. //得到当前调用者的文件名称和执行到的代码行数
  160. _, file, line, ok = runtime.Caller(log.calldDepth)
  161. if !ok {
  162. file = "unknown-file"
  163. line = 0
  164. }
  165. log.mu.Lock()
  166. }
  167. //清零buf
  168. log.buf.Reset()
  169. //写日志头
  170. log.formatHeader(&log.buf, now, file, line, level)
  171. //写日志内容
  172. log.buf.WriteString(s)
  173. //补充回车
  174. if len(s) > 0 && s[len(s)-1] != '\n' {
  175. log.buf.WriteByte('\n')
  176. }
  177. //将填充好的buf 写到IO输出上
  178. _, err := log.out.Write(log.buf.Bytes())
  179. return err
  180. }
  181. // ====> Debug <====
  182. func (log *ZinxLogger) Debugf(format string, v ...interface{}) {
  183. if log.debugClose == true {
  184. return
  185. }
  186. _ = log.OutPut(LogDebug, fmt.Sprintf(format, v...))
  187. }
  188. func (log *ZinxLogger) Debug(v ...interface{}) {
  189. if log.debugClose == true {
  190. return
  191. }
  192. _ = log.OutPut(LogDebug, fmt.Sprintln(v...))
  193. }
  194. // ====> Info <====
  195. func (log *ZinxLogger) Infof(format string, v ...interface{}) {
  196. _ = log.OutPut(LogInfo, fmt.Sprintf(format, v...))
  197. }
  198. func (log *ZinxLogger) Info(v ...interface{}) {
  199. _ = log.OutPut(LogInfo, fmt.Sprintln(v...))
  200. }
  201. // ====> Warn <====
  202. func (log *ZinxLogger) Warnf(format string, v ...interface{}) {
  203. _ = log.OutPut(LogWarn, fmt.Sprintf(format, v...))
  204. }
  205. func (log *ZinxLogger) Warn(v ...interface{}) {
  206. _ = log.OutPut(LogWarn, fmt.Sprintln(v...))
  207. }
  208. // ====> Error <====
  209. func (log *ZinxLogger) Errorf(format string, v ...interface{}) {
  210. _ = log.OutPut(LogError, fmt.Sprintf(format, v...))
  211. }
  212. func (log *ZinxLogger) Error(v ...interface{}) {
  213. _ = log.OutPut(LogError, fmt.Sprintln(v...))
  214. }
  215. // ====> Fatal 需要终止程序 <====
  216. func (log *ZinxLogger) Fatalf(format string, v ...interface{}) {
  217. _ = log.OutPut(LogFatal, fmt.Sprintf(format, v...))
  218. os.Exit(1)
  219. }
  220. func (log *ZinxLogger) Fatal(v ...interface{}) {
  221. _ = log.OutPut(LogFatal, fmt.Sprintln(v...))
  222. os.Exit(1)
  223. }
  224. // ====> Panic <====
  225. func (log *ZinxLogger) Panicf(format string, v ...interface{}) {
  226. s := fmt.Sprintf(format, v...)
  227. _ = log.OutPut(LogPanic, s)
  228. panic(s)
  229. }
  230. func (log *ZinxLogger) Panic(v ...interface{}) {
  231. s := fmt.Sprintln(v...)
  232. _ = log.OutPut(LogPanic, s)
  233. panic(s)
  234. }
  235. // ====> Stack <====
  236. func (log *ZinxLogger) Stack(v ...interface{}) {
  237. s := fmt.Sprint(v...)
  238. s += "\n"
  239. buf := make([]byte, LOG_MAX_BUF)
  240. n := runtime.Stack(buf, true) //得到当前堆栈信息
  241. s += string(buf[:n])
  242. s += "\n"
  243. _ = log.OutPut(LogError, s)
  244. }
  245. //获取当前日志bitmap标记
  246. func (log *ZinxLogger) Flags() int {
  247. log.mu.Lock()
  248. defer log.mu.Unlock()
  249. return log.flag
  250. }
  251. //重新设置日志Flags bitMap 标记位
  252. func (log *ZinxLogger) ResetFlags(flag int) {
  253. log.mu.Lock()
  254. defer log.mu.Unlock()
  255. log.flag = flag
  256. }
  257. //添加flag标记
  258. func (log *ZinxLogger) AddFlag(flag int) {
  259. log.mu.Lock()
  260. defer log.mu.Unlock()
  261. log.flag |= flag
  262. }
  263. //设置日志的 用户自定义前缀字符串
  264. func (log *ZinxLogger) SetPrefix(prefix string) {
  265. log.mu.Lock()
  266. defer log.mu.Unlock()
  267. log.prefix = prefix
  268. }
  269. //设置日志文件输出
  270. func (log *ZinxLogger) SetLogFile(fileDir string, fileName string) {
  271. var file *os.File
  272. //创建日志文件夹
  273. _ = mkdirLog(fileDir)
  274. fullPath := fileDir + "/" + fileName
  275. if log.checkFileExist(fullPath) {
  276. //文件存在,打开
  277. file, _ = os.OpenFile(fullPath, os.O_APPEND|os.O_RDWR, 0644)
  278. } else {
  279. //文件不存在,创建
  280. file, _ = os.OpenFile(fullPath, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644)
  281. }
  282. log.mu.Lock()
  283. defer log.mu.Unlock()
  284. //关闭之前绑定的文件
  285. log.closeFile()
  286. log.file = file
  287. log.out = file
  288. }
  289. //关闭日志绑定的文件
  290. func (log *ZinxLogger) closeFile() {
  291. if log.file != nil {
  292. _ = log.file.Close()
  293. log.file = nil
  294. log.out = os.Stderr
  295. }
  296. }
  297. func (log *ZinxLogger) CloseDebug() {
  298. log.debugClose = true
  299. }
  300. func (log *ZinxLogger) OpenDebug() {
  301. log.debugClose = false
  302. }
  303. // ================== 以下是一些工具方法 ==========
  304. //判断日志文件是否存在
  305. func (log *ZinxLogger) checkFileExist(filename string) bool {
  306. exist := true
  307. if _, err := os.Stat(filename); os.IsNotExist(err) {
  308. exist = false
  309. }
  310. return exist
  311. }
  312. func mkdirLog(dir string) (e error) {
  313. _, er := os.Stat(dir)
  314. b := er == nil || os.IsExist(er)
  315. if !b {
  316. if err := os.MkdirAll(dir, 0775); err != nil {
  317. if os.IsPermission(err) {
  318. e = err
  319. }
  320. }
  321. }
  322. return
  323. }
  324. //将一个整形转换成一个固定长度的字符串,字符串宽度应该是大于0的
  325. //要确保buffer是有容量空间的
  326. func itoa(buf *bytes.Buffer, i int, wID int) {
  327. var u uint = uint(i)
  328. if u == 0 && wID <= 1 {
  329. buf.WriteByte('0')
  330. return
  331. }
  332. // Assemble decimal in reverse order.
  333. var b [32]byte
  334. bp := len(b)
  335. for ; u > 0 || wID > 0; u /= 10 {
  336. bp--
  337. wID--
  338. b[bp] = byte(u%10) + '0'
  339. }
  340. // avoID slicing b to avoID an allocation.
  341. for bp < len(b) {
  342. buf.WriteByte(b[bp])
  343. bp++
  344. }
  345. }