- Created by 郭强, last modified on Nov 23, 2023
You are viewing an old version of this page. View the current version.
Compare with Current View Page History
Version 24 Next »
从v2.0
版本开始,glog
组件提供了超级强大的、可自定义日志处理的Handler
特性。Handler
采用了中间件设计方式,开发者可以为日志对象注册多个处理Handler
,也可以在Handler
中覆盖默认的日志组件处理逻辑。
相关定义
Handler
方法定义
// Handler is function handler for custom logging content outputs. type Handler func(ctx context.Context, in *HandlerInput)
可以看到第二个参数为日志处理的日志信息,并且为指针类型,意味着在Handler
中可以修改该参数的任意属性信息,并且修改后的内容将会传递给下一个Handler
。
Handler
参数定义
// HandlerInput is the input parameter struct for logging Handler. type HandlerInput struct { 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. }
开发者有两种方式通过Handler
自定义日志输出内容:
- 一种是直接修改
HandlerInput
中的属性信息,然后继续执行in.Next()
,默认的日志输出逻辑会将HandlerInput
中的属性打印为字符串输出。 - 另一种是将日志内容写入到
Buffer
缓冲对象中即可,默认的日志输出逻辑如果发现Buffer
已经存在日志内容将会忽略默认日志输出逻辑。
Handler
注册到Logger
方法
// SetHandlers sets the logging handlers for current logger. func (l *Logger) SetHandlers(handlers ...Handler)
使用示例
我们来看两个示例便于更快速了解Handler
的使用。
示例1. 将日志输出转换为Json
格式输出
在本示例中,我们采用了前置中间件的设计,通过自定义Handler
将日志内容输出格式修改为了JSON
格式。
package main import ( "context" "encoding/json" "os" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/glog" "github.com/gogf/gf/v2/text/gstr" ) // JsonOutputsForLogger is for JSON marshaling in sequence. type JsonOutputsForLogger struct { Time string `json:"time"` Level string `json:"level"` Content string `json:"content"` } // LoggingJsonHandler is a example handler for logging JSON format content. var LoggingJsonHandler glog.Handler = func(ctx context.Context, in *glog.HandlerInput) { jsonForLogger := JsonOutputsForLogger{ Time: in.TimeFormat, Level: gstr.Trim(in.LevelFormat, "[]"), Content: gstr.Trim(in.Content), } jsonBytes, err := json.Marshal(jsonForLogger) if err != nil { _, _ = os.Stderr.WriteString(err.Error()) return } in.Buffer.Write(jsonBytes) in.Buffer.WriteString("\n") in.Next(ctx) } func main() { g.Log().SetHandlers(LoggingJsonHandler) ctx := context.TODO() g.Log().Debug(ctx, "Debugging...") g.Log().Warning(ctx, "It is warning info") g.Log().Error(ctx, "Error occurs, please have a check") }
可以看到,我们可以在Handler
中通过Buffer
属性操作来控制输出的日志内容。如果在所有的前置中间件Handler
处理后Buffer
内容为空,那么继续Next
执行后将会执行日志中间件默认的Handler
逻辑。执行本示例的代码后,终端输出:
{"time":"2021-12-31 11:03:25.438","level":"DEBU","content":"Debugging..."} {"time":"2021-12-31 11:03:25.438","level":"WARN","content":"It is warning info"} {"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"}
示例2. 将内容输出到第三方日志搜集服务中
在本示例中,我们采用了后置中间件的设计,通过自定义Handler
将日志内容输出一份到第三方graylog
日志搜集服务中,并且不影响原有的日志输出处理。
Graylog
是与ELK
可以相提并论的一款集中式日志管理方案,支持数据收集、检索、可视化Dashboard
。在本示例中使用到了一个简单的第三方graylog
客户端组件。
package main import ( "context" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/glog" gelf "github.com/robertkowalski/graylog-golang" ) var grayLogClient = gelf.New(gelf.Config{ GraylogPort: 80, GraylogHostname: "graylog-host.com", Connection: "wan", MaxChunkSizeWan: 42, MaxChunkSizeLan: 1337, }) // LoggingGrayLogHandler is an example handler for logging content to remote GrayLog service. var LoggingGrayLogHandler glog.Handler = func(ctx context.Context, in *glog.HandlerInput) { in.Next() grayLogClient.Log(in.Buffer.String()) } func main() { g.Log().SetHandlers(LoggingGrayLogHandler) ctx := context.TODO() g.Log().Debug(ctx, "Debugging...") g.Log().Warning(ctx, "It is warning info") g.Log().Error(ctx, "Error occurs, please have a check") glog.Print(ctx, "test log") }
全局默认Handler
日志对象默认是没有设置任何的Handler
,从v2.1
版本开始,框架提供了可以设置全局默认Handler
的功能特性。全局默认Handler
将对所有的使用该日志组件,并且没有自定义Handler
的日志打印功能生效。同时,全局默认Handler
将会影响日志包方法的日志打印行为。
开发者可以通过以下两个方法来设置和获取全局默认的Handler
。
// SetDefaultHandler sets default handler for package. func SetDefaultHandler(handler Handler) // GetDefaultHandler returns the default handler of package. func GetDefaultHandler() Handler
需要注意,这种全局包配置的方法不是并发安全的,并且往往需要在项目启动逻辑最顶部执行。
使用示例,我们将项目所有的日志输出均采用JSON
格式输出,以保证日志内容结构化并且每次日志输出都是单行,方便日志采集期采集日志:
package main import ( "context" "encoding/json" "os" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/glog" "github.com/gogf/gf/v2/text/gstr" ) // JsonOutputsForLogger is for JSON marshaling in sequence. type JsonOutputsForLogger struct { Time string `json:"time"` Level string `json:"level"` Content string `json:"content"` } // LoggingJsonHandler is a example handler for logging JSON format content. var LoggingJsonHandler glog.Handler = func(ctx context.Context, in *glog.HandlerInput) { jsonForLogger := JsonOutputsForLogger{ Time: in.TimeFormat, Level: gstr.Trim(in.LevelFormat, "[]"), Content: gstr.Trim(in.Content), } jsonBytes, err := json.Marshal(jsonForLogger) if err != nil { _, _ = os.Stderr.WriteString(err.Error()) return } in.Buffer.Write(jsonBytes) in.Buffer.WriteString("\n") in.Next(ctx) } func main() { ctx := context.TODO() glog.SetDefaultHandler(LoggingJsonHandler) g.Log().Debug(ctx, "Debugging...") glog.Warning(ctx, "It is warning info") glog.Error(ctx, "Error occurs, please have a check") }
执行后,终端输出:
{"time":"2022-06-20 10:51:50.235","level":"DEBU","content":"Debugging..."} {"time":"2022-06-20 10:51:50.235","level":"WARN","content":"It is warning info"} {"time":"2022-06-20 10:51:50.235","level":"ERRO","content":"Error occurs, please have a check"}
组件通用Handler
组件提供了一些通用性的日志Handler
,方便开发者使用,提高开发效率。
HandlerJson
该Handler
可以将日志内容转换为Json
格式打印。使用示例:
package main import ( "context" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/glog" ) func main() { ctx := context.TODO() glog.SetDefaultHandler(glog.HandlerJson) g.Log().Debug(ctx, "Debugging...") glog.Warning(ctx, "It is warning info") glog.Error(ctx, "Error occurs, please have a check") }
执行后,终端输出:
{"Time":"2022-06-20 20:04:04.725","Level":"DEBU","Content":"Debugging..."} {"Time":"2022-06-20 20:04:04.725","Level":"WARN","Content":"It is warning info"} {"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
该Handler
可以将日志内容转换为结构化格式打印,主要是为了和Golang
新版本的slog
日志输出内容保持一致。使用示例:
package main import ( "context" "net" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/glog" ) func main() { ctx := context.TODO() glog.SetDefaultHandler(glog.HandlerStructure) g.Log().Info(ctx, "caution", "name", "admin") glog.Error(ctx, "oops", net.ErrClosed, "status", 500) }
执行后,终端输出:
Time="2023-11-23 21:00:08.671" Level=INFO Content=caution name=admin 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"
- No labels