logw.go 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. package logger
  2. import (
  3. "crypto/md5"
  4. "encoding/hex"
  5. "fmt"
  6. "log"
  7. "os"
  8. "runtime"
  9. "runtime/debug"
  10. "strconv"
  11. "sync"
  12. "sync/atomic"
  13. "time"
  14. )
  15. var defaultlog *logBean = getdefaultLogger()
  16. var skip int = 4
  17. type logger struct {
  18. lb *logBean
  19. }
  20. func (this *logger) SetConsole(isConsole bool) {
  21. this.lb.setConsole(isConsole)
  22. }
  23. func (this *logger) SetLevel(_level LEVEL) {
  24. this.lb.setLevel(_level)
  25. }
  26. func (this *logger) SetFormat(logFormat string) {
  27. this.lb.setFormat(logFormat)
  28. }
  29. func (this *logger) SetRollingFile(fileDir, fileName string, maxNumber int32, maxSize int64, _unit UNIT) {
  30. this.lb.setRollingFile(fileDir, fileName, maxNumber, maxSize, _unit)
  31. }
  32. func (this *logger) SetRollingDaily(fileDir, fileName string) {
  33. this.lb.setRollingDaily(fileDir, fileName)
  34. }
  35. func (this *logger) Debug(v ...interface{}) {
  36. this.lb.debug(v...)
  37. }
  38. func (this *logger) Info(v ...interface{}) {
  39. this.lb.info(v...)
  40. }
  41. func (this *logger) Warn(v ...interface{}) {
  42. this.lb.warn(v...)
  43. }
  44. func (this *logger) Error(v ...interface{}) {
  45. this.lb.error(v...)
  46. }
  47. func (this *logger) Fatal(v ...interface{}) {
  48. this.lb.fatal(v...)
  49. }
  50. func (this *logger) SetLevelFile(level LEVEL, dir, fileName string) {
  51. this.lb.setLevelFile(level, dir, fileName)
  52. }
  53. type logBean struct {
  54. mu *sync.Mutex
  55. logLevel LEVEL
  56. maxFileSize int64
  57. maxFileCount int32
  58. consoleAppender bool
  59. rolltype _ROLLTYPE
  60. format string
  61. id string
  62. d, i, w, e, f string //id
  63. }
  64. type fileBeanFactory struct {
  65. fbs map[string]*fileBean
  66. mu *sync.RWMutex
  67. }
  68. var fbf = &fileBeanFactory{fbs: make(map[string]*fileBean, 0), mu: new(sync.RWMutex)}
  69. func (this *fileBeanFactory) add(dir, filename string, _suffix int, maxsize int64, maxfileCount int32) {
  70. this.mu.Lock()
  71. defer this.mu.Unlock()
  72. id := md5str(fmt.Sprint(dir, filename))
  73. if _, ok := this.fbs[id]; !ok {
  74. this.fbs[id] = newFileBean(dir, filename, _suffix, maxsize, maxfileCount)
  75. }
  76. }
  77. func (this *fileBeanFactory) get(id string) *fileBean {
  78. this.mu.RLock()
  79. defer this.mu.RUnlock()
  80. return this.fbs[id]
  81. }
  82. type fileBean struct {
  83. id string
  84. dir string
  85. filename string
  86. _suffix int
  87. _date *time.Time
  88. mu *sync.RWMutex
  89. logfile *os.File
  90. lg *log.Logger
  91. filesize int64
  92. maxFileSize int64
  93. maxFileCount int32
  94. }
  95. func GetLogger() (l *logger) {
  96. l = new(logger)
  97. l.lb = getdefaultLogger()
  98. return
  99. }
  100. func getdefaultLogger() (lb *logBean) {
  101. lb = &logBean{}
  102. lb.mu = new(sync.Mutex)
  103. lb.setConsole(true)
  104. return
  105. }
  106. func (this *logBean) setConsole(isConsole bool) {
  107. this.consoleAppender = isConsole
  108. }
  109. func (this *logBean) setLevelFile(level LEVEL, dir, fileName string) {
  110. key := md5str(fmt.Sprint(dir, fileName))
  111. switch level {
  112. case DEBUG:
  113. this.d = key
  114. case INFO:
  115. this.i = key
  116. case WARN:
  117. this.w = key
  118. case ERROR:
  119. this.e = key
  120. case FATAL:
  121. this.f = key
  122. default:
  123. return
  124. }
  125. var _suffix = 0
  126. if this.maxFileCount < 1<<31-1 {
  127. for i := 1; i < int(this.maxFileCount); i++ {
  128. if isExist(dir + "/" + fileName + "." + strconv.Itoa(i)) {
  129. _suffix = i
  130. } else {
  131. break
  132. }
  133. }
  134. }
  135. fbf.add(dir, fileName, _suffix, this.maxFileSize, this.maxFileCount)
  136. }
  137. func (this *logBean) setLevel(_level LEVEL) {
  138. this.logLevel = _level
  139. }
  140. func (this *logBean) setFormat(logFormat string) {
  141. this.format = logFormat
  142. }
  143. func (this *logBean) setRollingFile(fileDir, fileName string, maxNumber int32, maxSize int64, _unit UNIT) {
  144. this.mu.Lock()
  145. defer this.mu.Unlock()
  146. if maxNumber > 0 {
  147. this.maxFileCount = maxNumber
  148. } else {
  149. this.maxFileCount = 1<<31 - 1
  150. }
  151. this.maxFileSize = maxSize * int64(_unit)
  152. this.rolltype = _ROLLFILE
  153. mkdirlog(fileDir)
  154. var _suffix = 0
  155. for i := 1; i < int(maxNumber); i++ {
  156. if isExist(fileDir + "/" + fileName + "." + strconv.Itoa(i)) {
  157. _suffix = i
  158. } else {
  159. break
  160. }
  161. }
  162. this.id = md5str(fmt.Sprint(fileDir, fileName))
  163. fbf.add(fileDir, fileName, _suffix, this.maxFileSize, this.maxFileCount)
  164. }
  165. func (this *logBean) setRollingDaily(fileDir, fileName string) {
  166. this.rolltype = _DAILY
  167. mkdirlog(fileDir)
  168. this.id = md5str(fmt.Sprint(fileDir, fileName))
  169. fbf.add(fileDir, fileName, 0, 0, 0)
  170. }
  171. func (this *logBean) console(v ...interface{}) {
  172. s := fmt.Sprint(v...)
  173. if this.consoleAppender {
  174. _, file, line, _ := runtime.Caller(skip)
  175. short := file
  176. for i := len(file) - 1; i > 0; i-- {
  177. if file[i] == '/' {
  178. short = file[i+1:]
  179. break
  180. }
  181. }
  182. file = short
  183. if this.format == "" {
  184. log.Println(file, strconv.Itoa(line), s)
  185. } else {
  186. vs := make([]interface{}, 0)
  187. vs = append(vs, file)
  188. vs = append(vs, strconv.Itoa(line))
  189. for _, vv := range v {
  190. vs = append(vs, vv)
  191. }
  192. log.Printf(fmt.Sprint("%s %s ", this.format, "\n"), vs...)
  193. }
  194. }
  195. }
  196. func (this *logBean) log(level string, v ...interface{}) {
  197. defer catchError()
  198. //s := fmt.Sprint(v...)
  199. s := ""
  200. for _, val := range v {
  201. s += " " + fmt.Sprint(val)
  202. }
  203. length := len([]byte(s))
  204. var lg *fileBean = fbf.get(this.id)
  205. var _level = ALL
  206. switch level {
  207. case "debug":
  208. if this.d != "" {
  209. lg = fbf.get(this.d)
  210. }
  211. _level = DEBUG
  212. case "info":
  213. if this.i != "" {
  214. lg = fbf.get(this.i)
  215. }
  216. _level = INFO
  217. case "warn":
  218. if this.w != "" {
  219. lg = fbf.get(this.w)
  220. }
  221. _level = WARN
  222. case "error":
  223. if this.e != "" {
  224. lg = fbf.get(this.e)
  225. }
  226. _level = ERROR
  227. case "fatal":
  228. if this.f != "" {
  229. lg = fbf.get(this.f)
  230. }
  231. _level = FATAL
  232. }
  233. if lg != nil {
  234. this.fileCheck(lg)
  235. lg.addsize(int64(length))
  236. if this.logLevel <= _level {
  237. if lg != nil {
  238. if this.format == "" {
  239. lg.write(level, s)
  240. } else {
  241. lg.writef(this.format, v...)
  242. }
  243. }
  244. this.console(v...)
  245. }
  246. } else {
  247. this.console(v...)
  248. }
  249. }
  250. func (this *logBean) debug(v ...interface{}) {
  251. this.log("debug", v...)
  252. }
  253. func (this *logBean) info(v ...interface{}) {
  254. this.log("info", v...)
  255. }
  256. func (this *logBean) warn(v ...interface{}) {
  257. this.log("warn", v...)
  258. }
  259. func (this *logBean) error(v ...interface{}) {
  260. this.log("error", v...)
  261. }
  262. func (this *logBean) fatal(v ...interface{}) {
  263. this.log("fatal", v...)
  264. }
  265. func (this *logBean) fileCheck(fb *fileBean) {
  266. defer catchError()
  267. if this.isMustRename(fb) {
  268. this.mu.Lock()
  269. defer this.mu.Unlock()
  270. if this.isMustRename(fb) {
  271. fb.rename(this.rolltype)
  272. }
  273. }
  274. }
  275. //--------------------------------------------------------------------------------
  276. func (this *logBean) isMustRename(fb *fileBean) bool {
  277. switch this.rolltype {
  278. case _DAILY:
  279. t, _ := time.Parse(_DATEFORMAT, time.Now().Format(_DATEFORMAT))
  280. if t.After(*fb._date) {
  281. return true
  282. }
  283. case _ROLLFILE:
  284. return fb.isOverSize()
  285. }
  286. return false
  287. }
  288. func (this *fileBean) nextSuffix() int {
  289. return int(this._suffix%int(this.maxFileCount) + 1)
  290. }
  291. func newFileBean(fileDir, fileName string, _suffix int, maxSize int64, maxfileCount int32) (fb *fileBean) {
  292. t, _ := time.Parse(_DATEFORMAT, time.Now().Format(_DATEFORMAT))
  293. fb = &fileBean{dir: fileDir, filename: fileName, _date: &t, mu: new(sync.RWMutex)}
  294. fb.logfile, _ = os.OpenFile(fileDir+"/"+fileName, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666)
  295. fb.lg = log.New(fb.logfile, "", log.Ldate|log.Ltime|log.Lshortfile)
  296. fb._suffix = _suffix
  297. fb.maxFileSize = maxSize
  298. fb.maxFileCount = maxfileCount
  299. fb.filesize = fileSize(fileDir + "/" + fileName)
  300. fb._date = &t
  301. return
  302. }
  303. func (this *fileBean) rename(rolltype _ROLLTYPE) {
  304. this.mu.Lock()
  305. defer this.mu.Unlock()
  306. this.close()
  307. nextfilename := ""
  308. switch rolltype {
  309. case _DAILY:
  310. nextfilename = fmt.Sprint(this.dir, "/", this.filename, ".", this._date.Format(_DATEFORMAT))
  311. case _ROLLFILE:
  312. nextfilename = fmt.Sprint(this.dir, "/", this.filename, ".", this.nextSuffix())
  313. this._suffix = this.nextSuffix()
  314. }
  315. if isExist(nextfilename) {
  316. os.Remove(nextfilename)
  317. }
  318. os.Rename(this.dir+"/"+this.filename, nextfilename)
  319. t, _ := time.Parse(_DATEFORMAT, time.Now().Format(_DATEFORMAT))
  320. this._date = &t
  321. this.logfile, _ = os.OpenFile(this.dir+"/"+this.filename, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666)
  322. this.lg = log.New(this.logfile, "", log.Ldate|log.Ltime|log.Lshortfile)
  323. this.filesize = fileSize(this.dir + "/" + this.filename)
  324. }
  325. func (this *fileBean) addsize(size int64) {
  326. atomic.AddInt64(&this.filesize, size)
  327. }
  328. func (this *fileBean) write(level string, v ...interface{}) {
  329. this.mu.RLock()
  330. defer this.mu.RUnlock()
  331. s := fmt.Sprint(v...)
  332. this.lg.Output(skip+1, fmt.Sprintln(level, s))
  333. }
  334. func (this *fileBean) writef(format string, v ...interface{}) {
  335. this.mu.RLock()
  336. defer this.mu.RUnlock()
  337. this.lg.Output(skip+1, fmt.Sprintf(format, v...))
  338. }
  339. func (this *fileBean) isOverSize() bool {
  340. return this.filesize >= this.maxFileSize
  341. }
  342. func (this *fileBean) close() {
  343. this.logfile.Close()
  344. }
  345. //-----------------------------------------------------------------------------------------------
  346. func mkdirlog(dir string) (e error) {
  347. _, er := os.Stat(dir)
  348. b := er == nil || os.IsExist(er)
  349. if !b {
  350. if err := os.MkdirAll(dir, 0666); err != nil {
  351. if os.IsPermission(err) {
  352. e = err
  353. }
  354. }
  355. }
  356. return
  357. }
  358. func fileSize(file string) int64 {
  359. f, e := os.Stat(file)
  360. if e != nil {
  361. fmt.Println(e.Error())
  362. return 0
  363. }
  364. return f.Size()
  365. }
  366. func isExist(path string) bool {
  367. _, err := os.Stat(path)
  368. return err == nil || os.IsExist(err)
  369. }
  370. func md5str(s string) string {
  371. m := md5.New()
  372. m.Write([]byte(s))
  373. return hex.EncodeToString(m.Sum(nil))
  374. }
  375. func catchError() {
  376. if err := recover(); err != nil {
  377. fmt.Println(string(debug.Stack()))
  378. }
  379. }