Starting from version v2.0, the glog component provides a super powerful, customizable log processing Handler feature. Handler adopts a middleware design approach, allowing developers to register multiple processing Handlers for the log object, and also to override the default log component processing logic within the Handler.

Handler Method Definition

  1. // Handler is function handler for custom logging content outputs.
  2. type Handler func(ctx context.Context, in *HandlerInput)

As you can see, the second parameter is the log information to be processed by the log and is of pointer type, meaning that any attribute information of this parameter can be modified within the Handler, and the modified content will be passed to the next Handler.

Handler Parameter Definition

  1. // HandlerInput is the input parameter struct for logging Handler.
  2. type HandlerInput struct {
  3. Logger *Logger // Current Logger object.
  4. Buffer *bytes.Buffer // Buffer for logging content outputs.
  5. Time time.Time // Logging time, which is the time that logging triggers.
  6. TimeFormat string // Formatted time string, like "2016-01-09 12:00:00".
  7. Color int // Using color, like COLOR_RED, COLOR_BLUE, etc. Eg: 34
  8. Level int // Using level, like LEVEL_INFO, LEVEL_ERRO, etc. Eg: 256
  9. LevelFormat string // Formatted level string, like "DEBU", "ERRO", etc. Eg: ERRO
  10. CallerFunc string // The source function name that calls logging, only available if F_CALLER_FN set.
  11. CallerPath string // The source file path and its line number that calls logging, only available if F_FILE_SHORT or F_FILE_LONG set.
  12. CtxStr string // The retrieved context value string from context, only available if Config.CtxKeys configured.
  13. TraceId string // Trace id, only available if OpenTelemetry is enabled.
  14. Prefix string // Custom prefix string for logging content.
  15. Content string // Content is the main logging content without error stack string produced by logger.
  16. Values []any // The passed un-formatted values array to logger.
  17. Stack string // Stack string produced by logger, only available if Config.StStatus configured.
  18. IsAsync bool // IsAsync marks it is in asynchronous logging.
  19. }

Developers have two ways to customize log output content through Handler:

  • One way is to directly modify the attribute information in HandlerInput and then continue to execute in.Next(ctx). The default log output logic will print the attributes in HandlerInput as a string output.
  • Another way is to write log content into the Buffer buffer object. If the default log output logic finds that Buffer already contains log content, it will ignore the default log output logic.

Registering Handler to Logger Method

  1. // SetHandlers sets the logging handlers for current logger.
  2. func (l *Logger) SetHandlers(handlers ...Handler)

Usage Example

Let’s look at two examples to better understand how to use Handler.

Example 1: Convert Log Output to Json Format

In this example, we use a pre-middleware design to modify the log output format to JSON format through a custom Handler.

  1. package main
  2. import (
  3. "context"
  4. "encoding/json"
  5. "os"
  6. "github.com/gogf/gf/v2/frame/g"
  7. "github.com/gogf/gf/v2/os/glog"
  8. "github.com/gogf/gf/v2/text/gstr"
  9. )
  10. // JsonOutputsForLogger is for JSON marshaling in sequence.
  11. type JsonOutputsForLogger struct {
  12. Time string `json:"time"`
  13. Level string `json:"level"`
  14. Content string `json:"content"`
  15. }
  16. // LoggingJsonHandler is a example handler for logging JSON format content.
  17. var LoggingJsonHandler glog.Handler = func(ctx context.Context, in *glog.HandlerInput) {
  18. jsonForLogger := JsonOutputsForLogger{
  19. Time: in.TimeFormat,
  20. Level: gstr.Trim(in.LevelFormat, "[]"),
  21. Content: gstr.Trim(in.Content), // For version 2.7 and above, use in.ValuesContent()
  22. }
  23. jsonBytes, err := json.Marshal(jsonForLogger)
  24. if err != nil {
  25. _, _ = os.Stderr.WriteString(err.Error())
  26. return
  27. }
  28. in.Buffer.Write(jsonBytes)
  29. in.Buffer.WriteString("\n")
  30. in.Next(ctx)
  31. }
  32. func main() {
  33. g.Log().SetHandlers(LoggingJsonHandler)
  34. ctx := context.TODO()
  35. g.Log().Debug(ctx, "Debugging...")
  36. g.Log().Warning(ctx, "It is warning info")
  37. g.Log().Error(ctx, "Error occurs, please have a check")
  38. }

As you can see, we can control the output log content through the Buffer attribute in the Handler. If the Buffer content is empty after all pre-middleware Handlers process, continuing to execute Next will execute the default Handler logic of the log middleware. After executing the code of this example, the terminal output:

  1. {"time":"2021-12-31 11:03:25.438","level":"DEBU","content":"Debugging..."}
  2. {"time":"2021-12-31 11:03:25.438","level":"WARN","content":"It is warning info"}
  3. {"time":"2021-12-31 11:03:25.438","level":"ERRO","content":"Error occurs, please have a check \nStack:\n1. main.main\n C:/hailaz/test/main.go:42"}

Example 2: Output Content to a Third-party Log Collection Service

In this example, we use a post-middleware design to output a copy of the log content to a third-party graylog log collection service through a custom Handler without affecting the original log output processing.

