log.go 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. package log
  2. import (
  3. "context"
  4. "flag"
  5. "fmt"
  6. "io"
  7. "os"
  8. "strconv"
  9. "time"
  10. "go-common/library/conf/env"
  11. "go-common/library/log/internal"
  12. "go-common/library/stat/prom"
  13. xtime "go-common/library/time"
  14. )
  15. // Config log config.
  16. type Config struct {
  17. Family string
  18. Host string
  19. // stdout
  20. Stdout bool
  21. // file
  22. Dir string
  23. // buffer size
  24. FileBufferSize int64
  25. // MaxLogFile
  26. MaxLogFile int
  27. // RotateSize
  28. RotateSize int64
  29. // log-agent
  30. Agent *AgentConfig
  31. // V Enable V-leveled logging at the specified level.
  32. V int32
  33. // Module=""
  34. // The syntax of the argument is a map of pattern=N,
  35. // where pattern is a literal file name (minus the ".go" suffix) or
  36. // "glob" pattern and N is a V level. For instance:
  37. // [module]
  38. // "service" = 1
  39. // "dao*" = 2
  40. // sets the V level to 2 in all Go files whose names begin "dao".
  41. Module map[string]int32
  42. // Filter tell log handler which field are sensitive message, use * instead.
  43. Filter []string
  44. }
  45. // errProm prometheus error counter.
  46. var errProm = prom.BusinessErrCount
  47. // Render render log output
  48. type Render interface {
  49. Render(io.Writer, map[string]interface{}) error
  50. RenderString(map[string]interface{}) string
  51. }
  52. // D represents a map of entry level data used for structured logging.
  53. // type D map[string]interface{}
  54. type D struct {
  55. Key string
  56. Value interface{}
  57. }
  58. // AddTo exports a field through the ObjectEncoder interface. It's primarily
  59. // useful to library authors, and shouldn't be necessary in most applications.
  60. func (d D) AddTo(enc core.ObjectEncoder) {
  61. var err error
  62. switch val := d.Value.(type) {
  63. case bool:
  64. enc.AddBool(d.Key, val)
  65. case complex128:
  66. enc.AddComplex128(d.Key, val)
  67. case complex64:
  68. enc.AddComplex64(d.Key, val)
  69. case float64:
  70. enc.AddFloat64(d.Key, val)
  71. case float32:
  72. enc.AddFloat32(d.Key, val)
  73. case int:
  74. enc.AddInt(d.Key, val)
  75. case int64:
  76. enc.AddInt64(d.Key, val)
  77. case int32:
  78. enc.AddInt32(d.Key, val)
  79. case int16:
  80. enc.AddInt16(d.Key, val)
  81. case int8:
  82. enc.AddInt8(d.Key, val)
  83. case string:
  84. enc.AddString(d.Key, val)
  85. case uint:
  86. enc.AddUint(d.Key, val)
  87. case uint64:
  88. enc.AddUint64(d.Key, val)
  89. case uint32:
  90. enc.AddUint32(d.Key, val)
  91. case uint16:
  92. enc.AddUint16(d.Key, val)
  93. case uint8:
  94. enc.AddUint8(d.Key, val)
  95. case []byte:
  96. enc.AddByteString(d.Key, val)
  97. case uintptr:
  98. enc.AddUintptr(d.Key, val)
  99. case time.Time:
  100. enc.AddTime(d.Key, val)
  101. case xtime.Time:
  102. enc.AddTime(d.Key, val.Time())
  103. case time.Duration:
  104. enc.AddDuration(d.Key, val)
  105. case xtime.Duration:
  106. enc.AddDuration(d.Key, time.Duration(val))
  107. case error:
  108. enc.AddString(d.Key, val.Error())
  109. case fmt.Stringer:
  110. enc.AddString(d.Key, val.String())
  111. default:
  112. err = enc.AddReflected(d.Key, val)
  113. }
  114. if err != nil {
  115. enc.AddString(fmt.Sprintf("%sError", d.Key), err.Error())
  116. }
  117. }
  118. // KV return a log kv for logging field.
  119. func KV(key string, value interface{}) D {
  120. return D{
  121. Key: key,
  122. Value: value,
  123. }
  124. }
  125. var (
  126. h Handler
  127. c *Config
  128. )
  129. func init() {
  130. host, _ := os.Hostname()
  131. c = &Config{
  132. Family: env.AppID,
  133. Host: host,
  134. }
  135. h = newHandlers([]string{}, NewStdout())
  136. addFlag(flag.CommandLine)
  137. }
  138. var (
  139. _v int
  140. _stdout bool
  141. _dir string
  142. _agentDSN string
  143. _filter logFilter
  144. _module = verboseModule{}
  145. _noagent bool
  146. )
  147. // addFlag init log from dsn.
  148. func addFlag(fs *flag.FlagSet) {
  149. if lv, err := strconv.ParseInt(os.Getenv("LOG_V"), 10, 64); err == nil {
  150. _v = int(lv)
  151. }
  152. _stdout, _ = strconv.ParseBool(os.Getenv("LOG_STDOUT"))
  153. _dir = os.Getenv("LOG_DIR")
  154. if _agentDSN = os.Getenv("LOG_AGENT"); _agentDSN == "" {
  155. _agentDSN = _defaultAgentConfig
  156. }
  157. if tm := os.Getenv("LOG_MODULE"); len(tm) > 0 {
  158. _module.Set(tm)
  159. }
  160. if tf := os.Getenv("LOG_FILTER"); len(tf) > 0 {
  161. _filter.Set(tf)
  162. }
  163. _noagent, _ = strconv.ParseBool(os.Getenv("LOG_NO_AGENT"))
  164. // get val from flag
  165. fs.IntVar(&_v, "log.v", _v, "log verbose level, or use LOG_V env variable.")
  166. fs.BoolVar(&_stdout, "log.stdout", _stdout, "log enable stdout or not, or use LOG_STDOUT env variable.")
  167. fs.StringVar(&_dir, "log.dir", _dir, "log file `path, or use LOG_DIR env variable.")
  168. fs.StringVar(&_agentDSN, "log.agent", _agentDSN, "log agent dsn, or use LOG_AGENT env variable.")
  169. fs.Var(&_module, "log.module", "log verbose for specified module, or use LOG_MODULE env variable, format: file=1,file2=2.")
  170. fs.Var(&_filter, "log.filter", "log field for sensitive message, or use LOG_FILTER env variable, format: field1,field2.")
  171. fs.BoolVar(&_noagent, "log.noagent", false, "force disable log agent print log to stderr, or use LOG_NO_AGENT")
  172. }
  173. // Init create logger with context.
  174. func Init(conf *Config) {
  175. var isNil bool
  176. if conf == nil {
  177. isNil = true
  178. conf = &Config{
  179. Stdout: _stdout,
  180. Dir: _dir,
  181. V: int32(_v),
  182. Module: _module,
  183. Filter: _filter,
  184. }
  185. }
  186. if len(env.AppID) != 0 {
  187. conf.Family = env.AppID // for caster
  188. }
  189. conf.Host = env.Hostname
  190. if len(conf.Host) == 0 {
  191. host, _ := os.Hostname()
  192. conf.Host = host
  193. }
  194. var hs []Handler
  195. // when env is dev
  196. if conf.Stdout || (isNil && (env.DeployEnv == "" || env.DeployEnv == env.DeployEnvDev)) || _noagent {
  197. hs = append(hs, NewStdout())
  198. }
  199. if conf.Dir != "" {
  200. hs = append(hs, NewFile(conf.Dir, conf.FileBufferSize, conf.RotateSize, conf.MaxLogFile))
  201. }
  202. // when env is not dev
  203. if !_noagent && (conf.Agent != nil || (isNil && env.DeployEnv != "" && env.DeployEnv != env.DeployEnvDev)) {
  204. hs = append(hs, NewAgent(conf.Agent))
  205. }
  206. h = newHandlers(conf.Filter, hs...)
  207. c = conf
  208. }
  209. // Info logs a message at the info log level.
  210. func Info(format string, args ...interface{}) {
  211. h.Log(context.Background(), _infoLevel, KV(_log, fmt.Sprintf(format, args...)))
  212. }
  213. // Warn logs a message at the warning log level.
  214. func Warn(format string, args ...interface{}) {
  215. h.Log(context.Background(), _warnLevel, KV(_log, fmt.Sprintf(format, args...)))
  216. }
  217. // Error logs a message at the error log level.
  218. func Error(format string, args ...interface{}) {
  219. h.Log(context.Background(), _errorLevel, KV(_log, fmt.Sprintf(format, args...)))
  220. }
  221. // Infov logs a message at the info log level.
  222. func Infov(ctx context.Context, args ...D) {
  223. h.Log(ctx, _infoLevel, args...)
  224. }
  225. // Warnv logs a message at the warning log level.
  226. func Warnv(ctx context.Context, args ...D) {
  227. h.Log(ctx, _warnLevel, args...)
  228. }
  229. // Errorv logs a message at the error log level.
  230. func Errorv(ctx context.Context, args ...D) {
  231. h.Log(ctx, _errorLevel, args...)
  232. }
  233. func logw(args []interface{}) []D {
  234. if len(args)%2 != 0 {
  235. Warn("log: the variadic must be plural, the last one will ignored")
  236. }
  237. ds := make([]D, 0, len(args)/2)
  238. for i := 0; i < len(args)-1; i = i + 2 {
  239. if key, ok := args[i].(string); ok {
  240. ds = append(ds, KV(key, args[i+1]))
  241. } else {
  242. Warn("log: key must be string, get %T, ignored", args[i])
  243. }
  244. }
  245. return ds
  246. }
  247. // Infow logs a message with some additional context. The variadic key-value pairs are treated as they are in With.
  248. func Infow(ctx context.Context, args ...interface{}) {
  249. h.Log(ctx, _infoLevel, logw(args)...)
  250. }
  251. // Warnw logs a message with some additional context. The variadic key-value pairs are treated as they are in With.
  252. func Warnw(ctx context.Context, args ...interface{}) {
  253. h.Log(ctx, _warnLevel, logw(args)...)
  254. }
  255. // Errorw logs a message with some additional context. The variadic key-value pairs are treated as they are in With.
  256. func Errorw(ctx context.Context, args ...interface{}) {
  257. h.Log(ctx, _errorLevel, logw(args)...)
  258. }
  259. // SetFormat only effective on stdout and file handler
  260. // %T time format at "15:04:05.999" on stdout handler, "15:04:05 MST" on file handler
  261. // %t time format at "15:04:05" on stdout handler, "15:04" on file on file handler
  262. // %D data format at "2006/01/02"
  263. // %d data format at "01/02"
  264. // %L log level e.g. INFO WARN ERROR
  265. // %M log message and additional fields: key=value this is log message
  266. // NOTE below pattern not support on file handler
  267. // %f function name and line number e.g. model.Get:121
  268. // %i instance id
  269. // %e deploy env e.g. dev uat fat prod
  270. // %z zone
  271. // %S full file name and line number: /a/b/c/d.go:23
  272. // %s final file name element and line number: d.go:23
  273. func SetFormat(format string) {
  274. h.SetFormat(format)
  275. }
  276. // Close close resource.
  277. func Close() (err error) {
  278. err = h.Close()
  279. h = _defaultStdout
  280. return
  281. }
  282. func errIncr(lv Level, source string) {
  283. if lv == _errorLevel {
  284. errProm.Incr(source)
  285. }
  286. }