improve handler feature for package glog

This commit is contained in:
John Guo
2022-04-29 14:13:54 +08:00
parent 7fcf7d31a0
commit 9ad9292321
4 changed files with 155 additions and 44 deletions

View File

@ -96,7 +96,7 @@ func (l *Logger) getFilePath(now time.Time) string {
}
// print prints `s` to defined writer, logging file or passed `std`.
func (l *Logger) print(ctx context.Context, level int, values ...interface{}) {
func (l *Logger) print(ctx context.Context, level int, stack string, values ...interface{}) {
// Lazy initialize for rotation feature.
// It uses atomic reading operation to enhance the performance checking.
// It here uses CAP for performance and concurrent safety.
@ -115,14 +115,26 @@ func (l *Logger) print(ctx context.Context, level int, values ...interface{}) {
var (
now = time.Now()
input = &HandlerInput{
Logger: l,
Buffer: bytes.NewBuffer(nil),
Time: now,
Color: defaultLevelColor[level],
Level: level,
handlerIndex: -1,
internalHandlerInfo: internalHandlerInfo{
index: -1,
},
Logger: l,
Buffer: bytes.NewBuffer(nil),
Time: now,
Color: defaultLevelColor[level],
Level: level,
Stack: stack,
}
)
// Logging handlers.
if len(l.config.Handlers) > 0 {
input.handlers = append(input.handlers, l.config.Handlers...)
} else if defaultHandler != nil {
input.handlers = []Handler{defaultHandler}
}
input.handlers = append(input.handlers, defaultPrintHandler)
if l.config.HeaderPrint {
// Time.
timeFormat := ""
@ -146,7 +158,7 @@ func (l *Logger) print(ctx context.Context, level int, values ...interface{}) {
}
// Level string.
input.LevelFormat = l.getLevelPrefixWithBrackets(level)
input.LevelFormat = l.GetLevelPrefix(level)
// Caller path and Fn name.
if l.config.Flags&(F_FILE_LONG|F_FILE_SHORT|F_CALLER_FN) > 0 {
@ -178,7 +190,7 @@ func (l *Logger) print(ctx context.Context, level int, values ...interface{}) {
// Tracing values.
spanCtx := trace.SpanContextFromContext(ctx)
if traceId := spanCtx.TraceID(); traceId.IsValid() {
input.CtxStr = traceId.String()
input.TraceId = traceId.String()
}
// Context values.
if len(l.config.CtxKeys) > 0 {
@ -195,9 +207,6 @@ func (l *Logger) print(ctx context.Context, level int, values ...interface{}) {
}
}
}
if input.CtxStr != "" {
input.CtxStr = "{" + input.CtxStr + "}"
}
}
var tempStr string
for _, v := range values {
@ -336,18 +345,17 @@ func (l *Logger) getFilePointer(ctx context.Context, path string) *gfpool.File {
// printStd prints content `s` without stack.
func (l *Logger) printStd(ctx context.Context, level int, value ...interface{}) {
l.print(ctx, level, value...)
l.print(ctx, level, "", value...)
}
// printStd prints content `s` with stack check.
func (l *Logger) printErr(ctx context.Context, level int, value ...interface{}) {
var stack string
if l.config.StStatus == 1 {
if s := l.GetStack(); s != "" {
value = append(value, "\nStack:\n"+s)
}
stack = l.GetStack()
}
// In matter of sequence, do not use stderr here, but use the same stdout.
l.print(ctx, level, value...)
l.print(ctx, level, stack, value...)
}
// format formats `values` using fmt.Sprintf.

View File

@ -17,24 +17,35 @@ type Handler func(ctx context.Context, in *HandlerInput)
// HandlerInput is the input parameter struct for logging Handler.
type HandlerInput struct {
Logger *Logger // Logger.
Buffer *bytes.Buffer // Buffer for logging content outputs.
Time time.Time // Logging time, which is the time that logging triggers.
TimeFormat string // Formatted time string, like "2016-01-09 12:00:00".
Color int // Using color, like COLOR_RED, COLOR_BLUE, etc. Eg: 34
Level int // Using level, like LEVEL_INFO, LEVEL_ERRO, etc. Eg: 256
LevelFormat string // Formatted level string, like "DEBU", "ERRO", etc. Eg: ERRO
CallerFunc string // The source function name that calls logging.
CallerPath string // The source file path and its line number that calls logging.
CtxStr string // The retrieved context value string from context. Usually a TraceId string.
Prefix string // Custom prefix string for logging content.
Content string // Content is the main logging content, containing error stack string produced by logger.
IsAsync bool // IsAsync marks it is in asynchronous logging.
handlerIndex int // Middleware handling index for internal usage.
internalHandlerInfo
Logger *Logger // Logger.
Buffer *bytes.Buffer // Buffer for logging content outputs.
Time time.Time // Logging time, which is the time that logging triggers.
TimeFormat string // Formatted time string, like "2016-01-09 12:00:00".
Color int // Using color, like COLOR_RED, COLOR_BLUE, etc. Eg: 34
Level int // Using level, like LEVEL_INFO, LEVEL_ERRO, etc. Eg: 256
LevelFormat string // Formatted level string, like "DEBU", "ERRO", etc. Eg: ERRO
CallerFunc string // The source function name that calls logging, only available if F_CALLER_FN set.
CallerPath string // The source file path and its line number that calls logging, only available if F_FILE_SHORT or F_FILE_LONG set.
CtxStr string // The retrieved context value string from context, only available if Config.CtxKeys configured.
TraceId string // Trace id, only available if tracing is enabled.
Prefix string // Custom prefix string for logging content.
Content string // Content is the main logging content without error stack string produced by logger.
Stack string // Stack string produced by logger, only available if Config.StStatus configured.
IsAsync bool // IsAsync marks it is in asynchronous logging.
}
type internalHandlerInfo struct {
index int // Middleware handling index for internal usage.
handlers []Handler // Handler array calling bu index.
}
// defaultHandler is the default handler for package.
var defaultHandler Handler = func(ctx context.Context, in *HandlerInput) {
var defaultHandler Handler
// defaultPrintHandler is a handler for logging content printing.
// This handler outputs logging content to file/stdout/write if any of them configured.
func defaultPrintHandler(ctx context.Context, in *HandlerInput) {
buffer := in.Logger.doDefaultPrint(ctx, in)
if in.Buffer.Len() == 0 {
in.Buffer = buffer
@ -46,18 +57,16 @@ func SetDefaultHandler(handler Handler) {
defaultHandler = handler
}
// HandlerJson is a handler for output logging content as a single json string.
func HandlerJson(ctx context.Context, in *HandlerInput) {
// GetDefaultHandler returns the default handler of package.
func GetDefaultHandler() Handler {
return defaultHandler
}
// Next calls the next logging handler in middleware way.
func (in *HandlerInput) Next(ctx context.Context) {
if len(in.Logger.config.Handlers)-1 > in.handlerIndex {
in.handlerIndex++
in.Logger.config.Handlers[in.handlerIndex](ctx, in)
} else if defaultHandler != nil {
defaultHandler(ctx, in)
in.index++
if in.index < len(in.handlers) {
in.handlers[in.index](ctx, in)
}
}
@ -76,19 +85,23 @@ func (in *HandlerInput) getDefaultBuffer(withColor bool) *bytes.Buffer {
buffer.WriteString(in.TimeFormat)
}
if in.LevelFormat != "" {
var levelStr = "[" + in.LevelFormat + "]"
if withColor {
in.addStringToBuffer(buffer, in.Logger.getColoredStr(
in.Logger.getColorByLevel(in.Level), in.LevelFormat,
in.Logger.getColorByLevel(in.Level), levelStr,
))
} else {
in.addStringToBuffer(buffer, in.LevelFormat)
in.addStringToBuffer(buffer, levelStr)
}
}
if in.Prefix != "" {
in.addStringToBuffer(buffer, in.Prefix)
}
if in.TraceId != "" {
in.addStringToBuffer(buffer, "{"+in.TraceId+"}")
}
if in.CtxStr != "" {
in.addStringToBuffer(buffer, in.CtxStr)
in.addStringToBuffer(buffer, "{"+in.CtxStr+"}")
}
if in.CallerFunc != "" {
in.addStringToBuffer(buffer, in.CallerFunc)
@ -97,7 +110,11 @@ func (in *HandlerInput) getDefaultBuffer(withColor bool) *bytes.Buffer {
in.addStringToBuffer(buffer, in.CallerPath)
}
if in.Content != "" {
in.addStringToBuffer(buffer, in.Content)
if in.Stack != "" {
in.addStringToBuffer(buffer, in.Content+"\nStack:\n"+in.Stack)
} else {
in.addStringToBuffer(buffer, in.Content)
}
}
// avoid a single space at the end of a line.
buffer.WriteString("\n")

View File

@ -0,0 +1,47 @@
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package glog
import (
"context"
"github.com/gogf/gf/v2/internal/json"
)
// HandlerOutputJson is the structure outputting logging content as single json.
type HandlerOutputJson struct {
Time string // Formatted time string, like "2016-01-09 12:00:00".
TraceId string // Trace id, only available if tracing is enabled.
CtxStr string // The retrieved context value string from context, only available if Config.CtxKeys configured.
Level string // Formatted level string, like "DEBU", "ERRO", etc. Eg: ERRO
CallerFunc string // The source function name that calls logging, only available if F_CALLER_FN set.
CallerPath string // The source file path and its line number that calls logging, only available if F_FILE_SHORT or F_FILE_LONG set.
Prefix string // Custom prefix string for logging content.
Content string // Content is the main logging content, containing error stack string produced by logger.
Stack string // Stack string produced by logger, only available if Config.StStatus configured.
}
// HandlerJson is a handler for output logging content as a single json string.
func HandlerJson(ctx context.Context, in *HandlerInput) {
output := HandlerOutputJson{
Time: in.TimeFormat,
TraceId: in.TraceId,
CtxStr: in.CtxStr,
Level: in.LevelFormat,
CallerFunc: in.CallerFunc,
CallerPath: in.CallerPath,
Prefix: in.Prefix,
Content: in.Content,
Stack: in.Stack,
}
jsonBytes, err := json.Marshal(output)
if err != nil {
panic(err)
}
in.Buffer.Write(jsonBytes)
in.Next(ctx)
}

View File

@ -71,3 +71,42 @@ func TestLogger_SetHandlers2(t *testing.T) {
t.Assert(gstr.Count(arrayForHandlerTest2.At(0), "1 2 3"), 1)
})
}
func TestLogger_SetHandlers_HandlerJson(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
w := bytes.NewBuffer(nil)
l := glog.NewWithWriter(w)
l.SetHandlers(glog.HandlerJson)
l.SetCtxKeys("Trace-Id", "Span-Id", "Test")
ctx := context.WithValue(context.Background(), "Trace-Id", "1234567890")
ctx = context.WithValue(ctx, "Span-Id", "abcdefg")
l.Debug(ctx, 1, 2, 3)
t.Assert(gstr.Count(w.String(), "1234567890"), 1)
t.Assert(gstr.Count(w.String(), "abcdefg"), 1)
t.Assert(gstr.Count(w.String(), `"1 2 3"`), 1)
t.Assert(gstr.Count(w.String(), `"DEBU"`), 1)
})
}
func Test_SetDefaultHandler(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
oldHandler := glog.GetDefaultHandler()
glog.SetDefaultHandler(func(ctx context.Context, in *glog.HandlerInput) {
glog.HandlerJson(ctx, in)
})
defer glog.SetDefaultHandler(oldHandler)
w := bytes.NewBuffer(nil)
l := glog.NewWithWriter(w)
l.SetCtxKeys("Trace-Id", "Span-Id", "Test")
ctx := context.WithValue(context.Background(), "Trace-Id", "1234567890")
ctx = context.WithValue(ctx, "Span-Id", "abcdefg")
l.Debug(ctx, 1, 2, 3)
t.Assert(gstr.Count(w.String(), "1234567890"), 1)
t.Assert(gstr.Count(w.String(), "abcdefg"), 1)
t.Assert(gstr.Count(w.String(), `"1 2 3"`), 1)
t.Assert(gstr.Count(w.String(), `"DEBU"`), 1)
})
}