Graylog is comparable to ELK and is a centralized log management solution that supports data collection, retrieval, and visualization dashboards. In this example, a simple third-party graylog client component is used.

  1. package main
  2. import (
  3. "context"
  4. "github.com/gogf/gf/v2/frame/g"
  5. "github.com/gogf/gf/v2/os/glog"
  6. gelf "github.com/robertkowalski/graylog-golang"
  7. )
  8. var grayLogClient = gelf.New(gelf.Config{
  9. GraylogPort: 80,
  10. GraylogHostname: "graylog-host.com",
  11. Connection: "wan",
  12. MaxChunkSizeWan: 42,
  13. MaxChunkSizeLan: 1337,
  14. })
  15. // LoggingGrayLogHandler is an example handler for logging content to remote GrayLog service.
  16. var LoggingGrayLogHandler glog.Handler = func(ctx context.Context, in *glog.HandlerInput) {
  17. in.Next(ctx)
  18. grayLogClient.Log(in.Buffer.String())
  19. }
  20. func main() {
  21. g.Log().SetHandlers(LoggingGrayLogHandler)
  22. ctx := context.TODO()
  23. g.Log().Debug(ctx, "Debugging...")
  24. g.Log().Warning(ctx, "It is warning info")
  25. g.Log().Error(ctx, "Error occurs, please have a check")
  26. glog.Print(ctx, "test log")
  27. }

Global Default Handler

By default, the log object does not have any Handlers set. From version v2.1, the framework provides a feature to set a global default Handler. The global default Handler will be effective for all log printing functions that use this log component and do not have custom Handlers. At the same time, the global default Handler will affect the log printing behavior of the log package methods.

Developers can use the following two methods to set and get the global default Handler.

  1. // SetDefaultHandler sets default handler for package.
  2. func SetDefaultHandler(handler Handler)
  3. // GetDefaultHandler returns the default handler of package.
  4. func GetDefaultHandler() Handler

It should be noted that this method of global package configuration is not thread-safe and often needs to be executed at the very top of the project startup logic.

Usage example, where we use JSON format for all project log outputs to ensure log content is structured and each log output is single-line for easy log collection during log collection period:

  1. package main
  2. import (
  3. "context"
  4. "encoding/json"
  5. "os"
  6. "github.com/gogf/gf/v2/frame/g"
  7. "github.com/gogf/gf/v2/os/glog"
  8. "github.com/gogf/gf/v2/text/gstr"
  9. )
  10. // JsonOutputsForLogger is for JSON marshaling in sequence.
  11. type JsonOutputsForLogger struct {
  12. Time string `json:"time"`
  13. Level string `json:"level"`
  14. Content string `json:"content"`
  15. }
  16. // LoggingJsonHandler is an example handler for logging JSON format content.
  17. var LoggingJsonHandler glog.Handler = func(ctx context.Context, in *glog.HandlerInput) {
  18. jsonForLogger := JsonOutputsForLogger{
  19. Time: in.TimeFormat,
  20. Level: gstr.Trim(in.LevelFormat, "[]"),
  21. Content: gstr.Trim(in.Content), // For version 2.7 and above, use in.ValuesContent()
  22. }
  23. jsonBytes, err := json.Marshal(jsonForLogger)
  24. if err != nil {
  25. _, _ = os.Stderr.WriteString(err.Error())
  26. return
  27. }
  28. in.Buffer.Write(jsonBytes)
  29. in.Buffer.WriteString("\n")
  30. in.Next(ctx)
  31. }
  32. func main() {
  33. ctx := context.TODO()
  34. glog.SetDefaultHandler(LoggingJsonHandler)
  35. g.Log().Debug(ctx, "Debugging...")
  36. glog.Warning(ctx, "It is warning info")
  37. glog.Error(ctx, "Error occurs, please have a check")
  38. }

After execution, the terminal output:

  1. {"time":"2022-06-20 10:51:50.235","level":"DEBU","content":"Debugging..."}
  2. {"time":"2022-06-20 10:51:50.235","level":"WARN","content":"It is warning info"}
  3. {"time":"2022-06-20 10:51:50.235","level":"ERRO","content":"Error occurs, please have a check"}

General Component Handler

The component provides a number of general-purpose log Handlers to facilitate developer use and improve development efficiency.

HandlerJson

This Handler can convert log content to Json format for printing. Usage example:

  1. package main
  2. import (
  3. "context"
  4. "github.com/gogf/gf/v2/frame/g"
  5. "github.com/gogf/gf/v2/os/glog"
  6. )
  7. func main() {
  8. ctx := context.TODO()
  9. glog.SetDefaultHandler(glog.HandlerJson)
  10. g.Log().Debug(ctx, "Debugging...")
  11. glog.Warning(ctx, "It is warning info")
  12. glog.Error(ctx, "Error occurs, please have a check")
  13. }

After execution, the terminal output:

  1. {"Time":"2022-06-20 20:04:04.725","Level":"DEBU","Content":"Debugging..."}
  2. {"Time":"2022-06-20 20:04:04.725","Level":"WARN","Content":"It is warning info"}
  3. {"Time":"2022-06-20 20:04:04.725","Level":"ERRO","Content":"Error occurs, please have a check","Stack":"1. main.main\n /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/.test/main.go:16\n"}

HandlerStructure

This Handler prints log content in a structured format, primarily to keep the log output content consistent with the slog format of the newer versions of Golang. It is important to note that the structured log printing feature needs to ensure that all log records use structured output for greater significance. Usage example:

  1. package main
  2. import (
  3. "context"
  4. "net"
  5. "github.com/gogf/gf/v2/frame/g"
  6. "github.com/gogf/gf/v2/os/glog"
  7. )
  8. func main() {
  9. ctx := context.TODO()
  10. glog.SetDefaultHandler(glog.HandlerStructure)
  11. g.Log().Info(ctx, "caution", "name", "admin")
  12. glog.Error(ctx, "oops", net.ErrClosed, "status", 500)
  13. }

After execution, the terminal output:

  1. Time="2023-11-23 21:00:08.671" Level=INFO Content=caution name=admin
  2. Time="2023-11-23 21:00:08.671" Level=ERRO oops="use of closed network connection" status=500 Stack="1. main.main\n /Users/txqiangguo/Workspace/gogf/gf/example/.test/main.go:16\n"