diff --git a/adapters/redis_adapter.go b/adapters/redis_adapter.go index 9c427ca..c1a3239 100644 --- a/adapters/redis_adapter.go +++ b/adapters/redis_adapter.go @@ -4,14 +4,182 @@ package adapters import ( + "encoding/json" + "fmt" + "io/ioutil" + "math/rand" + "time" + + "github.com/gomodule/redigo/redis" + "gopkg.in/yaml.v3" + "github.com/fuyibing/log/interfaces" ) +type redisConfig struct { + Address string `yaml:"addr"` + Database int `yaml:"database"` + IdleTimeout int `yaml:"idle-timeout"` + KeepAlive int `yaml:"keep-alive"` + MaxActive int `yaml:"max-active"` + MaxIdle int `yaml:"max-idle"` + MaxLifetime int `yaml:"max-lifetime"` + Network string `yaml:"network"` + Password string `yaml:"password"` + ReadTimeout int `yaml:"read-timeout"` + Timeout int `yaml:"timeout"` + Wait bool `yaml:"wait"` + WriteTimeout int `yaml:"write-timeout"` + KeyLifetime int `yaml:"key-lifetime"` + KeyPrefix string `yaml:"key-prefix"` + KeyList string `yaml:"key-list"` +} + type redisAdapter struct { + Conf *redisConfig `yaml:"redis"` + ch chan interfaces.LineInterface + pool *redis.Pool + handler interfaces.Handler +} + +func (o *redisAdapter) Run(line interfaces.LineInterface) { + go func() { + o.ch <- line + }() } -func (o *redisAdapter) Run(lineInterface interfaces.LineInterface) {} +func (o *redisAdapter) body(line interfaces.LineInterface) string { + // Init + data := make(map[string]interface{}) + // Basic. + data["content"] = line.Content() + data["duration"] = line.Duration() + data["level"] = line.Level() + data["time"] = line.Timeline() + // Tracing. + data["action"] = "" + if line.Tracing() { + data["parentSpanId"] = line.ParentSpanId() + data["requestId"] = line.TraceId() + data["requestMethod"], data["requestUrl"] = line.RequestInfo() + data["spanId"] = line.SpanId() + data["traceId"] = line.TraceId() + data["version"] = line.SpanVersion() + } else { + data["parentSpanId"] = "" + data["requestId"] = "" + data["requestMethod"] = "" + data["requestUrl"] = "" + data["spanId"] = "" + data["traceId"] = "" + data["version"] = "" + } + // Server. + data["module"] = line.ServiceName() + data["pid"] = line.Pid() + data["serverAddr"] = line.ServiceAddr() + data["taskId"] = 0 + data["taskName"] = "" + // JSON string. + if body, err := json.Marshal(data); err == nil { + return string(body) + } + return "" +} + +func (o *redisAdapter) listen() { + go func() { + defer o.listen() + for { + select { + case line := <-o.ch: + go o.send(line) + } + } + }() +} +func (o *redisAdapter) send(line interfaces.LineInterface) { + // Catch panic. + defer func() { + if r := recover(); r != nil { + o.handler(line) + } + }() + // Pool: get & release. + p := o.pool.Get() + defer func() { + _ = p.Close() + }() + // Send command. + data := o.body(line) + keyList := fmt.Sprintf("%s:%s", o.Conf.KeyPrefix, o.Conf.KeyList) + keyItem := fmt.Sprintf("%s:t%10d%09d%12d", o.Conf.KeyPrefix, line.Time().Unix(), line.Time().Nanosecond(), rand.Int63n(999999999999)) + if err := p.Send("SET", keyItem, data, "EX", o.Conf.KeyLifetime); err == nil { + _ = p.Send("RPUSH", keyList, keyItem) + } +} + +// 创建适配器. func NewRedis() *redisAdapter { - return &redisAdapter{} -} \ No newline at end of file + o := &redisAdapter{ch: make(chan interfaces.LineInterface), handler: NewTerm().Run} + // Parse configuration. + // 1. base config. + for _, file := range []string{"./tmp/log.yaml", "../tmp/log.yaml", "./config/log.yaml", "../config/log.yaml"} { + body, err := ioutil.ReadFile(file) + if err != nil { + continue + } + if yaml.Unmarshal(body, o) != nil { + continue + } + break + } + // 2. key settings. + if o.Conf.KeyLifetime == 0 { + o.Conf.KeyLifetime = 7200 + } + if o.Conf.KeyPrefix == "" { + o.Conf.KeyPrefix = "logger" + } + if o.Conf.KeyList == "" { + o.Conf.KeyList = "list" + } + // 3. Redis pool. + o.pool = &redis.Pool{MaxActive: o.Conf.MaxActive, MaxIdle: o.Conf.MaxIdle, Wait: o.Conf.Wait} + // 3.1 lifetime + if o.Conf.MaxLifetime > 0 { + o.pool.MaxConnLifetime = time.Duration(o.Conf.MaxLifetime) * time.Second + } + // 3.2 timeout: idle + if o.Conf.IdleTimeout > 0 { + o.pool.IdleTimeout = time.Duration(o.Conf.IdleTimeout) * time.Second + } + // 3.3 Connect + o.pool.Dial = func() (redis.Conn, error) { + // options: default. + opts := make([]redis.DialOption, 0) + opts = append(opts, redis.DialPassword(o.Conf.Password), redis.DialDatabase(o.Conf.Database)) + // options: timeouts. + // connect + // read + // write + if o.Conf.Timeout > 0 { + opts = append(opts, redis.DialConnectTimeout(time.Duration(o.Conf.Timeout)*time.Second)) + } + if o.Conf.ReadTimeout > 0 { + opts = append(opts, redis.DialReadTimeout(time.Duration(o.Conf.ReadTimeout)*time.Second)) + } + if o.Conf.WriteTimeout > 0 { + opts = append(opts, redis.DialWriteTimeout(time.Duration(o.Conf.WriteTimeout)*time.Second)) + } + // options: keep alive + if o.Conf.KeepAlive > 0 { + opts = append(opts, redis.DialKeepAlive(time.Duration(o.Conf.KeepAlive)*time.Second)) + } + // create connection + return redis.Dial(o.Conf.Network, o.Conf.Address, opts...) + } + o.listen() + return o +} diff --git a/client.go b/client.go index e8cc39c..c36dc68 100644 --- a/client.go +++ b/client.go @@ -20,78 +20,91 @@ func newClient() interfaces.ClientInterface { return o } +// 添加Debug日志. func (o *client) Debug(text string) { if Config.DebugOn() { o.log(nil, interfaces.LevelDebug, text) } } +// 添加Debug日志, 支持格式化. func (o *client) Debugf(text string, args ...interface{}) { if Config.DebugOn() { o.log(nil, interfaces.LevelDebug, text, args...) } } +// 添加Debug日志, 支持格式化和请求链. func (o *client) Debugfc(ctx context.Context, text string, args ...interface{}) { if Config.DebugOn() { o.log(ctx, interfaces.LevelDebug, text, args...) } } +// 添加Info日志. func (o *client) Info(text string) { if Config.InfoOn() { o.log(nil, interfaces.LevelInfo, text) } } +// 添加Info日志, 支持格式化. func (o *client) Infof(text string, args ...interface{}) { if Config.InfoOn() { o.log(nil, interfaces.LevelInfo, text, args...) } } +// 添加Info日志, 支持格式化和请求链. func (o *client) Infofc(ctx context.Context, text string, args ...interface{}) { if Config.InfoOn() { o.log(ctx, interfaces.LevelInfo, text, args...) } } +// 添加Warn日志. func (o *client) Warn(text string) { if Config.WarnOn() { o.log(nil, interfaces.LevelWarn, text) } } +// 添加Warn日志, 支持格式化. func (o *client) Warnf(text string, args ...interface{}) { if Config.WarnOn() { o.log(nil, interfaces.LevelWarn, text, args...) } } +// 添加Warn日志, 支持格式化和请求链. func (o *client) Warnfc(ctx context.Context, text string, args ...interface{}) { if Config.WarnOn() { o.log(ctx, interfaces.LevelWarn, text, args...) } } +// 添加Error日志. func (o *client) Error(text string) { if Config.ErrorOn() { o.log(nil, interfaces.LevelError, text) } } +// 添加Error日志, 支持格式化. func (o *client) Errorf(text string, args ...interface{}) { if Config.ErrorOn() { o.log(nil, interfaces.LevelError, text, args...) } } +// 添加Error日志, 支持格式化和请求链. func (o *client) Errorfc(ctx context.Context, text string, args ...interface{}) { if Config.ErrorOn() { o.log(ctx, interfaces.LevelError, text, args...) } } +// 日志处理逻辑. func (o *client) log(ctx context.Context, level interfaces.Level, text string, args ...interface{}) { if handler := Config.GetHandler(); handler != nil { handler(NewLine(ctx, level, text, args)) diff --git a/config/log.yaml b/config/log.yaml index ce98742..0f03370 100644 --- a/config/log.yaml +++ b/config/log.yaml @@ -19,11 +19,11 @@ # [idle-timeout]: 连结空闲N秒后自动关闭 # [max-conn-lifetime]: # -adapter: term +adapter: redis level: debug time: "2006-01-02 15:04:05.999999" redis: - addr: "127.0.0.1:6379" + addr: "192.168.3.133:6379" database: 0 idle-timeout: 30 keep-alive: 60 @@ -31,7 +31,7 @@ redis: max-idle: 2 max-lifetime: 60 network: "tcp" - password: "" + password: "uniondrug@123" timeout: 0 read-timeout: 0 wait: true diff --git a/context.go b/context.go index 71919f8..b001cb9 100644 --- a/context.go +++ b/context.go @@ -17,8 +17,8 @@ import ( func BindRequest(req *http.Request) { // Bound and reuse. if ctx := req.Context().Value(interfaces.OpenTracingKey); ctx != nil { - if tracing, ok := ctx.(*tracing); ok { - tracing.UseRequest(req) + if t, ok := ctx.(*tracing); ok { + t.UseRequest(req) return } } @@ -26,6 +26,7 @@ func BindRequest(req *http.Request) { req.WithContext(context.WithValue(context.TODO(), interfaces.OpenTracingKey, NewTracing().UseRequest(req))) } +// 创建上下文. func NewContext() context.Context { return context.WithValue(context.TODO(), interfaces.OpenTracingKey, NewTracing().UseDefault()) } diff --git a/func.go b/func.go index 1e8b5a9..8793e56 100644 --- a/func.go +++ b/func.go @@ -7,72 +7,84 @@ import ( "context" ) +// 添加Debug日志. func Debug(text string) { if Config.DebugOn() { Client.Debug(text) } } +// 添加Debug日志, 支持格式化. func Debugf(text string, args ...interface{}) { if Config.DebugOn() { Client.Debugf(text, args...) } } +// 添加Debug日志, 支持格式化和请求链. func Debugfc(ctx context.Context, text string, args ...interface{}) { if Config.DebugOn() { Client.Debugfc(ctx, text, args...) } } +// 添加Info日志. func Info(text string) { if Config.InfoOn() { Client.Info(text) } } +// 添加Info日志, 支持格式化. func Infof(text string, args ...interface{}) { if Config.InfoOn() { Client.Infof(text, args...) } } +// 添加Info日志, 支持格式化和请求链. func Infofc(ctx context.Context, text string, args ...interface{}) { if Config.InfoOn() { Client.Infofc(ctx, text, args...) } } +// 添加Warn日志. func Warn(text string) { if Config.WarnOn() { Client.Warn(text) } } +// 添加Warn日志, 支持格式化. func Warnf(text string, args ...interface{}) { if Config.WarnOn() { Client.Warnf(text, args...) } } +// 添加Warn日志, 支持格式化和请求链. func Warnfc(ctx context.Context, text string, args ...interface{}) { if Config.WarnOn() { Client.Warnfc(ctx, text, args...) } } +// 添加Error日志. func Error(text string) { if Config.ErrorOn() { Client.Error(text) } } +// 添加Error日志, 支持格式化. func Errorf(text string, args ...interface{}) { if Config.ErrorOn() { Client.Errorf(text, args...) } } +// 添加Error日志, 支持格式化和请求链. func Errorfc(ctx context.Context, text string, args ...interface{}) { if Config.ErrorOn() { Client.Errorfc(ctx, text, args...) diff --git a/go.mod b/go.mod index f07a750..90dc320 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/fuyibing/log go 1.14 require ( + github.com/gomodule/redigo v1.8.4 github.com/google/uuid v1.2.0 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b ) diff --git a/go.sum b/go.sum index 276d178..27f44fc 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,17 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gomodule/redigo v1.8.4 h1:Z5JUg94HMTR1XpwBaSH4vq3+PNSIykBLxMdglbw10gg= +github.com/gomodule/redigo v1.8.4/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/interfaces/client_interface.go b/interfaces/client_interface.go index d406375..c42410e 100644 --- a/interfaces/client_interface.go +++ b/interfaces/client_interface.go @@ -9,16 +9,39 @@ import ( // 客户端接口 type ClientInterface interface { + // 添加Debug日志. Debug(text string) + + // 添加Debug日志, 支持格式化. Debugf(text string, args ...interface{}) + + // 添加Debug日志, 支持格式化和请求链. Debugfc(ctx context.Context, text string, args ...interface{}) + + // 添加Info日志. Info(text string) + + // 添加Info日志, 支持格式化. Infof(text string, args ...interface{}) + + // 添加Info日志, 支持格式化和请求链. Infofc(ctx context.Context, text string, args ...interface{}) + + // 添加Warn日志. Warn(text string) + + // 添加Warn日志, 支持格式化. Warnf(text string, args ...interface{}) + + // 添加Warn日志, 支持格式化和请求链. Warnfc(ctx context.Context, text string, args ...interface{}) + + // 添加Error日志. Error(text string) + + // 添加Error日志, 支持格式化. Errorf(text string, args ...interface{}) + + // 添加Error日志, 支持格式化和请求链. Errorfc(ctx context.Context, text string, args ...interface{}) } diff --git a/tests/func_test.go b/tests/func_test.go index a8127e1..c738c89 100644 --- a/tests/func_test.go +++ b/tests/func_test.go @@ -5,14 +5,21 @@ package tests import ( "testing" + "time" "github.com/fuyibing/log" + "github.com/fuyibing/log/interfaces" ) func TestFunc(t *testing.T) { ctx := log.NewContext() + println("traceid:", ctx.Value(interfaces.OpenTracingKey).(interfaces.TraceInterface).GetTraceId()) + log.Client.Debugfc(ctx, "debug fc") log.Client.Infofc(ctx, "info fc") log.Client.Warnfc(ctx, "warn fc") log.Client.Errorfc(ctx, "error fc") + + + time.Sleep(time.Second * 60) }