options.go的源码学习
设置 logger 相关的属性,包括从命令行参数中解析标记
Dapr logger package中的 options.go 文件的源码学习,设置logger相关的属性,包括从命令行参数中解析标记。
默认属性
const (
defaultJSONOutput = false
defaultOutputLevel = "info"
undefinedAppID = ""
)
Options 结构体定义
Options 结构体,就三个字段:
// Options defines the sets of options for Dapr logging
type Options struct {
// appID is the unique id of Dapr Application
// 默认为空
appID string
// JSONFormatEnabled is the flag to enable JSON formatted log
// 默认为fasle
JSONFormatEnabled bool
// OutputLevel is the level of logging
// 默认为 info
OutputLevel string
}
设值方法
// SetOutputLevel sets the log output level
func (o *Options) SetOutputLevel(outputLevel string) error {
// 疑问:这里检查和赋值存在不一致:如果 outputLevel 中有大写字母
// TODO:改进一下
if toLogLevel(outputLevel) == UndefinedLevel {
return errors.Errorf("undefined Log Output Level: %s", outputLevel)
}
o.OutputLevel = outputLevel
return nil
}
// SetAppID sets Dapr ID
func (o *Options) SetAppID(id string) {
o.appID = id
}
疑问 :为什么字段和设置方法不统一?
- JSONFormatEnabled 是 public 字段,没有Set方法
- OutputLevel 是 public 字段,有 Set 方法,Set 方法做了输入值的检测。
- 问题来了:既然是 public 字段,那么绕开 Set 方法直接赋值岂不是就绕开了输入值检测的逻辑?
- appID 是 private 字段,有 Set 方法,而 Set 方法什么都没有做,只是简单赋值,那么为什么不直接用 public 字段呢?
检查发现:
- SetOutputLevel 在dapr/dapr 项目中没有任何人调用
默认构造
返回每个字段的默认值,没啥特殊:
// DefaultOptions returns default values of Options
func DefaultOptions() Options {
return Options{
JSONFormatEnabled: defaultJSONOutput,
appID: undefinedAppID,
OutputLevel: defaultOutputLevel,
}
}
备注:go 不像 java 可以在字段定义时直接赋值一个默认值,有时还真不方便。
从命令行标记中读取日志属性
在命令行参数中读取 log-level
和 log-as-json
两个标记并设置 OutputLevel 和 JSONFormatEnabled:
// AttachCmdFlags attaches log options to command flags
func (o *Options) AttachCmdFlags(
stringVar func(p *string, name string, value string, usage string),
boolVar func(p *bool, name string, value bool, usage string)) {
if stringVar != nil {
stringVar(
&o.OutputLevel,
"log-level",
defaultOutputLevel,
"Options are debug, info, warn, error, or fatal (default info)")
}
if boolVar != nil {
boolVar(
&o.JSONFormatEnabled,
"log-as-json",
defaultJSONOutput,
"print log as JSON (default false)")
}
}
备注:这大概就是 OutputLevel 和 JSONFormatEnabled 两个字段是 public 的原因?
这个方法会在每个二进制文件(runtime(也就是daprd) / injector / operator / placement / sentry) 的初始化代码中调用:
loggerOptions := logger.DefaultOptions()
loggerOptions.AttachCmdFlags(flag.StringVar, flag.BoolVar)
注意:这个时候 OutputLevel 的值是没有经过检查而直接设值的,绕开了 SetOutputLevel 方法的检查。
将属性应用到所有的logger
// ApplyOptionsToLoggers applys options to all registered loggers
func ApplyOptionsToLoggers(options *Options) error {
// 所有的 logger 指的是保存在全局 logger map 中所有 logger
internalLoggers := getLoggers()
// Apply formatting options first
for _, v := range internalLoggers {
v.EnableJSONOutput(options.JSONFormatEnabled)
if options.appID != undefinedAppID {
v.SetAppID(options.appID)
}
}
daprLogLevel := toLogLevel(options.OutputLevel)
if daprLogLevel == UndefinedLevel {
// 在这里做了 OutputLevel 值的有效性检查
return errors.Errorf("invalid value for --log-level: %s", options.OutputLevel)
}
for _, v := range internalLoggers {
v.SetOutputLevel(daprLogLevel)
}
return nil
}
TODO:OutputLevel 赋值有效性检查的地方现在发现有两个,其中一个还没有被使用。准备PR修订。
查了一下这个方法的确是在每个二进制文件(runtime(也就是daprd) / injector / operator / placement / sentry) 的初始化代码中调用:
loggerOptions := logger.DefaultOptions()
loggerOptions.AttachCmdFlags(flag.StringVar, flag.BoolVar)
......
// Apply options to all loggers
loggerOptions.SetAppID(*appID)
if err := logger.ApplyOptionsToLoggers(&loggerOptions); err != nil {
return nil, err
}
TODO: ApplyOptionsToLoggers这个方法名最好修改增加“来自命令行的options”语义,否则报错 “invalid value for –log-level“ 就会很奇怪。