viper--easy-to-use Golang configuration library

Source: Internet
Author: User
Tags control characters etcd

Objective


This article is mainly for the reader to introduce a handy Golang configuration library viper

Body

Features of the Viper

Viper supports the following features:
1. Support the configuration of Yaml, Json, TOML, HCL and other formats
2. Can extract configuration from file, IO, environment variable, command line
3. Support for automatic conversion of type resolution
4. The configuration can be read remotely from the ETCD

Sample code

Define a type:

type config struct {    v  *viper.Viper;}

YAML configuration file for testing Config.yaml

TimeStamp: "2018-07-16 10:23:19"Author: "WZP"PassWd: "Hello"Information:   Name: "Harry"   Age: "37"   Alise:   - "Lion"   - "NK"   - "KaQS"   Image: "/path/header.rpg"   Public: falseFavorite:  Sport:  - "swimming"  - "football"  Music:  - "zui xuan min zu feng"  LuckyNumber: 99


Read YAML configuration file

func LoadConfigFromYaml (c *config) error  {    c.v = viper.New();    //设置配置文件的名字    c.v.SetConfigName("config")    //添加配置文件所在的路径,注意在Linux环境下%GOPATH要替换为$GOPATH    c.v.AddConfigPath("%GOPATH/src/")    c.v.AddConfigPath("./")    //设置配置文件类型    c.v.SetConfigType("yaml");    if err := c.v.ReadInConfig(); err != nil{        return  err;    }    log.Printf("age: %s, name: %s \n", c.v.Get("information.age"), c.v.Get("information.name"));    return nil;}

Note: If you do not need to Addconfigpath to specify the path, it will be in the program execution directory to find Config.yaml

Reading the configuration from IO

//由IO读取配置func ReadConfigFormIo(c *config) error {    c.v = viper.New()    if f, err := os.Open("config.yaml"); err != nil{        log.Printf("filure: %s", err.Error());        return err;    }else {        confLength, _ :=f.Seek(0,2);        //注意,通常写c++的习惯害怕读取字符串的时候越界,都会多留出一个NULL在末尾,但是在这里不行,会报出如下错误:        //While parsing config: yaml: control characters are not allowed        //错误参考网址:https://stackoverflow.com/questions/33717799/go-yaml-control-characters-are-not-allowed-error        configData := make([]byte, confLength);        f.Seek(0, 0);        f.Read(configData);        log.Printf("%s\n", string(configData))        c.v.SetConfigType("yaml");        if err := c.v.ReadConfig(bytes.NewBuffer(configData)); err != nil{            log.Fatalf(err.Error());        }    }    log.Printf("age: %s, name: %s \n", c.v.Get("information.age"), c.v.Get("information.name"));    return nil;}

The code above is to import the data from the configuration file into Io and then read from IO


read configuration from environment variables

Read local environment variable func envconfigprefix (c *config) error {C.V = viper.    New (); Bindenv ($1,$2)///If only one parameter is passed, the specified environment variable will be fetched, and if the prefix is set, the prefix will be automatically complete _$1/////If passing two parameters will not complete the prefix, directly get the second parameter in the environment variable passed to the $ OS.    Setenv ("Log_level", "INFO"); if nil = = C.v.get ("Log_level") {LOG.    Printf ("Log_level is nil");    }else {return errornotmacth;    }//must be bound to obtain c.v.bindenv ("Log_level"); Log. Printf ("Log_level is%s", Os.    Getenv ("Log_level"));    All environment variables are obtained, and if a prefix is set, the prefix name c.v.automaticenv () is automatically complete; environment variable prefix casing does not differentiate between OS.    Setenv ("Dev_addones", "none"); Log.    Printf ("Dev_addones:%s", C.v.get ("Dev_addones"));    Setenvprefix will set the prefix name of an environment variable c.v.setenvprefix ("DEV"); Os.    Setenv ("Dev_Mode", "true"); This will automatically complete the prefix, actually to get the dev_dev_mode if nil = = C.v.get ("Dev_Mode") {log.    Printf ("Dev_Mode is nil");    }else {return errornotmacth;    }//At this time we directly specify the corresponding environment variable loglevel, then will not go to complement the full prefix c.v.bindenv ("loglevel", "Log_level"); Log. Printf ("Log_level:%s", C.V.    Get ("LogLevel")); Return nil}

Setenvprefix and Automaticenv, bindenv collocation is very convenient, for example, we put the current program environment variables are set to Xx_, so as to facilitate our management, but also avoid conflicts with other environment variables, and in the reading time is very convenient to read.

Convenient replacement character

func EnvCongiReplacer(c *config, setPerfix bool) error {    c.v = viper.New();    c.v.AutomaticEnv();    c.v.SetEnvKeyReplacer(strings.NewReplacer(".","_"));    os.Setenv("API_VERSION","v0.1.0");    //Replacer和prefix一起使用可能会冲突,比如我下面的例子    //因为会自动补全前缀最终由获取API_VERSION变成API_API_VERSION    if setPerfix{ c.v.SetEnvPrefix("api");}    if s := c.v.Get("api.version"); s==nil{        return ErrorNoxExistKey    }else {        log.Printf("%s", c.v.Get("api.version"));    }    return nil;}

Sometimes we need to replace some of the characters in the key to convert to the corresponding environment, such as the example will '. ' Replace with ' _ ', by getting api.version into api_version, but one thing to note, Setenvprefix and setenvkeyreplacer may be confused when used together.

Alias function

//设置重载 和别名func SetAndAliases(c *config) error {    c.v = viper.New();    c.v.Set("Name","wzp");    c.v.RegisterAlias("id","Name");    c.v.Set("id","Mr.Wang");    //我们可以发现当别名对应的值修改之后,原本的key也发生变化    log.Printf("id %s, name %s",c.v.Get("id"),c.v.Get("name") );    return nil;}

We can set the alias for the key, and when the value of the alias is reset, the value corresponding to the original key will also change.

'

Serialization and deserialization

Type favorite struct {Sports []string;    Music []string; Luckynumber int;}    Type information struct {Name string;    Age int;    Alise []string;    Image string; Public Bool}type yamlconfig struct {TimeStamp string Author string PassWd string information information F Avorite favorite;}    Resolves the configuration to a struct object func umshalstruct (c *config) error {Loadconfigfromyaml (c); var cf yamlconfig if err: = C.v.unmarshal (&CF);    Err! = nil{return err; } return nil;} Func yamlstringsettings (c *config) string {C.V = viper.    New ();    C.v.set ("name", "Wzp");    C.v.set ("Age", 18); C.v.set ("Aliase", []string{"one", "one", "one", "three"}) CF: = C.v.allsettings () bs, err: = Yaml. Marshal (CF) if err! = Nil {log.    Fatalf ("Unable to marshal config to YAML:%v", Err)} return string (BS)}func jsonstringsettings (c *config) string { C.V = viper.    New ();    C.v.set ("name", "Wzp");    C.v.set ("Age", 18); C.v.set ("Aliase", []string{"One "," one "," three "}) CF: = C.v.allsettings () bs, err: = json. Marshal (CF) if err! = Nil {log. Fatalf ("Unable to marshal config to YAML:%v", Err)} return string (BS)}

Super affordable a feature that directly deserializes the configuration into a structure, the cool crooked have wood? You can also serialize the settings directly to the type we want: YAML, JSON, and so on.

Read the configuration from command line

func main()  {    flag.String("mode","RUN","please input the mode: RUN or DEBUG");    pflag.Int("port",1080,"please input the listen port");    pflag.String("ip","127.0.0.1","please input the bind ip");    //获取标准包的flag    pflag.CommandLine.AddGoFlagSet(flag.CommandLine);    pflag.Parse();    //BindFlag    //在pflag.Init key后面使用    viper.BindPFlag("port", pflag.Lookup("port"));    log.Printf("set port: %d", viper.GetInt("port"));    viper.BindPFlags(pflag.CommandLine);    log.Printf("set ip: %s", viper.GetString("ip"));}

You can use the standard flag or the pflag that comes with the Viper package, and the author suggests using PFLAG.

Listening configuration file

//监听配置文件的修改和变动func WatchConfig(c *config) error {    if err := LoadConfigFromYaml(c); err !=nil{        return err;    }    ctx, cancel := context.WithCancel(context.Background());    c.v.WatchConfig()                //监听回调函数    watch := func(e fsnotify.Event) {        log.Printf("Config file is changed: %s \n", e.String())        cancel();    }    c.v.OnConfigChange(watch);    <-ctx.Done();    return nil;}

The point is, ah, this can be said to be very very useful a function, in the past we modify the configuration file to either restart the service, or make an API to modify, Viper this function to help us achieve. As long as the configuration file is modified and saved, our pre-registered Watch function is triggered, as long as we add the update operation in this area is OK. But the drawback is that it currently only listens for configuration files.

Copy sub-branches

func TestSubConfig(t *testing.T)  {    c := config{};    LoadConfigFromYaml(&c);    sc := c.v.Sub("information");    sc.Set("age", 80);    scs,_:=yaml.Marshal(sc.AllSettings())    t.Log(string(scs));    t.Logf("age: %d", c.v.GetInt("information.age"));}

Copy a sub-branch the biggest use is that we can copy a configuration, so that the original configuration when modifying the copy is not modified, if there is a problem with the configuration of the modification, we can easily rollback.

Ways to get Configuration items

//测试各种get类型func TestGetValues(t *testing.T)  {    c := &config{}    if err := LoadConfigFromYaml(c); err != nil{        t.Fatalf("%s: %s",t.Name(), err.Error());    }    if info := c.v.GetStringMap("information"); info != nil{        t.Logf("%T", info);    }    if aliases := c.v.GetStringSlice("information.aliases"); aliases != nil{        for _, a := range  aliases{            t.Logf("%s",a);        }    }    timeStamp := c.v.GetTime("timestamp");    t.Logf("%s", timeStamp.String());    if public := c.v.GetBool("information.public"); public{        t.Logf("the information is public");    }    age := c.v.GetInt("information.age");    t.Logf("%s age  is %d", c.v.GetString("information.name"), age);}

If we get the return value directly with Get is the interface{} type, so we have to manually convert, you can directly specify the type to get, convenient and quick.

In addition to the functions described above, Viper also has the ability to extract configurations from ETCD and customize Flage, which are interesting for you to get to know for yourself.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.