diff --git a/contrib/rpc/grpcx/grpcx_unit_z_grpc_server_config_test.go b/contrib/rpc/grpcx/grpcx_unit_z_grpc_server_config_test.go index fd4f7c27816..9a1baaf8e5f 100644 --- a/contrib/rpc/grpcx/grpcx_unit_z_grpc_server_config_test.go +++ b/contrib/rpc/grpcx/grpcx_unit_z_grpc_server_config_test.go @@ -94,6 +94,7 @@ func Test_Grpcx_Grpc_Server_Config_Logger(t *testing.T) { logFilePath = fmt.Sprintf("/tmp/log/%s.log", gtime.Now().Format("Y-m-d")) logFileContent = gfile.GetContents(logFilePath) ) + defer gfile.Remove(logFilePath) t.Assert(gfile.Exists(logFilePath), true) t.Assert(gstr.Contains(logFileContent, "TestLogger "), true) }) diff --git a/os/glog/glog_logger_handler.go b/os/glog/glog_logger_handler.go index e2d931592c3..bf4c105ab06 100644 --- a/os/glog/glog_logger_handler.go +++ b/os/glog/glog_logger_handler.go @@ -18,24 +18,69 @@ import ( type Handler func(ctx context.Context, in *HandlerInput) // HandlerInput is the input parameter struct for logging Handler. +// +// The logging content is consisted in: +// TimeFormat [LevelFormat] {TraceId} {CtxStr} Prefix CallerFunc CallerPath Content Values Stack +// +// The header in the logging content is: +// TimeFormat [LevelFormat] {TraceId} {CtxStr} Prefix CallerFunc CallerPath type HandlerInput struct { internalHandlerInfo - Logger *Logger // Current Logger object. - 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 OpenTelemetry 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. - Values []any // The passed un-formatted values array to logger. - Stack string // Stack string produced by logger, only available if Config.StStatus configured. - IsAsync bool // IsAsync marks it is in asynchronous logging. + + // Current Logger object. + Logger *Logger + + // Buffer for logging content outputs. + Buffer *bytes.Buffer + + // (ReadOnly) Logging time, which is the time that logging triggers. + Time time.Time + + // Formatted time string for output, like "2016-01-09 12:00:00". + TimeFormat string + + // (ReadOnly) Using color constant value, like COLOR_RED, COLOR_BLUE, etc. + // Example: 34 + Color int + + // (ReadOnly) Using level, like LEVEL_INFO, LEVEL_ERRO, etc. + // Example: 256 + Level int + + // Formatted level string for output, like "DEBU", "ERRO", etc. + // Example: ERRO + LevelFormat string + + // The source function name that calls logging, only available if F_CALLER_FN set. + CallerFunc string + + // The source file path and its line number that calls logging, + // only available if F_FILE_SHORT or F_FILE_LONG set. + CallerPath string + + // The retrieved context value string from context, only available if Config.CtxKeys configured. + // It's empty if no Config.CtxKeys configured. + CtxStr string + + // Trace id, only available if OpenTelemetry is enabled, or else it's an empty string. + TraceId string + + // Custom prefix string in logging content header part. + // Note that, it takes no effect if HeaderPrint is disabled. + Prefix string + + // Custom logging content for logging. + Content string + + // The passed un-formatted values array to logger. + Values []any + + // Stack string produced by logger, only available if Config.StStatus configured. + // Note that there are usually multiple lines in stack content. + Stack string + + // IsAsync marks it is in asynchronous logging. + IsAsync bool } type internalHandlerInfo struct { @@ -82,6 +127,34 @@ func (in *HandlerInput) String(withColor ...bool) string { return in.getDefaultBuffer(formatWithColor).String() } +// ValuesContent converts and returns values as string content. +func (in *HandlerInput) ValuesContent() string { + var ( + buffer = bytes.NewBuffer(nil) + valueContent string + ) + for _, v := range in.Values { + valueContent = gconv.String(v) + if len(valueContent) == 0 { + continue + } + if buffer.Len() == 0 { + buffer.WriteString(valueContent) + continue + } + if buffer.Bytes()[buffer.Len()-1] != '\n' { + buffer.WriteString(" " + valueContent) + continue + } + // Remove one blank line(\n\n). + if valueContent[0] == '\n' { + valueContent = valueContent[1:] + } + buffer.WriteString(valueContent) + } + return buffer.String() +} + func (in *HandlerInput) getDefaultBuffer(withColor bool) *bytes.Buffer { buffer := bytes.NewBuffer(nil) if in.Logger.config.HeaderPrint { @@ -121,26 +194,8 @@ func (in *HandlerInput) getDefaultBuffer(withColor bool) *bytes.Buffer { in.addStringToBuffer(buffer, in.Content) } - // Convert values string content. - var valueContent string - for _, v := range in.Values { - valueContent = gconv.String(v) - if len(valueContent) == 0 { - continue - } - if buffer.Len() > 0 { - if buffer.Bytes()[buffer.Len()-1] == '\n' { - // Remove one blank line(\n\n). - if valueContent[0] == '\n' { - valueContent = valueContent[1:] - } - buffer.WriteString(valueContent) - } else { - buffer.WriteString(" " + valueContent) - } - } else { - buffer.WriteString(valueContent) - } + if len(in.Values) > 0 { + in.addStringToBuffer(buffer, in.ValuesContent()) } if in.Stack != "" { diff --git a/os/glog/glog_logger_handler_json.go b/os/glog/glog_logger_handler_json.go index 3ef2c400733..be5d6cdab36 100644 --- a/os/glog/glog_logger_handler_json.go +++ b/os/glog/glog_logger_handler_json.go @@ -10,7 +10,6 @@ import ( "context" "github.com/gogf/gf/v2/internal/json" - "github.com/gogf/gf/v2/util/gconv" ) // HandlerOutputJson is the structure outputting logging content as single json. @@ -39,26 +38,11 @@ func HandlerJson(ctx context.Context, in *HandlerInput) { Content: in.Content, Stack: in.Stack, } - // Convert values string content. - var valueContent string - for _, v := range in.Values { - valueContent = gconv.String(v) - if len(valueContent) == 0 { - continue - } - if len(output.Content) > 0 { - if output.Content[len(output.Content)-1] == '\n' { - // Remove one blank line(\n\n). - if valueContent[0] == '\n' { - valueContent = valueContent[1:] - } - output.Content += valueContent - } else { - output.Content += " " + valueContent - } - } else { - output.Content += valueContent + if len(in.Values) > 0 { + if output.Content != "" { + output.Content += " " } + output.Content += in.ValuesContent() } // Output json content. jsonBytes, err := json.Marshal(output) diff --git a/os/glog/glog_z_unit_logger_handler_test.go b/os/glog/glog_z_unit_logger_handler_test.go index 7bbec9559e8..86dcf20e687 100644 --- a/os/glog/glog_z_unit_logger_handler_test.go +++ b/os/glog/glog_z_unit_logger_handler_test.go @@ -82,10 +82,9 @@ func TestLogger_SetHandlers_HandlerJson(t *testing.T) { 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) + t.Assert(gstr.Count(w.String(), `"CtxStr":"1234567890, abcdefg"`), 1) + t.Assert(gstr.Count(w.String(), `"Content":"1 2 3"`), 1) + t.Assert(gstr.Count(w.String(), `"Level":"DEBU"`), 1) }) }