package pkg import ( "encoding/json" "fmt" "strconv" "time" sls "github.com/aliyun/aliyun-log-go-sdk" "github.com/aliyun/aliyun-log-go-sdk/producer" "google.golang.org/protobuf/proto" "github.com/go-kratos/kratos/v2/log" ) type aliyunLog struct { producer *producer.Producer opts *options } func (a *aliyunLog) GetProducer() *producer.Producer { return a.producer } type options struct { accessKey string accessSecret string endpoint string project string logstore string } func defaultOptions() *options { return &options{ project: "projectName", logstore: "app", } } func WithEndpoint(endpoint string) Option { return func(alc *options) { alc.endpoint = endpoint } } func WithProject(project string) Option { return func(alc *options) { alc.project = project } } func WithLogstore(logstore string) Option { return func(alc *options) { alc.logstore = logstore } } func WithAccessKey(ak string) Option { return func(alc *options) { alc.accessKey = ak } } func WithAccessSecret(as string) Option { return func(alc *options) { alc.accessSecret = as } } type Option func(alc *options) func (a *aliyunLog) Close() error { return a.producer.Close(5000) } func (a *aliyunLog) Log(level log.Level, keyvals ...interface{}) error { contents := make([]*sls.LogContent, 0, len(keyvals)/2+1) contents = append(contents, &sls.LogContent{ Key: newString(level.Key()), Value: newString(level.String()), }) for i := 0; i < len(keyvals); i += 2 { contents = append(contents, &sls.LogContent{ Key: newString(toString(keyvals[i])), Value: newString(toString(keyvals[i+1])), }) } logInst := &sls.Log{ Time: proto.Uint32(uint32(time.Now().Unix())), Contents: contents, } return a.producer.SendLog(a.opts.project, a.opts.logstore, "", "", logInst) } // NewAliyunLog new a aliyun logger with options. func NewAliyunLog(options ...Option) Logger { opts := defaultOptions() for _, o := range options { o(opts) } producerConfig := producer.GetDefaultProducerConfig() producerConfig.Endpoint = opts.endpoint producerConfig.AccessKeyID = opts.accessKey producerConfig.AccessKeySecret = opts.accessSecret producerInst := producer.InitProducer(producerConfig) producerInst.Start() return &aliyunLog{ opts: opts, producer: producerInst, } } // newString string convert to *string func newString(s string) *string { return &s } // toString convert any type to string func toString(v interface{}) string { var key string if v == nil { return key } switch v := v.(type) { case float64: key = strconv.FormatFloat(v, 'f', -1, 64) case float32: key = strconv.FormatFloat(float64(v), 'f', -1, 32) case int: key = strconv.Itoa(v) case uint: key = strconv.FormatUint(uint64(v), 10) case int8: key = strconv.Itoa(int(v)) case uint8: key = strconv.FormatUint(uint64(v), 10) case int16: key = strconv.Itoa(int(v)) case uint16: key = strconv.FormatUint(uint64(v), 10) case int32: key = strconv.Itoa(int(v)) case uint32: key = strconv.FormatUint(uint64(v), 10) case int64: key = strconv.FormatInt(v, 10) case uint64: key = strconv.FormatUint(v, 10) case string: key = v case bool: key = strconv.FormatBool(v) case []byte: key = string(v) case fmt.Stringer: key = v.String() default: newValue, _ := json.Marshal(v) key = string(newValue) } return key }