Skip to content

Commit

Permalink
feat: update sdk log. (#125)
Browse files Browse the repository at this point in the history
* feat: update sdk log.

* update zap and test file.

* update log logic.

* update log.

* update const.

* update caller name.

* update log key.

* remove cn comment.
  • Loading branch information
mo3et authored Aug 19, 2024
1 parent 6b57840 commit 2e43916
Show file tree
Hide file tree
Showing 2 changed files with 213 additions and 24 deletions.
183 changes: 159 additions & 24 deletions log/zap.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,3 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package log

import (
Expand All @@ -35,26 +21,36 @@ type LogFormatter interface {
Format() any
}

const (
LevelFatal = iota
LevelPanic
LevelError
LevelWarn
LevelInfo
LevelDebug
LevelDebugWithSQL
)

var (
pkgLogger Logger
osStdout Logger
sp = string(filepath.Separator)
logLevelMap = map[int]zapcore.Level{
6: zapcore.DebugLevel,
5: zapcore.DebugLevel,
4: zapcore.InfoLevel,
3: zapcore.WarnLevel,
2: zapcore.ErrorLevel,
1: zapcore.FatalLevel,
0: zapcore.PanicLevel,
LevelDebugWithSQL: zapcore.DebugLevel,
LevelDebug: zapcore.DebugLevel,
LevelInfo: zapcore.InfoLevel,
LevelWarn: zapcore.WarnLevel,
LevelError: zapcore.ErrorLevel,
LevelPanic: zapcore.PanicLevel,
LevelFatal: zapcore.FatalLevel,
}
)

const callDepth = 2
const hoursPerDay = 24

// InitFromConfig initializes a Zap-based logger.
func InitFromConfig(
func InitLoggerFromConfig(
loggerPrefixName, moduleName string,
logLevel int,
isStdout bool,
Expand Down Expand Up @@ -93,6 +89,29 @@ func InitConsoleLogger(moduleName string,

}

func InitSDKLogger(
loggerPrefixName, moduleName string,
sdkType, platformName string,
logLevel int,
isStdout bool,
isJson bool,
logLocation string,
rotateCount uint,
rotationTime uint,
moduleVersion string,
isSimplify bool,
) error {
l, err := NewSDKZapLogger(loggerPrefixName, moduleName, sdkType, platformName, logLevel, isStdout, isJson, logLocation, rotateCount, rotationTime, moduleVersion, isSimplify)
if err != nil {
return err
}
pkgLogger = l.WithCallDepth(callDepth)
if isJson {
pkgLogger = pkgLogger.WithName(moduleName)
}
return nil
}

func ZDebug(ctx context.Context, msg string, keysAndValues ...any) {
if pkgLogger == nil {
return
Expand Down Expand Up @@ -135,6 +154,8 @@ type ZapLogger struct {
moduleVersion string
loggerPrefixName string
rotationTime time.Duration
sdkType string
platformName string
isSimplify bool
}

Expand All @@ -158,8 +179,13 @@ func NewZapLogger(
} else {
zapConfig.Encoding = "console"
}
zl := &ZapLogger{level: logLevelMap[logLevel], moduleName: moduleName, loggerPrefixName: loggerPrefixName,
rotationTime: time.Duration(rotationTime) * time.Hour, moduleVersion: moduleVersion, isSimplify: isSimplify}
zl := &ZapLogger{level: logLevelMap[logLevel],
moduleName: moduleName,
loggerPrefixName: loggerPrefixName,
rotationTime: time.Duration(rotationTime) * time.Hour,
moduleVersion: moduleVersion,
isSimplify: isSimplify,
}
opts, err := zl.cores(isStdout, isJson, logLocation, rotateCount)
if err != nil {
return nil, err
Expand Down Expand Up @@ -199,6 +225,48 @@ func NewConsoleZapLogger(
zl.zap = l.Sugar()
return zl, nil
}

func NewSDKZapLogger(
loggerPrefixName, moduleName string, sdkType, platformName string,
logLevel int,
isStdout bool,
isJson bool,
logLocation string,
rotateCount uint,
rotationTime uint,
moduleVersion string,
isSimplify bool,
) (*ZapLogger, error) {
zapConfig := zap.Config{
Level: zap.NewAtomicLevelAt(logLevelMap[logLevel]),
DisableStacktrace: true,
}
if isJson {
zapConfig.Encoding = "json"
} else {
zapConfig.Encoding = "console"
}
zl := &ZapLogger{level: logLevelMap[logLevel],
moduleName: moduleName,
loggerPrefixName: loggerPrefixName,
rotationTime: time.Duration(rotationTime) * time.Hour,
moduleVersion: moduleVersion,
sdkType: sdkType,
platformName: platformName,
isSimplify: isSimplify,
}
opts, err := zl.sdkCores(isStdout, isJson, logLocation, rotateCount)
if err != nil {
return nil, err
}
l, err := zapConfig.Build(opts)
if err != nil {
return nil, err
}
zl.zap = l.Sugar()
return zl, nil
}

func (l *ZapLogger) cores(isStdout bool, isJson bool, logLocation string, rotateCount uint) (zap.Option, error) {
c := zap.NewProductionEncoderConfig()
c.EncodeTime = l.timeEncoder
Expand Down Expand Up @@ -267,6 +335,45 @@ func (l *ZapLogger) consoleCores(outPut *os.File, isJson bool) (zap.Option, erro
}), nil
}

func (l *ZapLogger) sdkCores(isStdout bool, isJson bool, logLocation string, rotateCount uint) (zap.Option, error) {
c := zap.NewProductionEncoderConfig()
c.EncodeTime = l.timeEncoder
c.EncodeDuration = zapcore.SecondsDurationEncoder
c.MessageKey = "msg"
c.LevelKey = "level"
c.TimeKey = "time"
c.CallerKey = "caller"
c.NameKey = "logger"
var fileEncoder zapcore.Encoder
if isJson {
c.EncodeLevel = zapcore.CapitalLevelEncoder
fileEncoder = zapcore.NewJSONEncoder(c)
fileEncoder.AddInt("PID", os.Getpid())
fileEncoder.AddString("version", l.moduleVersion)
} else {
c.EncodeLevel = l.capitalColorLevelEncoder
c.EncodeCaller = l.platformCallerEncoder
fileEncoder = zapcore.NewConsoleEncoder(c)
}
fileEncoder = &alignEncoder{Encoder: fileEncoder}
writer, err := l.getWriter(logLocation, rotateCount)
if err != nil {
return nil, err
}
var cores []zapcore.Core
if logLocation != "" {
cores = []zapcore.Core{
zapcore.NewCore(fileEncoder, writer, zap.NewAtomicLevelAt(l.level)),
}
}
if isStdout {
cores = append(cores, zapcore.NewCore(fileEncoder, zapcore.Lock(os.Stdout), zap.NewAtomicLevelAt(l.level)))
}
return zap.WrapCore(func(c zapcore.Core) zapcore.Core {
return zapcore.NewTee(cores...)
}), nil
}

func (l *ZapLogger) customCallerEncoder(caller zapcore.EntryCaller, enc zapcore.PrimitiveArrayEncoder) {
fixedLength := 50
trimmedPath := caller.TrimmedPath()
Expand All @@ -275,6 +382,34 @@ func (l *ZapLogger) customCallerEncoder(caller zapcore.EntryCaller, enc zapcore.
enc.AppendString(s)
}

func (l *ZapLogger) platformCallerEncoder(caller zapcore.EntryCaller, enc zapcore.PrimitiveArrayEncoder) {
fixedLength := 50

sdkPlatform := fmt.Sprintf("[%s/%s]", l.sdkType, l.platformName)
sdkPlatformFormatted := stringutil.FormatString(sdkPlatform, fixedLength, true)

enc.AppendString(sdkPlatformFormatted)
}

func SDKLog(ctx context.Context, logLevel int, file string, line int, msg string, err error, keysAndValues []any) {
nativeCallerKey := "native_caller"
nativeCaller := fmt.Sprintf("[%s:%d]", file, line)

kv := []any{nativeCallerKey, nativeCaller}
kv = append(kv, keysAndValues...)

switch logLevel {
case LevelDebugWithSQL:
ZDebug(ctx, msg, kv...)
case LevelInfo:
ZInfo(ctx, msg, kv...)
case LevelWarn:
ZWarn(ctx, msg, err, kv...)
case LevelError:
ZError(ctx, msg, err, kv...)
}
}

func (l *ZapLogger) timeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
layout := "2006-01-02 15:04:05.000"
type appendTimeEncoder interface {
Expand Down
54 changes: 54 additions & 0 deletions log/zap_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package log

import (
"context"
"os"
"testing"

"github.com/stretchr/testify/assert"
"go.uber.org/zap"
)

// TestSDKLog tests the SDKLog function for proper log output including custom [file:line] information
func TestSDKLog(t *testing.T) {
err := InitSDKLogger(
"testLogger", // loggerPrefixName
"testModule", // moduleName
"TestSDK", // sdkType
"TestPlatform", // platformName
5, // logLevel (INFO)
true, // isStdout
true, // isJson
"./logs", // logLocation
5, // rotateCount
24, // rotationTime
"1.0.0", // moduleVersion
false, // isSimplify
)
assert.NoError(t, err)

// var buf bytes.Buffer
stdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w

logger := zap.NewExample()
defer logger.Sync()

SDKLog(context.Background(), 5, "cmd/abc.go", 666, "This is a test message", nil, []any{"key", "value"})
SDKLog(context.Background(), 4, "cmd/abc.go", 666, "This is a test message", nil, []any{"key", "value", "key", "key", 1})
SDKLog(context.Background(), 3, "cmd/abc.go", 666, "This is a test message", nil, []any{"key", "value"})
SDKLog(context.Background(), 2, "cmd/abc.go", 666, "This is a test message", nil, []any{"key", "value"})
ZDebug(context.TODO(), "msg")

w.Close()
out, _ := os.ReadFile(r.Name())
os.Stdout = stdout

_ = string(out)
// assert.Contains(t, output, "This is a test message")
// assert.Contains(t, output, "[TestSDK/TestPlatform]")
// assert.Contains(t, output, "[test_file.go:123]")
// assert.Contains(t, output, "key")
// assert.Contains(t, output, "value")
}

0 comments on commit 2e43916

Please sign in to comment.