Uber's logging library Zap has a Config
struct, in which the log level is defined as follows:
type Config struct {
Level AtomicLevel `json:"level" yaml:"level"`
}
Where the type AtomicLevel
is a struct
that wraps Go's atomic.Int32
:
type AtomicLevel struct {
l *atomic.Int32
}
With this setup, how can Go's json.Unmarshall
successfully unmarshalls a string
, say, "debug" into the correct atomic int value, as the following example illustrates?
package main
import (
"encoding/json"
"go.uber.org/zap"
)
func main() {
// For some users, the presets offered by the NewProduction, NewDevelopment,
// and NewExample constructors won't be appropriate. For most of those
// users, the bundled Config struct offers the right balance of flexibility
// and convenience. (For more complex needs, see the AdvancedConfiguration
// example.)
//
// See the documentation for Config and zapcore.EncoderConfig for all the
// available options.
rawJSON := []byte(`{
"level": "debug",
"encoding": "json",
}`)
var cfg zap.Config
if err := json.Unmarshal(rawJSON, &cfg); err != nil {
panic(err)
}
logger, err := cfg.Build()
if err != nil {
panic(err)
}
defer logger.Sync()
logger.Info("logger construction succeeded")
}
AtomicLevel has an UnmarshalText method, which means it implements the encoding.TextUnmarshaler interface. When the JSON decoder sees a JSON string and the destination is a type that implements TextUnmarshaler, its UnmarshalText method is called to convert the string into an appropriate value (unless the type implements json.Unmarshaler
, in which case that takes precedence. AtomicLevel doesn't.)