mirror of
https://gitee.com/johng/gf
synced 2026-06-07 02:12:11 +08:00
improve handler feature for package glog
This commit is contained in:
@ -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.
|
||||
|
||||
@ -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")
|
||||
|
||||
47
os/glog/glog_logger_handler_json.go
Normal file
47
os/glog/glog_logger_handler_json.go
Normal 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)
|
||||
}
|
||||
@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user