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.

399 lines
9.2 KiB

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