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.

398 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(t time.Time, file string, line int, level int) {
  89. var buf *bytes.Buffer = &log.buf
  90. //如果当前前缀字符串不为空,那么需要先写前缀
  91. if log.prefix != "" {
  92. buf.WriteByte('<')
  93. buf.WriteString(log.prefix)
  94. buf.WriteByte('>')
  95. }
  96. //已经设置了时间相关的标识位,那么需要加时间信息在日志头部
  97. if log.flag&(BitDate|BitTime|BitMicroSeconds) != 0 {
  98. //日期位被标记
  99. if log.flag&BitDate != 0 {
  100. year, month, day := t.Date()
  101. itoa(buf, year, 4)
  102. buf.WriteByte('/') // "2019/"
  103. itoa(buf, int(month), 2)
  104. buf.WriteByte('/') // "2019/04/"
  105. itoa(buf, day, 2)
  106. buf.WriteByte(' ') // "2019/04/11 "
  107. }
  108. //时钟位被标记
  109. if log.flag&(BitTime|BitMicroSeconds) != 0 {
  110. hour, min, sec := t.Clock()
  111. itoa(buf, hour, 2)
  112. buf.WriteByte(':') // "11:"
  113. itoa(buf, min, 2)
  114. buf.WriteByte(':') // "11:15:"
  115. itoa(buf, sec, 2) // "11:15:33"
  116. //微秒被标记
  117. if log.flag&BitMicroSeconds != 0 {
  118. buf.WriteByte('.')
  119. itoa(buf, t.Nanosecond()/1e3, 6) // "11:15:33.123123
  120. }
  121. buf.WriteByte(' ')
  122. }
  123. // 日志级别位被标记
  124. if log.flag&BitLevel != 0 {
  125. buf.WriteString(levels[level])
  126. }
  127. //日志当前代码调用文件名名称位被标记
  128. if log.flag&(BitShortFile|BitLongFile) != 0 {
  129. //短文件名称
  130. if log.flag&BitShortFile != 0 {
  131. short := file
  132. for i := len(file) - 1; i > 0; i-- {
  133. if file[i] == '/' {
  134. //找到最后一个'/'之后的文件名称 如:/home/go/src/zinx.go 得到 "zinx.go"
  135. short = file[i+1:]
  136. break
  137. }
  138. }
  139. file = short
  140. }
  141. buf.WriteString(file)
  142. buf.WriteByte(':')
  143. itoa(buf, line, -1) //行数
  144. buf.WriteString(": ")
  145. }
  146. }
  147. }
  148. /*
  149. 输出日志文件,原方法
  150. */
  151. func (log *ZinxLogger) OutPut(level int, s string) error {
  152. now := time.Now() // 得到当前时间
  153. var file string //当前调用日志接口的文件名称
  154. var line int //当前代码行数
  155. log.mu.Lock()
  156. defer log.mu.Unlock()
  157. if log.flag&(BitShortFile|BitLongFile) != 0 {
  158. log.mu.Unlock()
  159. var ok bool
  160. //得到当前调用者的文件名称和执行到的代码行数
  161. _, file, line, ok = runtime.Caller(log.calldDepth)
  162. if !ok {
  163. file = "unknown-file"
  164. line = 0
  165. }
  166. log.mu.Lock()
  167. }
  168. //清零buf
  169. log.buf.Reset()
  170. //写日志头
  171. log.formatHeader(now, file, line, level)
  172. //写日志内容
  173. log.buf.WriteString(s)
  174. //补充回车
  175. if len(s) > 0 && s[len(s)-1] != '\n' {
  176. log.buf.WriteByte('\n')
  177. }
  178. //将填充好的buf 写到IO输出上
  179. _, err := log.out.Write(log.buf.Bytes())
  180. return err
  181. }
  182. // ====> Debug <====
  183. func (log *ZinxLogger) Debugf(format string, v ...interface{}) {
  184. if log.debugClose == true {
  185. return
  186. }
  187. _ = log.OutPut(LogDebug, fmt.Sprintf(format, v...))
  188. }
  189. func (log *ZinxLogger) Debug(v ...interface{}) {
  190. if log.debugClose == true {
  191. return
  192. }
  193. _ = log.OutPut(LogDebug, fmt.Sprintln(v...))
  194. }
  195. // ====> Info <====
  196. func (log *ZinxLogger) Infof(format string, v ...interface{}) {
  197. _ = log.OutPut(LogInfo, fmt.Sprintf(format, v...))
  198. }
  199. func (log *ZinxLogger) Info(v ...interface{}) {
  200. _ = log.OutPut(LogInfo, fmt.Sprintln(v...))
  201. }
  202. // ====> Warn <====
  203. func (log *ZinxLogger) Warnf(format string, v ...interface{}) {
  204. _ = log.OutPut(LogWarn, fmt.Sprintf(format, v...))
  205. }
  206. func (log *ZinxLogger) Warn(v ...interface{}) {
  207. _ = log.OutPut(LogWarn, fmt.Sprintln(v...))
  208. }
  209. // ====> Error <====
  210. func (log *ZinxLogger) Errorf(format string, v ...interface{}) {
  211. _ = log.OutPut(LogError, fmt.Sprintf(format, v...))
  212. }
  213. func (log *ZinxLogger) Error(v ...interface{}) {
  214. _ = log.OutPut(LogError, fmt.Sprintln(v...))
  215. }
  216. // ====> Fatal 需要终止程序 <====
  217. func (log *ZinxLogger) Fatalf(format string, v ...interface{}) {
  218. _ = log.OutPut(LogFatal, fmt.Sprintf(format, v...))
  219. os.Exit(1)
  220. }
  221. func (log *ZinxLogger) Fatal(v ...interface{}) {
  222. _ = log.OutPut(LogFatal, fmt.Sprintln(v...))
  223. os.Exit(1)
  224. }
  225. // ====> Panic <====
  226. func (log *ZinxLogger) Panicf(format string, v ...interface{}) {
  227. s := fmt.Sprintf(format, v...)
  228. _ = log.OutPut(LogPanic, s)
  229. panic(s)
  230. }
  231. func (log *ZinxLogger) Panic(v ...interface{}) {
  232. s := fmt.Sprintln(v...)
  233. _ = log.OutPut(LogPanic, s)
  234. panic(s)
  235. }
  236. // ====> Stack <====
  237. func (log *ZinxLogger) Stack(v ...interface{}) {
  238. s := fmt.Sprint(v...)
  239. s += "\n"
  240. buf := make([]byte, LOG_MAX_BUF)
  241. n := runtime.Stack(buf, true) //得到当前堆栈信息
  242. s += string(buf[:n])
  243. s += "\n"
  244. _ = log.OutPut(LogError, s)
  245. }
  246. //获取当前日志bitmap标记
  247. func (log *ZinxLogger) Flags() int {
  248. log.mu.Lock()
  249. defer log.mu.Unlock()
  250. return log.flag
  251. }
  252. //重新设置日志Flags bitMap 标记位
  253. func (log *ZinxLogger) ResetFlags(flag int) {
  254. log.mu.Lock()
  255. defer log.mu.Unlock()
  256. log.flag = flag
  257. }
  258. //添加flag标记
  259. func (log *ZinxLogger) AddFlag(flag int) {
  260. log.mu.Lock()
  261. defer log.mu.Unlock()
  262. log.flag |= flag
  263. }
  264. //设置日志的 用户自定义前缀字符串
  265. func (log *ZinxLogger) SetPrefix(prefix string) {
  266. log.mu.Lock()
  267. defer log.mu.Unlock()
  268. log.prefix = prefix
  269. }
  270. //设置日志文件输出
  271. func (log *ZinxLogger) SetLogFile(fileDir string, fileName string) {
  272. var file *os.File
  273. //创建日志文件夹
  274. _ = mkdirLog(fileDir)
  275. fullPath := fileDir + "/" + fileName
  276. if log.checkFileExist(fullPath) {
  277. //文件存在,打开
  278. file, _ = os.OpenFile(fullPath, os.O_APPEND|os.O_RDWR, 0644)
  279. } else {
  280. //文件不存在,创建
  281. file, _ = os.OpenFile(fullPath, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644)
  282. }
  283. log.mu.Lock()
  284. defer log.mu.Unlock()
  285. //关闭之前绑定的文件
  286. log.closeFile()
  287. log.file = file
  288. log.out = file
  289. }
  290. //关闭日志绑定的文件
  291. func (log *ZinxLogger) closeFile() {
  292. if log.file != nil {
  293. _ = log.file.Close()
  294. log.file = nil
  295. log.out = os.Stderr
  296. }
  297. }
  298. func (log *ZinxLogger) CloseDebug() {
  299. log.debugClose = true
  300. }
  301. func (log *ZinxLogger) OpenDebug() {
  302. log.debugClose = false
  303. }
  304. // ================== 以下是一些工具方法 ==========
  305. //判断日志文件是否存在
  306. func (log *ZinxLogger) checkFileExist(filename string) bool {
  307. exist := true
  308. if _, err := os.Stat(filename); os.IsNotExist(err) {
  309. exist = false
  310. }
  311. return exist
  312. }
  313. func mkdirLog(dir string) (e error) {
  314. _, er := os.Stat(dir)
  315. b := er == nil || os.IsExist(er)
  316. if !b {
  317. if err := os.MkdirAll(dir, 0775); err != nil {
  318. if os.IsPermission(err) {
  319. e = err
  320. }
  321. }
  322. }
  323. return
  324. }
  325. //将一个整形转换成一个固定长度的字符串,字符串宽度应该是大于0的
  326. //要确保buffer是有容量空间的
  327. func itoa(buf *bytes.Buffer, i int, wID int) {
  328. var u uint = uint(i)
  329. if u == 0 && wID <= 1 {
  330. buf.WriteByte('0')
  331. return
  332. }
  333. // Assemble decimal in reverse order.
  334. var b [32]byte
  335. bp := len(b)
  336. for ; u > 0 || wID > 0; u /= 10 {
  337. bp--
  338. wID--
  339. b[bp] = byte(u%10) + '0'
  340. }
  341. // avoID slicing b to avoID an allocation.
  342. for bp < len(b) {
  343. buf.WriteByte(b[bp])
  344. bp++
  345. }
  346. }