logger.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. package logger
  2. import (
  3. "database/sql/driver"
  4. "fmt"
  5. "github.com/lestrrat-go/file-rotatelogs"
  6. "github.com/rifflock/lfshook"
  7. "github.com/sirupsen/logrus"
  8. "github.com/spf13/viper"
  9. "gopkg.in/gemnasium/logrus-graylog-hook.v2"
  10. "os"
  11. "reflect"
  12. "regexp"
  13. "runtime"
  14. "strings"
  15. "time"
  16. "unicode"
  17. )
  18. var MaxInt64 = ^int64(0)
  19. type Logger struct {
  20. logrus.Entry
  21. Level logrus.Level
  22. }
  23. func New() *Logger {
  24. timeFormat := "2006/01/02 15:04:05.000 -0700"
  25. if tmFormat, ok := viper.Get("logger.time_format").(string); ok && len(tmFormat) > 0 {
  26. timeFormat = strings.TrimSpace(tmFormat)
  27. }
  28. level := logrus.InfoLevel
  29. if lvStr, ok := viper.Get("logger.level").(string); ok {
  30. lvStr = strings.TrimSpace(strings.ToLower(lvStr))
  31. if lvStr == "warn" {
  32. level = logrus.WarnLevel
  33. } else if lvStr == "debug" {
  34. level = logrus.DebugLevel
  35. } else if lvStr == "error" {
  36. level = logrus.ErrorLevel
  37. } else if lvStr == "fatal" {
  38. level = logrus.FatalLevel
  39. } else if lvStr == "panic" {
  40. level = logrus.PanicLevel
  41. }
  42. }
  43. var formatter logrus.Formatter
  44. if viper.GetString("logger.formatter") == "json" {
  45. formatter = &logrus.JSONFormatter{TimestampFormat: timeFormat}
  46. } else {
  47. formatter = &logrus.TextFormatter{TimestampFormat: timeFormat}
  48. }
  49. log := &logrus.Logger{
  50. Out: os.Stderr,
  51. Formatter: formatter,
  52. Hooks: make(logrus.LevelHooks),
  53. Level: level,
  54. }
  55. if grayAddr, ok := viper.Get("logger.graylog.addr").(string); ok && len(grayAddr) > 0 {
  56. grayHook := graylog.NewGraylogHook(grayAddr, nil)
  57. log.AddHook(grayHook)
  58. }
  59. lfMap := viper.GetStringMapString("logger.local.file.path")
  60. if nil != lfMap && len(lfMap) > 0 {
  61. viper.SetDefault("logger.local.file.rotation.hours", 24)
  62. viper.SetDefault("logger.local.file.rotation.count", 7)
  63. rotationHours := viper.GetInt("logger.local.file.rotation.hours")
  64. rotationCount := viper.GetInt("logger.local.file.rotation.count")
  65. writerMap := lfshook.WriterMap{}
  66. if v, ok := lfMap["panic"]; ok {
  67. writerMap[logrus.PanicLevel], _ = rotatelogs.New(
  68. v+".%Y%m%d",
  69. rotatelogs.WithLinkName(v), // 为最新的日志建立软连接,以方便随着找到当前日志文件
  70. rotatelogs.WithRotationCount(uint(rotationCount)), // 设置文件清理前最多保存的个数,也可通过WithMaxAge设置最长保存时间,二者取其一
  71. rotatelogs.WithRotationTime(time.Duration(rotationHours)*time.Hour), // 设置日志分割的时间,例如一天一次
  72. )
  73. }
  74. if v, ok := lfMap["fatal"]; ok {
  75. writerMap[logrus.FatalLevel], _ = rotatelogs.New(
  76. v+".%Y%m%d",
  77. rotatelogs.WithLinkName(v),
  78. rotatelogs.WithRotationCount(uint(rotationCount)),
  79. rotatelogs.WithRotationTime(time.Duration(rotationHours)*time.Hour),
  80. )
  81. }
  82. if v, ok := lfMap["error"]; ok {
  83. writerMap[logrus.ErrorLevel], _ = rotatelogs.New(
  84. v+".%Y%m%d",
  85. rotatelogs.WithLinkName(v),
  86. rotatelogs.WithRotationCount(uint(rotationCount)),
  87. rotatelogs.WithRotationTime(time.Duration(rotationHours)*time.Hour),
  88. )
  89. }
  90. if v, ok := lfMap["warn"]; ok {
  91. writerMap[logrus.WarnLevel], _ = rotatelogs.New(
  92. v+".%Y%m%d",
  93. rotatelogs.WithLinkName(v),
  94. rotatelogs.WithRotationCount(uint(rotationCount)),
  95. rotatelogs.WithRotationTime(time.Duration(rotationHours)*time.Hour),
  96. )
  97. }
  98. if v, ok := lfMap["info"]; ok {
  99. writerMap[logrus.InfoLevel], _ = rotatelogs.New(
  100. v+".%Y%m%d",
  101. rotatelogs.WithLinkName(v),
  102. rotatelogs.WithRotationCount(uint(rotationCount)),
  103. rotatelogs.WithRotationTime(time.Duration(rotationHours)*time.Hour),
  104. )
  105. }
  106. if v, ok := lfMap["debug"]; ok {
  107. writerMap[logrus.DebugLevel], _ = rotatelogs.New(
  108. v+".%Y%m%d",
  109. rotatelogs.WithLinkName(v),
  110. rotatelogs.WithRotationCount(uint(rotationCount)),
  111. rotatelogs.WithRotationTime(time.Duration(rotationHours)*time.Hour),
  112. )
  113. }
  114. if v, ok := lfMap["trace"]; ok {
  115. writerMap[logrus.TraceLevel], _ = rotatelogs.New(
  116. v+".%Y%m%d",
  117. rotatelogs.WithLinkName(v),
  118. rotatelogs.WithRotationCount(uint(rotationCount)),
  119. rotatelogs.WithRotationTime(time.Duration(rotationHours)*time.Hour),
  120. )
  121. }
  122. var lfFormatter logrus.Formatter
  123. if viper.GetString("logger.local.file.formatter") == "json" {
  124. lfFormatter = &logrus.JSONFormatter{TimestampFormat: timeFormat}
  125. } else {
  126. lfFormatter = &logrus.TextFormatter{TimestampFormat: timeFormat}
  127. }
  128. lfHook := lfshook.NewHook(writerMap, lfFormatter)
  129. log.AddHook(lfHook)
  130. }
  131. entry := logrus.NewEntry(log)
  132. extra := viper.GetStringMap("logger.extra")
  133. if nil != extra && len(extra) > 0 {
  134. entry = entry.WithFields(extra)
  135. }
  136. return &Logger{Entry: *entry, Level: level}
  137. }
  138. func (logger *Logger) Print(args ...interface{}) {
  139. if args == nil || len(args) == 0 {
  140. return
  141. }
  142. if tp, ok := args[0].(string); ok {
  143. tp = strings.ToLower(strings.TrimSpace(tp))
  144. if "sql" == tp && len(args) == 6 {
  145. logger.printSql(args...)
  146. } else {
  147. logger.WithCaller(2).Entry.Print(args...)
  148. }
  149. } else {
  150. logger.WithCaller(2).Entry.Print(args...)
  151. }
  152. }
  153. func (logger *Logger) WithField(key string, value interface{}) *Logger {
  154. return &Logger{Entry: *logger.Entry.WithField(key, value)}
  155. }
  156. func (logger *Logger) WithFields(fields map[string]interface{}) *Logger {
  157. return &Logger{Entry: *logger.Entry.WithFields(fields)}
  158. }
  159. func (logger *Logger) WithError(err error) *Logger {
  160. return &Logger{Entry: *logger.Entry.WithError(err)}
  161. }
  162. func (logger *Logger) WithCaller(skip int) *Logger {
  163. if _, ok := logger.Data["codeline"]; ok {
  164. return logger
  165. }
  166. for i := 0; i < 100; i++ {
  167. if _, file, line, ok := runtime.Caller(i); ok {
  168. if strings.Contains(file, "git.haoqitour.com") &&
  169. !strings.Contains(file, "git.haoqitour.com/haoqi/go-common/utils/logger") {
  170. return logger.
  171. WithField("codeline", fmt.Sprintf("%s:%d", file, line))
  172. //WithField("func", runtime.FuncForPC(pc).Name())
  173. }
  174. }
  175. }
  176. if _, file, line, ok := runtime.Caller(skip); ok {
  177. return logger.
  178. WithField("codeline", fmt.Sprintf("%s:%d", file, line))
  179. //WithField("func", runtime.FuncForPC(pc).Name())
  180. }
  181. return logger
  182. }
  183. func (logger *Logger) Debugf(format string, args ...interface{}) {
  184. logger.WithCaller(2).Entry.Debugf(format, args...)
  185. }
  186. func (logger *Logger) Infof(format string, args ...interface{}) {
  187. logger.WithCaller(2).Entry.Infof(format, args...)
  188. }
  189. func (logger *Logger) Printf(format string, args ...interface{}) {
  190. logger.WithCaller(2).Entry.Printf(format, args...)
  191. }
  192. func (logger *Logger) Warnf(format string, args ...interface{}) {
  193. logger.WithCaller(2).Entry.Warnf(format, args...)
  194. }
  195. func (logger *Logger) Warningf(format string, args ...interface{}) {
  196. logger.WithCaller(2).Entry.Warningf(format, args...)
  197. }
  198. func (logger *Logger) Errorf(format string, args ...interface{}) {
  199. logger.WithCaller(2).Entry.Errorf(format, args...)
  200. }
  201. func (logger *Logger) Fatalf(format string, args ...interface{}) {
  202. logger.WithCaller(2).Entry.Fatalf(format, args...)
  203. }
  204. func (logger *Logger) Panicf(format string, args ...interface{}) {
  205. logger.WithCaller(2).Entry.Panicf(format, args...)
  206. }
  207. func (logger *Logger) Debug(args ...interface{}) {
  208. logger.WithCaller(2).Entry.Debug(args...)
  209. }
  210. func (logger *Logger) Info(args ...interface{}) {
  211. logger.WithCaller(2).Entry.Info(args...)
  212. }
  213. func (logger *Logger) Warn(args ...interface{}) {
  214. logger.WithCaller(2).Entry.Warn(args...)
  215. }
  216. func (logger *Logger) Warning(args ...interface{}) {
  217. logger.WithCaller(2).Entry.Warning(args...)
  218. }
  219. func (logger *Logger) Error(args ...interface{}) {
  220. logger.WithCaller(2).Entry.Error(args...)
  221. }
  222. func (logger *Logger) Fatal(args ...interface{}) {
  223. logger.WithCaller(2).Entry.Fatal(args...)
  224. }
  225. func (logger *Logger) Panic(args ...interface{}) {
  226. logger.WithCaller(2).Entry.Panic(args...)
  227. }
  228. func (logger *Logger) Debugln(args ...interface{}) {
  229. logger.WithCaller(2).Entry.Debugln(args...)
  230. }
  231. func (logger *Logger) Infoln(args ...interface{}) {
  232. logger.WithCaller(2).Entry.Infoln(args...)
  233. }
  234. func (logger *Logger) Println(args ...interface{}) {
  235. logger.WithCaller(2).Entry.Println(args...)
  236. }
  237. func (logger *Logger) Warnln(args ...interface{}) {
  238. logger.WithCaller(2).Entry.Warnln(args...)
  239. }
  240. func (logger *Logger) Warningln(args ...interface{}) {
  241. logger.WithCaller(2).Entry.Warningln(args...)
  242. }
  243. func (logger *Logger) Errorln(args ...interface{}) {
  244. logger.WithCaller(2).Entry.Errorln(args...)
  245. }
  246. func (logger *Logger) Fatalln(args ...interface{}) {
  247. logger.WithCaller(2).Entry.Fatalln(args...)
  248. }
  249. func (logger *Logger) Panicln(args ...interface{}) {
  250. logger.WithCaller(2).Entry.Panicln(args...)
  251. }
  252. var (
  253. sqlRegexp = regexp.MustCompile(`\?`)
  254. numericPlaceHolderRegexp = regexp.MustCompile(`\$\d+`)
  255. )
  256. func (logger *Logger) printSql(args ...interface{}) {
  257. length := len(args)
  258. var (
  259. codeLine, sql string
  260. params []interface{}
  261. latency time.Duration
  262. rows int64
  263. ok bool
  264. )
  265. if length > 1 {
  266. codeLine, _ = args[1].(string)
  267. }
  268. if length > 2 {
  269. latency, _ = args[2].(time.Duration)
  270. }
  271. if length > 3 {
  272. sql, ok = args[3].(string)
  273. if ok {
  274. sql = strings.TrimSpace(strings.Replace(strings.Replace(strings.Replace(sql, "\r\n", " ", -1), "\n", " ", -1), "\t", " ", -1))
  275. }
  276. }
  277. if length > 4 {
  278. params, _ = args[4].([]interface{})
  279. }
  280. if length > 5 {
  281. rows, _ = args[5].(int64)
  282. }
  283. lg := logger.
  284. WithField("tag", "SQL").
  285. WithField("sql", logger.getSql(sql, params))
  286. if len(codeLine) > 0 {
  287. lg = lg.WithField("codeline", strings.TrimSpace(codeLine))
  288. } else {
  289. lg = lg.WithCaller(9)
  290. }
  291. if latency > 0 {
  292. lg = lg.WithField("latency", fmt.Sprintf("%v", latency))
  293. }
  294. if rows != MaxInt64 {
  295. lg = lg.WithField("rows", fmt.Sprintf("%d rows affected or returned", rows))
  296. }
  297. lg.Info(fmt.Sprintf("%s %v", sql, params))
  298. }
  299. func (logger *Logger) getSql(originSql string, params []interface{}) string {
  300. var formattedValues []string
  301. for _, value := range params {
  302. indirectValue := reflect.Indirect(reflect.ValueOf(value))
  303. if indirectValue.IsValid() {
  304. value = indirectValue.Interface()
  305. if t, ok := value.(time.Time); ok {
  306. formattedValues = append(formattedValues, fmt.Sprintf("'%v'", t.Format("2006-01-02 15:04:05")))
  307. } else if b, ok := value.([]byte); ok {
  308. if str := string(b); logger.isPrintable(str) {
  309. formattedValues = append(formattedValues, fmt.Sprintf("'%v'", str))
  310. } else {
  311. formattedValues = append(formattedValues, "'<binary>'")
  312. }
  313. } else if r, ok := value.(driver.Valuer); ok {
  314. if value, err := r.Value(); err == nil && value != nil {
  315. formattedValues = append(formattedValues, fmt.Sprintf("'%v'", value))
  316. } else {
  317. formattedValues = append(formattedValues, "NULL")
  318. }
  319. } else {
  320. formattedValues = append(formattedValues, fmt.Sprintf("'%v'", value))
  321. }
  322. } else {
  323. formattedValues = append(formattedValues, "NULL")
  324. }
  325. }
  326. var sql string
  327. // differentiate between $n placeholders or else treat like ?
  328. if numericPlaceHolderRegexp.MatchString(originSql) {
  329. for index, value := range formattedValues {
  330. placeholder := fmt.Sprintf(`\$%d([^\d]|$)`, index+1)
  331. sql = regexp.MustCompile(placeholder).ReplaceAllString(originSql, value+"$1")
  332. }
  333. } else {
  334. formattedValuesLength := len(formattedValues)
  335. for index, value := range sqlRegexp.Split(originSql, -1) {
  336. sql += value
  337. if index < formattedValuesLength {
  338. sql += formattedValues[index]
  339. }
  340. }
  341. }
  342. return sql
  343. }
  344. func (logger *Logger) isPrintable(s string) bool {
  345. for _, r := range s {
  346. if !unicode.IsPrint(r) {
  347. return false
  348. }
  349. }
  350. return true
  351. }