123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318 |
- package log
- import (
- "context"
- "flag"
- "fmt"
- "io"
- "os"
- "strconv"
- "time"
- "go-common/library/conf/env"
- "go-common/library/log/internal"
- "go-common/library/stat/prom"
- xtime "go-common/library/time"
- )
- // Config log config.
- type Config struct {
- Family string
- Host string
- // stdout
- Stdout bool
- // file
- Dir string
- // buffer size
- FileBufferSize int64
- // MaxLogFile
- MaxLogFile int
- // RotateSize
- RotateSize int64
- // log-agent
- Agent *AgentConfig
- // V Enable V-leveled logging at the specified level.
- V int32
- // Module=""
- // The syntax of the argument is a map of pattern=N,
- // where pattern is a literal file name (minus the ".go" suffix) or
- // "glob" pattern and N is a V level. For instance:
- // [module]
- // "service" = 1
- // "dao*" = 2
- // sets the V level to 2 in all Go files whose names begin "dao".
- Module map[string]int32
- // Filter tell log handler which field are sensitive message, use * instead.
- Filter []string
- }
- // errProm prometheus error counter.
- var errProm = prom.BusinessErrCount
- // Render render log output
- type Render interface {
- Render(io.Writer, map[string]interface{}) error
- RenderString(map[string]interface{}) string
- }
- // D represents a map of entry level data used for structured logging.
- // type D map[string]interface{}
- type D struct {
- Key string
- Value interface{}
- }
- // AddTo exports a field through the ObjectEncoder interface. It's primarily
- // useful to library authors, and shouldn't be necessary in most applications.
- func (d D) AddTo(enc core.ObjectEncoder) {
- var err error
- switch val := d.Value.(type) {
- case bool:
- enc.AddBool(d.Key, val)
- case complex128:
- enc.AddComplex128(d.Key, val)
- case complex64:
- enc.AddComplex64(d.Key, val)
- case float64:
- enc.AddFloat64(d.Key, val)
- case float32:
- enc.AddFloat32(d.Key, val)
- case int:
- enc.AddInt(d.Key, val)
- case int64:
- enc.AddInt64(d.Key, val)
- case int32:
- enc.AddInt32(d.Key, val)
- case int16:
- enc.AddInt16(d.Key, val)
- case int8:
- enc.AddInt8(d.Key, val)
- case string:
- enc.AddString(d.Key, val)
- case uint:
- enc.AddUint(d.Key, val)
- case uint64:
- enc.AddUint64(d.Key, val)
- case uint32:
- enc.AddUint32(d.Key, val)
- case uint16:
- enc.AddUint16(d.Key, val)
- case uint8:
- enc.AddUint8(d.Key, val)
- case []byte:
- enc.AddByteString(d.Key, val)
- case uintptr:
- enc.AddUintptr(d.Key, val)
- case time.Time:
- enc.AddTime(d.Key, val)
- case xtime.Time:
- enc.AddTime(d.Key, val.Time())
- case time.Duration:
- enc.AddDuration(d.Key, val)
- case xtime.Duration:
- enc.AddDuration(d.Key, time.Duration(val))
- case error:
- enc.AddString(d.Key, val.Error())
- case fmt.Stringer:
- enc.AddString(d.Key, val.String())
- default:
- err = enc.AddReflected(d.Key, val)
- }
- if err != nil {
- enc.AddString(fmt.Sprintf("%sError", d.Key), err.Error())
- }
- }
- // KV return a log kv for logging field.
- func KV(key string, value interface{}) D {
- return D{
- Key: key,
- Value: value,
- }
- }
- var (
- h Handler
- c *Config
- )
- func init() {
- host, _ := os.Hostname()
- c = &Config{
- Family: env.AppID,
- Host: host,
- }
- h = newHandlers([]string{}, NewStdout())
- addFlag(flag.CommandLine)
- }
- var (
- _v int
- _stdout bool
- _dir string
- _agentDSN string
- _filter logFilter
- _module = verboseModule{}
- _noagent bool
- )
- // addFlag init log from dsn.
- func addFlag(fs *flag.FlagSet) {
- if lv, err := strconv.ParseInt(os.Getenv("LOG_V"), 10, 64); err == nil {
- _v = int(lv)
- }
- _stdout, _ = strconv.ParseBool(os.Getenv("LOG_STDOUT"))
- _dir = os.Getenv("LOG_DIR")
- if _agentDSN = os.Getenv("LOG_AGENT"); _agentDSN == "" {
- _agentDSN = _defaultAgentConfig
- }
- if tm := os.Getenv("LOG_MODULE"); len(tm) > 0 {
- _module.Set(tm)
- }
- if tf := os.Getenv("LOG_FILTER"); len(tf) > 0 {
- _filter.Set(tf)
- }
- _noagent, _ = strconv.ParseBool(os.Getenv("LOG_NO_AGENT"))
- // get val from flag
- fs.IntVar(&_v, "log.v", _v, "log verbose level, or use LOG_V env variable.")
- fs.BoolVar(&_stdout, "log.stdout", _stdout, "log enable stdout or not, or use LOG_STDOUT env variable.")
- fs.StringVar(&_dir, "log.dir", _dir, "log file `path, or use LOG_DIR env variable.")
- fs.StringVar(&_agentDSN, "log.agent", _agentDSN, "log agent dsn, or use LOG_AGENT env variable.")
- fs.Var(&_module, "log.module", "log verbose for specified module, or use LOG_MODULE env variable, format: file=1,file2=2.")
- fs.Var(&_filter, "log.filter", "log field for sensitive message, or use LOG_FILTER env variable, format: field1,field2.")
- fs.BoolVar(&_noagent, "log.noagent", false, "force disable log agent print log to stderr, or use LOG_NO_AGENT")
- }
- // Init create logger with context.
- func Init(conf *Config) {
- var isNil bool
- if conf == nil {
- isNil = true
- conf = &Config{
- Stdout: _stdout,
- Dir: _dir,
- V: int32(_v),
- Module: _module,
- Filter: _filter,
- }
- }
- if len(env.AppID) != 0 {
- conf.Family = env.AppID // for caster
- }
- conf.Host = env.Hostname
- if len(conf.Host) == 0 {
- host, _ := os.Hostname()
- conf.Host = host
- }
- var hs []Handler
- // when env is dev
- if conf.Stdout || (isNil && (env.DeployEnv == "" || env.DeployEnv == env.DeployEnvDev)) || _noagent {
- hs = append(hs, NewStdout())
- }
- if conf.Dir != "" {
- hs = append(hs, NewFile(conf.Dir, conf.FileBufferSize, conf.RotateSize, conf.MaxLogFile))
- }
- // when env is not dev
- if !_noagent && (conf.Agent != nil || (isNil && env.DeployEnv != "" && env.DeployEnv != env.DeployEnvDev)) {
- hs = append(hs, NewAgent(conf.Agent))
- }
- h = newHandlers(conf.Filter, hs...)
- c = conf
- }
- // Info logs a message at the info log level.
- func Info(format string, args ...interface{}) {
- h.Log(context.Background(), _infoLevel, KV(_log, fmt.Sprintf(format, args...)))
- }
- // Warn logs a message at the warning log level.
- func Warn(format string, args ...interface{}) {
- h.Log(context.Background(), _warnLevel, KV(_log, fmt.Sprintf(format, args...)))
- }
- // Error logs a message at the error log level.
- func Error(format string, args ...interface{}) {
- h.Log(context.Background(), _errorLevel, KV(_log, fmt.Sprintf(format, args...)))
- }
- // Infov logs a message at the info log level.
- func Infov(ctx context.Context, args ...D) {
- h.Log(ctx, _infoLevel, args...)
- }
- // Warnv logs a message at the warning log level.
- func Warnv(ctx context.Context, args ...D) {
- h.Log(ctx, _warnLevel, args...)
- }
- // Errorv logs a message at the error log level.
- func Errorv(ctx context.Context, args ...D) {
- h.Log(ctx, _errorLevel, args...)
- }
- func logw(args []interface{}) []D {
- if len(args)%2 != 0 {
- Warn("log: the variadic must be plural, the last one will ignored")
- }
- ds := make([]D, 0, len(args)/2)
- for i := 0; i < len(args)-1; i = i + 2 {
- if key, ok := args[i].(string); ok {
- ds = append(ds, KV(key, args[i+1]))
- } else {
- Warn("log: key must be string, get %T, ignored", args[i])
- }
- }
- return ds
- }
- // Infow logs a message with some additional context. The variadic key-value pairs are treated as they are in With.
- func Infow(ctx context.Context, args ...interface{}) {
- h.Log(ctx, _infoLevel, logw(args)...)
- }
- // Warnw logs a message with some additional context. The variadic key-value pairs are treated as they are in With.
- func Warnw(ctx context.Context, args ...interface{}) {
- h.Log(ctx, _warnLevel, logw(args)...)
- }
- // Errorw logs a message with some additional context. The variadic key-value pairs are treated as they are in With.
- func Errorw(ctx context.Context, args ...interface{}) {
- h.Log(ctx, _errorLevel, logw(args)...)
- }
- // SetFormat only effective on stdout and file handler
- // %T time format at "15:04:05.999" on stdout handler, "15:04:05 MST" on file handler
- // %t time format at "15:04:05" on stdout handler, "15:04" on file on file handler
- // %D data format at "2006/01/02"
- // %d data format at "01/02"
- // %L log level e.g. INFO WARN ERROR
- // %M log message and additional fields: key=value this is log message
- // NOTE below pattern not support on file handler
- // %f function name and line number e.g. model.Get:121
- // %i instance id
- // %e deploy env e.g. dev uat fat prod
- // %z zone
- // %S full file name and line number: /a/b/c/d.go:23
- // %s final file name element and line number: d.go:23
- func SetFormat(format string) {
- h.SetFormat(format)
- }
- // Close close resource.
- func Close() (err error) {
- err = h.Close()
- h = _defaultStdout
- return
- }
- func errIncr(lv Level, source string) {
- if lv == _errorLevel {
- errProm.Incr(source)
- }
- }
|