diff --git a/core/logx/config.go b/core/logx/config.go index 87d02684..41f52981 100644 --- a/core/logx/config.go +++ b/core/logx/config.go @@ -1,5 +1,6 @@ package logx +// A LogConf is a logging config. type LogConf struct { ServiceName string `json:",optional"` Mode string `json:",default=console,options=console|file|volume"` diff --git a/core/logx/durationlogger.go b/core/logx/durationlogger.go index 45fe5524..d6832076 100644 --- a/core/logx/durationlogger.go +++ b/core/logx/durationlogger.go @@ -12,6 +12,7 @@ const durationCallerDepth = 3 type durationLogger logEntry +// WithDuration returns a Logger which logs the given duration. func WithDuration(d time.Duration) Logger { return &durationLogger{ Duration: timex.ReprOfDuration(d), diff --git a/core/logx/lesslogger.go b/core/logx/lesslogger.go index 15c878a6..94200ca6 100644 --- a/core/logx/lesslogger.go +++ b/core/logx/lesslogger.go @@ -1,21 +1,25 @@ package logx +// A LessLogger is a logger that control to log once during the given duration. type LessLogger struct { *limitedExecutor } +// NewLessLogger returns a LessLogger. func NewLessLogger(milliseconds int) *LessLogger { return &LessLogger{ limitedExecutor: newLimitedExecutor(milliseconds), } } +// Error logs v into error log or discard it if more than once in the given duration. func (logger *LessLogger) Error(v ...interface{}) { logger.logOrDiscard(func() { Error(v...) }) } +// Errorf logs v with format into error log or discard it if more than once in the given duration. func (logger *LessLogger) Errorf(format string, v ...interface{}) { logger.logOrDiscard(func() { Errorf(format, v...) diff --git a/core/logx/lesswriter.go b/core/logx/lesswriter.go index 4fec9abd..b85396a3 100644 --- a/core/logx/lesswriter.go +++ b/core/logx/lesswriter.go @@ -7,7 +7,7 @@ type lessWriter struct { writer io.Writer } -func NewLessWriter(writer io.Writer, milliseconds int) *lessWriter { +func newLessWriter(writer io.Writer, milliseconds int) *lessWriter { return &lessWriter{ limitedExecutor: newLimitedExecutor(milliseconds), writer: writer, diff --git a/core/logx/lesswriter_test.go b/core/logx/lesswriter_test.go index 2b72d011..799d64d1 100644 --- a/core/logx/lesswriter_test.go +++ b/core/logx/lesswriter_test.go @@ -9,7 +9,7 @@ import ( func TestLessWriter(t *testing.T) { var builder strings.Builder - w := NewLessWriter(&builder, 500) + w := newLessWriter(&builder, 500) for i := 0; i < 100; i++ { _, err := w.Write([]byte("hello")) assert.Nil(t, err) diff --git a/core/logx/logs.go b/core/logx/logs.go index 98b57b27..8ef42a10 100644 --- a/core/logx/logs.go +++ b/core/logx/logs.go @@ -57,8 +57,11 @@ const ( ) var ( - ErrLogPathNotSet = errors.New("log path must be set") - ErrLogNotInitialized = errors.New("log not initialized") + // ErrLogPathNotSet is an error that indicates the log path is not set. + ErrLogPathNotSet = errors.New("log path must be set") + // ErrLogNotInitialized is an error that log is not initialized. + ErrLogNotInitialized = errors.New("log not initialized") + // ErrLogServiceNameNotSet is an error that indicates that the service name is not set. ErrLogServiceNameNotSet = errors.New("log service name must be set") writeConsole bool @@ -89,8 +92,10 @@ type ( keepDays int } + // LogOption defines the method to customize the logging. LogOption func(options *logOptions) + // A Logger represents a logger. Logger interface { Error(...interface{}) Errorf(string, ...interface{}) @@ -102,6 +107,7 @@ type ( } ) +// MustSetup sets up logging with given config c. It exits on error. func MustSetup(c LogConf) { Must(SetUp(c)) } @@ -122,10 +128,12 @@ func SetUp(c LogConf) error { } } +// Alert alerts v in alert level, and the message is written to error log. func Alert(v string) { output(errorLog, levelAlert, v) } +// Close closes the logging. func Close() error { if writeConsole { return nil @@ -170,6 +178,7 @@ func Close() error { return nil } +// Disable disables the logging. func Disable() { once.Do(func() { atomic.StoreUint32(&initialized, 1) @@ -183,40 +192,49 @@ func Disable() { }) } +// Error writes v into error log. func Error(v ...interface{}) { ErrorCaller(1, v...) } +// Errorf writes v with format into error log. func Errorf(format string, v ...interface{}) { ErrorCallerf(1, format, v...) } +// ErrorCaller writes v with context into error log. func ErrorCaller(callDepth int, v ...interface{}) { errorSync(fmt.Sprint(v...), callDepth+callerInnerDepth) } +// ErrorCallerf writes v with context in format into error log. func ErrorCallerf(callDepth int, format string, v ...interface{}) { errorSync(fmt.Sprintf(format, v...), callDepth+callerInnerDepth) } +// ErrorStack writes v along with call stack into error log. func ErrorStack(v ...interface{}) { // there is newline in stack string stackSync(fmt.Sprint(v...)) } +// ErrorStackf writes v along with call stack in format into error log. func ErrorStackf(format string, v ...interface{}) { // there is newline in stack string stackSync(fmt.Sprintf(format, v...)) } +// Info writes v into access log. func Info(v ...interface{}) { infoSync(fmt.Sprint(v...)) } +// Infof writes v with format into access log. func Infof(format string, v ...interface{}) { infoSync(fmt.Sprintf(format, v...)) } +// Must checks if err is nil, otherwise logs the err and exits. func Must(err error) { if err != nil { msg := formatWithCaller(err.Error(), 3) @@ -226,46 +244,56 @@ func Must(err error) { } } +// SetLevel sets the logging level. It can be used to suppress some logs. func SetLevel(level uint32) { atomic.StoreUint32(&logLevel, level) } +// Severe writes v into severe log. func Severe(v ...interface{}) { severeSync(fmt.Sprint(v...)) } +// Severef writes v with format into severe log. func Severef(format string, v ...interface{}) { severeSync(fmt.Sprintf(format, v...)) } +// Slow writes v into slow log. func Slow(v ...interface{}) { slowSync(fmt.Sprint(v...)) } +// Slowf writes v with format into slow log. func Slowf(format string, v ...interface{}) { slowSync(fmt.Sprintf(format, v...)) } +// Stat writes v into stat log. func Stat(v ...interface{}) { statSync(fmt.Sprint(v...)) } +// Statf writes v with format into stat log. func Statf(format string, v ...interface{}) { statSync(fmt.Sprintf(format, v...)) } +// WithCooldownMillis customizes logging on writting call stack interval. func WithCooldownMillis(millis int) LogOption { return func(opts *logOptions) { opts.logStackCooldownMills = millis } } +// WithKeepDays customizes logging to keep logs with days. func WithKeepDays(days int) LogOption { return func(opts *logOptions) { opts.keepDays = days } } +// WithGzip customizes logging to automatically gzip the log files. func WithGzip() LogOption { return func(opts *logOptions) { opts.gzipEnabled = true @@ -382,7 +410,7 @@ func setupWithConsole(c LogConf) { errorLog = newLogWriter(log.New(os.Stderr, "", flags)) severeLog = newLogWriter(log.New(os.Stderr, "", flags)) slowLog = newLogWriter(log.New(os.Stderr, "", flags)) - stackLog = NewLessWriter(errorLog, options.logStackCooldownMills) + stackLog = newLessWriter(errorLog, options.logStackCooldownMills) statLog = infoLog }) } @@ -434,7 +462,7 @@ func setupWithFiles(c LogConf) error { return } - stackLog = NewLessWriter(errorLog, options.logStackCooldownMills) + stackLog = newLessWriter(errorLog, options.logStackCooldownMills) }) return err diff --git a/core/logx/rotatelogger.go b/core/logx/rotatelogger.go index ec2261a6..c615f070 100644 --- a/core/logx/rotatelogger.go +++ b/core/logx/rotatelogger.go @@ -26,9 +26,11 @@ const ( defaultFileMode = 0600 ) +// ErrLogFileClosed is an error that indicates the log file is already closed. var ErrLogFileClosed = errors.New("error: log file closed") type ( + // A RotateRule interface is used to define the log rotating rules. RotateRule interface { BackupFileName() string MarkRotated() @@ -36,6 +38,7 @@ type ( ShallRotate() bool } + // A RotateLogger is a Logger that can rotate log files with given rules. RotateLogger struct { filename string backup string @@ -50,6 +53,7 @@ type ( closeOnce sync.Once } + // A DailyRotateRule is a rule to daily rotate the log files. DailyRotateRule struct { rotatedTime string filename string @@ -59,6 +63,7 @@ type ( } ) +// DefaultRotateRule is a default log rotating rule, currently DailyRotateRule. func DefaultRotateRule(filename, delimiter string, days int, gzip bool) RotateRule { return &DailyRotateRule{ rotatedTime: getNowDate(), @@ -69,14 +74,17 @@ func DefaultRotateRule(filename, delimiter string, days int, gzip bool) RotateRu } } +// BackupFileName returns the backup filename on rotating. func (r *DailyRotateRule) BackupFileName() string { return fmt.Sprintf("%s%s%s", r.filename, r.delimiter, getNowDate()) } +// MarkRotated marks the rotated time of r to be the current time. func (r *DailyRotateRule) MarkRotated() { r.rotatedTime = getNowDate() } +// OutdatedFiles returns the files that exceeded the keeping days. func (r *DailyRotateRule) OutdatedFiles() []string { if r.days <= 0 { return nil @@ -113,10 +121,12 @@ func (r *DailyRotateRule) OutdatedFiles() []string { return outdates } +// ShallRotate checks if the file should be rotated. func (r *DailyRotateRule) ShallRotate() bool { return len(r.rotatedTime) > 0 && getNowDate() != r.rotatedTime } +// NewLogger returns a RotateLogger with given filename and rule, etc. func NewLogger(filename string, rule RotateRule, compress bool) (*RotateLogger, error) { l := &RotateLogger{ filename: filename, @@ -133,6 +143,7 @@ func NewLogger(filename string, rule RotateRule, compress bool) (*RotateLogger, return l, nil } +// Close closes l. func (l *RotateLogger) Close() error { var err error diff --git a/core/logx/tracelogger.go b/core/logx/tracelogger.go index b07a22ee..a40602bb 100644 --- a/core/logx/tracelogger.go +++ b/core/logx/tracelogger.go @@ -67,6 +67,7 @@ func (l *traceLogger) write(writer io.Writer, level, content string) { outputJson(writer, l) } +// WithContext sets ctx to log, for keeping tracing infomation. func WithContext(ctx context.Context) Logger { return &traceLogger{ ctx: ctx, diff --git a/core/logx/tracelogger_test.go b/core/logx/tracelogger_test.go index 37e7cfef..68735a12 100644 --- a/core/logx/tracelogger_test.go +++ b/core/logx/tracelogger_test.go @@ -13,8 +13,8 @@ import ( ) const ( - mockTraceId = "mock-trace-id" - mockSpanId = "mock-span-id" + mockTraceID = "mock-trace-id" + mockSpanID = "mock-span-id" ) var mock tracespec.Trace = new(mockTrace) @@ -24,8 +24,8 @@ func TestTraceLog(t *testing.T) { atomic.StoreUint32(&initialized, 1) ctx := context.WithValue(context.Background(), tracespec.TracingKey, mock) WithContext(ctx).(*traceLogger).write(&buf, levelInfo, testlog) - assert.True(t, strings.Contains(buf.String(), mockTraceId)) - assert.True(t, strings.Contains(buf.String(), mockSpanId)) + assert.True(t, strings.Contains(buf.String(), mockTraceID)) + assert.True(t, strings.Contains(buf.String(), mockSpanID)) } func TestTraceError(t *testing.T) { @@ -36,12 +36,12 @@ func TestTraceError(t *testing.T) { l := WithContext(ctx).(*traceLogger) SetLevel(InfoLevel) l.WithDuration(time.Second).Error(testlog) - assert.True(t, strings.Contains(buf.String(), mockTraceId)) - assert.True(t, strings.Contains(buf.String(), mockSpanId)) + assert.True(t, strings.Contains(buf.String(), mockTraceID)) + assert.True(t, strings.Contains(buf.String(), mockSpanID)) buf.Reset() l.WithDuration(time.Second).Errorf(testlog) - assert.True(t, strings.Contains(buf.String(), mockTraceId)) - assert.True(t, strings.Contains(buf.String(), mockSpanId)) + assert.True(t, strings.Contains(buf.String(), mockTraceID)) + assert.True(t, strings.Contains(buf.String(), mockSpanID)) } func TestTraceInfo(t *testing.T) { @@ -52,12 +52,12 @@ func TestTraceInfo(t *testing.T) { l := WithContext(ctx).(*traceLogger) SetLevel(InfoLevel) l.WithDuration(time.Second).Info(testlog) - assert.True(t, strings.Contains(buf.String(), mockTraceId)) - assert.True(t, strings.Contains(buf.String(), mockSpanId)) + assert.True(t, strings.Contains(buf.String(), mockTraceID)) + assert.True(t, strings.Contains(buf.String(), mockSpanID)) buf.Reset() l.WithDuration(time.Second).Infof(testlog) - assert.True(t, strings.Contains(buf.String(), mockTraceId)) - assert.True(t, strings.Contains(buf.String(), mockSpanId)) + assert.True(t, strings.Contains(buf.String(), mockTraceID)) + assert.True(t, strings.Contains(buf.String(), mockSpanID)) } func TestTraceSlow(t *testing.T) { @@ -68,12 +68,12 @@ func TestTraceSlow(t *testing.T) { l := WithContext(ctx).(*traceLogger) SetLevel(InfoLevel) l.WithDuration(time.Second).Slow(testlog) - assert.True(t, strings.Contains(buf.String(), mockTraceId)) - assert.True(t, strings.Contains(buf.String(), mockSpanId)) + assert.True(t, strings.Contains(buf.String(), mockTraceID)) + assert.True(t, strings.Contains(buf.String(), mockSpanID)) buf.Reset() l.WithDuration(time.Second).Slowf(testlog) - assert.True(t, strings.Contains(buf.String(), mockTraceId)) - assert.True(t, strings.Contains(buf.String(), mockSpanId)) + assert.True(t, strings.Contains(buf.String(), mockTraceID)) + assert.True(t, strings.Contains(buf.String(), mockSpanID)) } func TestTraceWithoutContext(t *testing.T) { @@ -83,22 +83,22 @@ func TestTraceWithoutContext(t *testing.T) { l := WithContext(context.Background()).(*traceLogger) SetLevel(InfoLevel) l.WithDuration(time.Second).Info(testlog) - assert.False(t, strings.Contains(buf.String(), mockTraceId)) - assert.False(t, strings.Contains(buf.String(), mockSpanId)) + assert.False(t, strings.Contains(buf.String(), mockTraceID)) + assert.False(t, strings.Contains(buf.String(), mockSpanID)) buf.Reset() l.WithDuration(time.Second).Infof(testlog) - assert.False(t, strings.Contains(buf.String(), mockTraceId)) - assert.False(t, strings.Contains(buf.String(), mockSpanId)) + assert.False(t, strings.Contains(buf.String(), mockTraceID)) + assert.False(t, strings.Contains(buf.String(), mockSpanID)) } type mockTrace struct{} func (t mockTrace) TraceId() string { - return mockTraceId + return mockTraceID } func (t mockTrace) SpanId() string { - return mockSpanId + return mockSpanID } func (t mockTrace) Finish() {