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.

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