logger.go 11 KB

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