diff --git a/config.go b/config.go index 39cce0f..327b2e0 100644 --- a/config.go +++ b/config.go @@ -1,6 +1,7 @@ package logger import ( + "fmt" "os" "strings" "time" @@ -95,15 +96,31 @@ type Config struct { // File logger options FileLogger *FileLoggerConfig `mapstructure:"file_logger_options"` + + // UseLocalTime is used to set the encoder to use local time instead of UTC. + UseLocalTime bool `mapstructure:"use_local_time"` + + // ShowCaller is used to set the encoder to show the caller. + ShowCaller bool `mapstructure:"show_caller"` } // BuildLogger converts config into Zap configuration. func (cfg *Config) BuildLogger() (*zap.Logger, error) { var zCfg zap.Config + var callerKey = zapcore.OmitKey + var encodeCaller zapcore.CallerEncoder = nil + if cfg.ShowCaller { + callerKey = "caller" + encodeCaller = ColoredShortCallerEncoder + } switch Mode(strings.ToLower(string(cfg.Mode))) { case off, none: return zap.NewNop(), nil case production: + encodeTime := utcEpochTimeEncoder + if cfg.UseLocalTime { + encodeTime = localTimeEncoder + } zCfg = zap.Config{ Level: zap.NewAtomicLevelAt(zap.InfoLevel), Development: false, @@ -112,20 +129,24 @@ func (cfg *Config) BuildLogger() (*zap.Logger, error) { TimeKey: "ts", LevelKey: "level", NameKey: "logger", - CallerKey: zapcore.OmitKey, + CallerKey: callerKey, FunctionKey: zapcore.OmitKey, MessageKey: "msg", StacktraceKey: zapcore.OmitKey, LineEnding: cfg.LineEnding, EncodeLevel: zapcore.LowercaseLevelEncoder, - EncodeTime: utcEpochTimeEncoder, + EncodeTime: encodeTime, EncodeDuration: zapcore.SecondsDurationEncoder, - EncodeCaller: zapcore.ShortCallerEncoder, + EncodeCaller: encodeCaller, }, OutputPaths: []string{"stderr"}, ErrorOutputPaths: []string{"stderr"}, } case development: + encodeTime := utcISO8601TimeEncoder + if cfg.UseLocalTime { + encodeTime = localTimeEncoder + } zCfg = zap.Config{ Level: zap.NewAtomicLevelAt(zap.DebugLevel), Development: true, @@ -134,16 +155,16 @@ func (cfg *Config) BuildLogger() (*zap.Logger, error) { TimeKey: "ts", LevelKey: "level", NameKey: "logger", - CallerKey: zapcore.OmitKey, + CallerKey: callerKey, FunctionKey: zapcore.OmitKey, MessageKey: "msg", StacktraceKey: zapcore.OmitKey, LineEnding: cfg.LineEnding, EncodeLevel: ColoredLevelEncoder, EncodeName: ColoredNameEncoder, - EncodeTime: utcISO8601TimeEncoder, + EncodeTime: encodeTime, EncodeDuration: zapcore.StringDurationEncoder, - EncodeCaller: zapcore.ShortCallerEncoder, + EncodeCaller: encodeCaller, }, OutputPaths: []string{"stderr"}, ErrorOutputPaths: []string{"stderr"}, @@ -160,6 +181,10 @@ func (cfg *Config) BuildLogger() (*zap.Logger, error) { ErrorOutputPaths: []string{"stderr"}, } default: + encodeTime := utcISO8601TimeEncoder + if cfg.UseLocalTime { + encodeTime = localTimeEncoder + } zCfg = zap.Config{ Level: zap.NewAtomicLevelAt(zap.DebugLevel), Encoding: "console", @@ -167,16 +192,16 @@ func (cfg *Config) BuildLogger() (*zap.Logger, error) { TimeKey: "T", LevelKey: "L", NameKey: "N", - CallerKey: zapcore.OmitKey, + CallerKey: callerKey, FunctionKey: zapcore.OmitKey, MessageKey: "M", StacktraceKey: zapcore.OmitKey, LineEnding: cfg.LineEnding, EncodeLevel: ColoredLevelEncoder, EncodeName: ColoredNameEncoder, - EncodeTime: utcISO8601TimeEncoder, + EncodeTime: encodeTime, EncodeDuration: zapcore.StringDurationEncoder, - EncodeCaller: zapcore.ShortCallerEncoder, + EncodeCaller: encodeCaller, }, OutputPaths: []string{"stderr"}, ErrorOutputPaths: []string{"stderr"}, @@ -206,6 +231,11 @@ func (cfg *Config) BuildLogger() (*zap.Logger, error) { // init it // otherwise - return standard config if cfg.FileLogger != nil { + fileEncoderConfig := zCfg.EncoderConfig + if cfg.ShowCaller { + fileEncoderConfig.EncodeCaller = ShortCallerEncoderWithPadding + } + // init absent options cfg.FileLogger.InitDefaults() @@ -220,10 +250,13 @@ func (cfg *Config) BuildLogger() (*zap.Logger, error) { ) core := zapcore.NewCore( - zapcore.NewJSONEncoder(zCfg.EncoderConfig), + zapcore.NewJSONEncoder(fileEncoderConfig), w, zCfg.Level, ) + if cfg.ShowCaller { + return zap.New(core, zap.AddCaller()), nil + } return zap.New(core), nil } @@ -251,3 +284,15 @@ func utcISO8601TimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { func utcEpochTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { enc.AppendInt64(t.UTC().UnixNano()) } + +func localTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { + enc.AppendString(t.Local().Format("2006-01-02 15:04:05.000 ")) +} + +func ColoredShortCallerEncoder(caller zapcore.EntryCaller, enc zapcore.PrimitiveArrayEncoder) { + enc.AppendString(fmt.Sprintf("\x1b[35m%-30s\x1b[0m", caller.TrimmedPath())) +} + +func ShortCallerEncoderWithPadding(caller zapcore.EntryCaller, enc zapcore.PrimitiveArrayEncoder) { + enc.AppendString(fmt.Sprintf("%-30s", caller.TrimmedPath())) +} diff --git a/go.work.sum b/go.work.sum index 70b1cc6..a42c4bc 100644 --- a/go.work.sum +++ b/go.work.sum @@ -145,6 +145,8 @@ github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQs github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= @@ -169,6 +171,7 @@ github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI= github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.1 h1:I2qBYMChEhIjOgazfJmV3/mZM256btk6wkCDRmW7JYs= @@ -184,6 +187,7 @@ github.com/sagikazarmark/crypt v0.19.0 h1:WMyLTjHBo64UvNcWqpzY3pbZTYgnemZU8FBZig github.com/sagikazarmark/crypt v0.19.0/go.mod h1:c6vimRziqqERhtSe0MhIvzE1w54FrCHtrXb5NH/ja78= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= @@ -216,6 +220,8 @@ go.temporal.io/api v1.30.1 h1:73UCTi+8l+Qy3GdDypW2FB5rj995A3Pi0mXkSu/qedw= go.temporal.io/api v1.30.1/go.mod h1:xI9UdP3s07881dgWzG8idIBAnZq3/aop+O682EIDoT0= go.temporal.io/api v1.35.0 h1:+1o2zyBncLjnpjJt4FedKZMtuUav/LUgTB+mhQxx0zs= go.temporal.io/api v1.35.0/go.mod h1:OYkuupuCw6s/5TkcKHMb9EcIrOI+vTsbf/CGaprbzb0= +go.temporal.io/api v1.36.0 h1:WdntOw9m38lFvMdMXuOO+3BQ0R8HpVLgtk9+f+FwiDk= +go.temporal.io/api v1.36.0/go.mod h1:0nWIrFRVPlcrkopXqxir/UWOtz/NZCo+EE9IX4UwVxw= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= @@ -232,6 +238,8 @@ golang.org/x/oauth2 v0.19.0 h1:9+E/EZBCbTLNrbN35fHv/a/d/mOBatymz1zbtQrXpIg= golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8= golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457 h1:zf5N6UOrA487eEFacMePxjXAJctxKmyjKUsjA11Uzuk= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU= @@ -241,6 +249,8 @@ golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8= golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= @@ -270,6 +280,8 @@ google.golang.org/genproto/googleapis/api v0.0.0-20240325203815-454cdb8f5daa h1: google.golang.org/genproto/googleapis/api v0.0.0-20240325203815-454cdb8f5daa/go.mod h1:K4kfzHtI0kqWA79gecJarFtDn/Mls+GxQcg3Zox91Ac= google.golang.org/genproto/googleapis/api v0.0.0-20240624140628-dc46fd24d27d h1:Aqf0fiIdUQEj0Gn9mKFFXoQfTTEaNopWpfVyYADxiSg= google.golang.org/genproto/googleapis/api v0.0.0-20240624140628-dc46fd24d27d/go.mod h1:Od4k8V1LQSizPRUK4OzZ7TBE/20k+jPczUDAEyvn69Y= +google.golang.org/genproto/googleapis/api v0.0.0-20240722135656-d784300faade h1:WxZOF2yayUHpHSbUE6NMzumUzBxYc3YGwo0YHnbzsJY= +google.golang.org/genproto/googleapis/api v0.0.0-20240722135656-d784300faade/go.mod h1:mw8MG/Qz5wfgYr6VqVCiZcHe/GJEfI+oGGDCohaVgB0= google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97 h1:6GQBEOdGkX6MMTLT9V+TjtIRZCw9VPD5Z+yHY9wMgS0= @@ -278,6 +290,8 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20240325203815-454cdb8f5daa h1: google.golang.org/genproto/googleapis/rpc v0.0.0-20240325203815-454cdb8f5daa/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d h1:k3zyW3BYYR30e8v3x0bTDdE9vpYFjZHK+HcyqkrppWk= google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240722135656-d784300faade h1:oCRSWfwGXQsqlVdErcyTt4A93Y8fo0/9D4b1gnI++qo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240722135656-d784300faade/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/grpc v1.58.0 h1:32JY8YpPMSR45K+c3o6b8VL73V+rR8k+DeMIr4vRH8o= google.golang.org/grpc v1.58.0/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I= @@ -286,6 +300,8 @@ google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/tests/configs/.rr-file-logger.yaml b/tests/configs/.rr-file-logger.yaml index d7fb8a5..569b0cf 100644 --- a/tests/configs/.rr-file-logger.yaml +++ b/tests/configs/.rr-file-logger.yaml @@ -21,5 +21,7 @@ http: logs: mode: development level: debug + use_local_time: true + show_caller: true file_logger_options: log_output: "test.log" diff --git a/tests/logger_test.go b/tests/logger_test.go index 34eab36..e958cbb 100644 --- a/tests/logger_test.go +++ b/tests/logger_test.go @@ -6,6 +6,7 @@ import ( "net/http" "os" "os/signal" + "regexp" "strings" "sync" "syscall" @@ -337,6 +338,16 @@ func TestFileLogger(t *testing.T) { strings.Contains(string(f), "worker constructed") strings.Contains(string(f), "201 GET") + // when use_local_time=true, check local time format + matched, err := regexp.MatchString(`\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{3}`, string(f)) + assert.NoError(t, err) + assert.True(t, matched) + + // when show_caller=true, check caller info + callerMatched, err := regexp.MatchString(`\w+\.go:\d+`, string(f)) + assert.NoError(t, err) + assert.True(t, callerMatched) + _ = os.Remove("test.log") stopCh <- struct{}{}