Skip to content

Example ENG

SHEP4RDO edited this page Apr 18, 2024 · 2 revisions

Using the mkconf module

Example of using one config

Initialization

ConfigManager - is the default means of communication between you and the mkconf module. It will help you create, read, update and start tracking your config. If you don't like its implementation, you can create your own. The example will use ConfigManager.

To add a configuration, the AddConfig function is used, which accepts a configuration name, a path to the configuration file, a file extension, and a pointer to the configuration structure.

cm := mkconf.NewConfigManager()
configPath := "C:\\My saved files\\testConfig" configName := "config"
cm.AddConfig(configName, configPath, ".json", &cf1)

&cf1 is a pointer to your configuration structure:

type  MyConfig  struct {
Key  string  `json:"key"`
Value  int  `json:"value"`
}

Prepare monitoring

These methods start change tracking and begin change monitoring for a configuration named config. You can receive only the change notification, only the change history, or both.

cm.StartAllLogChanges()
cm.StartChangeMonitoring("config", cf1)

To use them, you need to define callback functions:

changeCallback := func(configName string) {
    myCallbacks1(cm, configName, &cf1)
}
trackingCallback := func(configName string) {
    myCallbacks2(cm, configName)
}

cm.ChangeCallbackFunc(configName, changeCallback)
cm.TrackingCallbackFunc(configName, trackingCallback)

My callback functions look like this:

func  myCallbacks1(cm  *mkconf.ConfigManager, configName  string, cf1  *MyConfig) {
fmt.Printf("Reload config '%v'.\n", configName)
cm.LoadConfig(configName)
cf, err  :=  cm.GetConfig(configName)
if  err  !=  nil {
fmt.Printf("Error while reading config: %v\n", err)
return
}
*cf1  =  *cf.(*MyConfig)
fmt.Printf("Key : %v\n", cf1.Key)
fmt.Printf("Value : %v\n", cf1.Value)
}

  

func  myCallbacks2(cm  *mkconf.ConfigManager, configName  string) {
ff  :=  cm.GetChangesForConfig(configName)
for  i, v  :=  range  ff {
fmt.Printf("Changes №%v  %v:\n", i+1, v.Timestamp.Format("02.01.2006 15:04:05.000"))
fmt.Printf("%v: old value: %v new value:%v  \n", v.FieldName, v.OldValue, v.NewValue)
}
cm.ClearChangeLogs(configName)
fmt.Println()
fmt.Println()
fmt.Println()
fmt.Println()
}

These are demo functions that show the changes. You can use any kind you want.

cm.ClearChangeLogs(configName) is optional. By default, the entire list of changes is stored and it is not deleted. If you need to keep it - you can choose not to clear it.

Start monitoring

This step starts asynchronous tracking of configuration changes. If an error occurs, it will be sent to the errChan error channel.

errChan  :=  make(chan  error)
go  func() {
err  :=  cm.WatchForChanges()
if  err  !=  nil {
errChan  <-  err
}
}()

cm.WatchForChanges() automatically receives signals from channels about changes to the file and updates the change logs. It runs a callback that you have set up in advance. If it has NOT been installed, you will get an error.

Output example:

Reload config 'config'.
Key : da
Value : 9
Changes №1 18.04.2024 10:02:16.981:
key: old value: net  new value:da

Reload config 'config'.
Key : ili net
Value : 15
Changes №1 18.04.2024 10:02:26.091:
key: old value: da  new value:ili net
Changes №2 18.04.2024 10:02:26.091:
value: old value: 9  new value:15

Example of using multiple configs

Initialization

Here we define the configuration structures MyConfig, MyConfig2 and MyConfig3. For each configuration an instance of the corresponding structure is created. After that, a slice of itf interfaces is created, to which pointers to the previously created configuration instances are added.

cf1 := MyConfig{} 
cf2 := MyConfig2{}
cf3 := MyConfig3{}
configPath := "C:\\My saved files\\testConfig"
configNames := []string{"config.json", "config2.json", "config3.xml"}

var itf []interface{} 
itf = append(itf, &cf1, &cf2, &cf3)

cm := mkconf.NewConfigManager()  

Configurations from the specified path with the specified file names are loaded here. Each configuration is associated with the corresponding interface from the itf slice.

errors := cm.LoadConfigsFromPath(configPath, configNames, itf) 
for _, err := range errors { 
if err != nil { 
fmt.Printf("error: %v", err) 
	}
}

Prepare monitoring

Callback functions can also be set for each config separately, or for all of them at once. This function assigns one callback for all configurations. The changeCallback function will be called when any of the configurations is changed.

changeCallback  :=  func(configName  string) {
myCallbacksAll1(cm, configName, itf)
}

cm.ChangeCallbackFuncAll(changeCallback)
cm.StartAllChangeMonitoring()
func  myCallbacksAll1(cm  *mkconf.ConfigManager, configName  string, cfs []interface{}) {
fmt.Printf("Reload config '%v'.\n", configName)
cm.LoadConfig(configName)
cf, err  :=  cm.GetConfig(configName)
if  err  !=  nil {
fmt.Printf("Error while reading config: %v\n", err)
return
}

switch  v  :=  cf.(type) {
case  *MyConfig:
fmt.Printf("Key : %v\n", v.Key)
fmt.Printf("Value : %v\n", v.Value)
cfs[0] =  v

case  *MyConfig2:
fmt.Printf("Key : %v\n", v.Key)
fmt.Printf("Value : %v\n", v.Value)
cfs[1] =  v

case  *MyConfig3:
fmt.Printf("Database : %v\n", v.Database)
fmt.Printf("Logging : %v\n", v.Logging)
fmt.Printf("ServerInfo : %v\n", v.ServerInfo)
cfs[2] =  v

default:
fmt.Printf("hz cho za tip")
}
}

This is a test generic callback. This is an example for outputting and updating the modified config.

The following configs are used in the example:

type  MyConfig  struct {
Key  string  `json:"key"`
Value  int  `json:"value"`
}

type  MyConfig2  struct {
Key  string  `yaml:"key"`
Value  string  `yaml:"value"`
}

type  MyConfig3  struct {
XMLName  xml.Name  `xml:"config"`
ServerInfo  ServerInfo  `xml:"server_info"`
Database  Database  `xml:"database"`
Logging  Logging  `xml:"logging"`
}

type  ServerInfo  struct {
Host  string  `xml:"host"`
Port  int  `xml:"port"`
}

type  Database  struct {
DatabaseName  string  `xml:"database_name"`
User  string  `xml:"user"`
Password  string  `xml:"password"`
}

type  Logging  struct {
Level  string  `xml:"level"`
Output  string  `xml:"output"`
}

Start monitoring

This step starts asynchronous tracking of configuration changes. If an error occurs, it will be sent to the errChan error channel.

errChan  :=  make(chan  error)
go  func() {
err  :=  cm.WatchForChanges()
if  err  !=  nil {
errChan  <-  err
}
}()

cm.WatchForChanges() automatically receives signals from channels about changes to the file and updates the change logs. It runs a callback that you have set up in advance. If it has NOT been installed, you will get an error.

Output example:

Reload config 'config'.
Key : a)))
Value : 15

Reload config 'config2'.
Key : hi.
Value : !!!

Reload config 'config3'.
Database : {my_database admin secretpassword}
Logging : {info logs/app.log}
ServerInfo : {example.ua.com 8080}