| // Licensed to Apache Software Foundation (ASF) under one or more contributor |
| // license agreements. See the NOTICE file distributed with |
| // this work for additional information regarding copyright |
| // ownership. Apache Software Foundation (ASF) licenses this file to you under |
| // the Apache License, Version 2.0 (the "License"); you may |
| // not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| package plugin |
| |
| import ( |
| "fmt" |
| "reflect" |
| "strings" |
| |
| "github.com/sirupsen/logrus" |
| |
| "github.com/spf13/viper" |
| |
| "github.com/apache/skywalking-satellite/internal/pkg/config" |
| "github.com/apache/skywalking-satellite/internal/pkg/log" |
| ) |
| |
| // the global plugin registry |
| var ( |
| Reg map[reflect.Type]map[string]reflect.Value |
| ) |
| |
| func init() { |
| Reg = make(map[reflect.Type]map[string]reflect.Value) |
| } |
| |
| // RegisterPluginCategory register the RegInfo to the global type registry. |
| func RegisterPluginCategory(pluginType reflect.Type) { |
| Reg[pluginType] = map[string]reflect.Value{} |
| } |
| |
| // RegisterPlugin registers the pluginType as plugin. |
| // If the plugin is a pointer receiver, please pass a pointer. Otherwise, please pass a value. |
| func RegisterPlugin(plugin Plugin) { |
| v := reflect.ValueOf(plugin) |
| success := false |
| for pCategory, pReg := range Reg { |
| if v.Type().Implements(pCategory) { |
| pReg[plugin.Name()] = v |
| log.Logger.WithFields(logrus.Fields{ |
| "category": v.Type().String(), |
| "plugin_name": plugin.Name(), |
| }).Debug("register plugin success") |
| success = true |
| } |
| } |
| if !success { |
| log.Logger.WithFields(logrus.Fields{ |
| "category": v.Type().String(), |
| "plugin_name": plugin.Name(), |
| }).Error("plugin is not allowed to register") |
| } |
| } |
| |
| // Get an initialized specific plugin according to the pluginCategory and config. |
| func Get(category reflect.Type, cfg Config) Plugin { |
| pluginName := nameFinder(cfg) |
| value, ok := Reg[category][pluginName] |
| if !ok { |
| panic(fmt.Errorf("cannot find %s plugin, and the category of plugin is %s", pluginName, category)) |
| } |
| t := value.Type() |
| if t.Kind() == reflect.Ptr { |
| t = t.Elem() |
| } |
| plugin := reflect.New(t).Interface().(Plugin) |
| initializing(plugin, cfg) |
| return plugin |
| } |
| |
| // nameFinder is used to get the plugin name in Config. |
| func nameFinder(cfg interface{}) string { |
| c, ok := cfg.(Config) |
| if !ok { |
| panic(fmt.Errorf("nameFinder only supports Config")) |
| } |
| name, ok := c[NameField] |
| if !ok { |
| panic(fmt.Errorf("%s is requeired in Config", NameField)) |
| } |
| return name.(string) |
| } |
| |
| // initializing initialize the fields by fields mapping. |
| func initializing(plugin Plugin, cfg Config) { |
| v := viper.New() |
| v.SetConfigType("yaml") |
| if plugin.DefaultConfig() != "" { |
| if err := v.ReadConfig(strings.NewReader(plugin.DefaultConfig())); err != nil { |
| panic(fmt.Errorf("cannot read default config in the plugin: %s, the error is %v", plugin.Name(), err)) |
| } |
| } |
| if err := v.MergeConfigMap(cfg); err != nil { |
| panic(fmt.Errorf("%s plugin cannot merge the custom configuration, the error is %v", plugin.Name(), err)) |
| } |
| if err := v.Unmarshal(plugin); err != nil { |
| panic(fmt.Errorf("cannot inject the config to the %s plugin, the error is %v", plugin.Name(), err)) |
| } |
| cf := reflect.ValueOf(plugin).Elem().FieldByName(config.CommonFieldsName) |
| if !cf.IsValid() { |
| panic(fmt.Errorf("%s plugin must have a field named CommonField", plugin.Name())) |
| } |
| for i := 0; i < cf.NumField(); i++ { |
| tagVal := cf.Type().Field(i).Tag.Get(config.TagName) |
| if tagVal != "" { |
| if val := cfg[strings.ToLower(config.CommonFieldsName)+"_"+tagVal]; val != nil { |
| cf.Field(i).Set(reflect.ValueOf(val)) |
| } |
| } |
| } |
| } |