- Created by 郭强, last modified on Jun 20, 2022
从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 // Logger. Ctx context.Context // Context. 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. Level int // Using level, like LEVEL_INFO, LEVEL_ERRO, etc. LevelFormat string // Formatted level string, like "DEBU", "ERRO", etc. 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. Prefix string // Custom prefix string for logging content. Content string // Content is the main logging content that passed by you. IsAsync bool // IsAsync marks it is in asynchronous logging. handlerIndex int // Middleware handling index for internal usage. }
开发者有两种方式修改默认的日志输出内容:
- 一种是直接修改
HandlerInput
中的属性信息,然后继续执行in.Next()
- 另一种自定义日志输出内容,将日志内容写入到
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"}
- No labels
5 Comments
fanglong
通过日志handler方式扩展了日志处理,变更日志输出格式
但是依然存在内部组件相关日志,并没有按照特定格式输出,怀疑内部组件相关日志并会回调自定义日志Handler
[v1] [2022-05-11 10:21:59.324] [INFO] [] logger [c07966ffa1ebed163a44f654c9723734] 获取用户信息失败 token 为空
[v1] [2022-05-11 10:21:59.326] [INFO] [] logger [c07966ffa1ebed163a44f654c9723734] 请求body &{{} 共享服务组 saas 共享服务组,可观测性,faas平台}
2022-05-11 10:21:59.338 {c07966ffa1ebed163a44f654c9723734} 200 "POST http 127.0.0.1:14529 /tenant HTTP/1.1" 0.006, 127.0.0.1, "", "ApiPOST Runtime +https://www.apipost.cn", 61, "Not Authorized", ""
fanglong
已找到问题,除了需要
荆义顺
fanghaiting
不知道为什么,我找不到组件通用的glog.HandlerJson
anben
有这个方法 我看过 , 不过发不了图片