github.com/go-spring/spring-base@v1.1.3/log/log_inject.go (about) 1 /* 2 * Copyright 2012-2019 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * https://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package log 18 19 import ( 20 "errors" 21 "fmt" 22 "reflect" 23 "strconv" 24 "strings" 25 26 "github.com/go-spring/spring-base/code" 27 "github.com/go-spring/spring-base/util" 28 ) 29 30 var converters = map[reflect.Type]util.Converter{} 31 32 func init() { 33 RegisterConverter(ParseLevel) 34 RegisterConverter(ParseColorStyle) 35 } 36 37 // RegisterConverter registers Converter for non-primitive type such as 38 // time.Time, time.Duration, or other user-defined value type. 39 func RegisterConverter(fn util.Converter) { 40 t := reflect.TypeOf(fn) 41 if !util.IsConverter(t) { 42 panic(errors.New("fn must be func(string)(type,error)")) 43 } 44 converters[t.Out(0)] = fn 45 } 46 47 func newPlugin(t reflect.Type, node *Node) (reflect.Value, error) { 48 v := reflect.New(t) 49 err := inject(v.Elem(), v.Type().Elem(), node) 50 if err != nil { 51 return reflect.Value{}, err 52 } 53 i, ok := v.Interface().(Initializer) 54 if ok { 55 if err = i.Init(); err != nil { 56 return reflect.Value{}, err 57 } 58 } 59 return v, nil 60 } 61 62 // inject handles the struct field with the PluginAttribute or PluginElement tag. 63 func inject(v reflect.Value, t reflect.Type, node *Node) error { 64 for i := 0; i < v.NumField(); i++ { 65 ft := t.Field(i) 66 fv := v.Field(i) 67 if tag, ok := ft.Tag.Lookup("PluginAttribute"); ok { 68 if err := injectAttribute(tag, fv, ft, node); err != nil { 69 return err 70 } 71 continue 72 } 73 if tag, ok := ft.Tag.Lookup("PluginElement"); ok { 74 if err := injectElement(tag, fv, ft, node); err != nil { 75 return err 76 } 77 continue 78 } 79 if ft.Anonymous && ft.Type.Kind() == reflect.Struct { 80 if err := inject(fv, fv.Type(), node); err != nil { 81 return err 82 } 83 } 84 } 85 return nil 86 } 87 88 type PluginTag string 89 90 func (tag PluginTag) Get(key string) string { 91 v, _ := tag.Lookup(key) 92 return v 93 } 94 95 func (tag PluginTag) Lookup(key string) (value string, ok bool) { 96 kvs := strings.Split(string(tag), ",") 97 if key == "" { 98 return kvs[0], true 99 } 100 for i := 1; i < len(kvs); i++ { 101 ss := strings.Split(kvs[i], "=") 102 if ss[0] == key { 103 if len(ss) > 1 { 104 return ss[1], true 105 } 106 return "", true 107 } 108 } 109 return "", false 110 } 111 112 // injectAttribute handles the struct field with the PluginAttribute tag. 113 func injectAttribute(tag string, fv reflect.Value, ft reflect.StructField, node *Node) error { 114 115 attrTag := PluginTag(tag) 116 attrName := attrTag.Get("") 117 val, ok := node.Attributes[attrName] 118 if !ok { 119 val, ok = attrTag.Lookup("default") 120 if !ok { 121 return fmt.Errorf("found no attribute for %s", attrName) 122 } 123 } 124 125 if fn := converters[ft.Type]; fn != nil { 126 fnValue := reflect.ValueOf(fn) 127 out := fnValue.Call([]reflect.Value{reflect.ValueOf(val)}) 128 if !out[1].IsNil() { 129 err := out[1].Interface().(error) 130 return util.Wrapf(err, code.FileLine(), "inject error") 131 } 132 fv.Set(out[0]) 133 return nil 134 } 135 136 switch fv.Kind() { 137 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 138 u, err := strconv.ParseUint(val, 0, 0) 139 if err == nil { 140 fv.SetUint(u) 141 return nil 142 } 143 return util.Wrapf(err, code.FileLine(), "inject %s error", ft.Name) 144 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 145 i, err := strconv.ParseInt(val, 0, 0) 146 if err == nil { 147 fv.SetInt(i) 148 return nil 149 } 150 return util.Wrapf(err, code.FileLine(), "inject %s error", ft.Name) 151 case reflect.Float32, reflect.Float64: 152 f, err := strconv.ParseFloat(val, 64) 153 if err == nil { 154 fv.SetFloat(f) 155 return nil 156 } 157 return util.Wrapf(err, code.FileLine(), "inject %s error", ft.Name) 158 case reflect.Bool: 159 b, err := strconv.ParseBool(val) 160 if err == nil { 161 fv.SetBool(b) 162 return nil 163 } 164 return util.Wrapf(err, code.FileLine(), "inject %s error", ft.Name) 165 case reflect.String: 166 fv.SetString(val) 167 return nil 168 } 169 return fmt.Errorf("unsupported inject type %s for struct field %s", ft.Type.String(), ft.Name) 170 } 171 172 // injectElement handles the struct field with the PluginElement tag. 173 func injectElement(tag string, fv reflect.Value, ft reflect.StructField, node *Node) error { 174 175 elemTag := PluginTag(tag) 176 elemType := elemTag.Get("") 177 178 var children []reflect.Value 179 for _, c := range node.Children { 180 p, ok := plugins[c.Label] 181 if !ok { 182 err := fmt.Errorf("plugin %s not found", c.Label) 183 return util.Wrap(err, code.FileLine(), "inject element") 184 } 185 if p.Type != elemType { 186 continue 187 } 188 v, err := newPlugin(p.Class, c) 189 if err != nil { 190 return err 191 } 192 children = append(children, v) 193 } 194 195 if len(children) == 0 { 196 elemLabel, ok := elemTag.Lookup("default") 197 if !ok { 198 return nil 199 } 200 p, ok := plugins[elemLabel] 201 if !ok { 202 err := fmt.Errorf("plugin %s not found", elemLabel) 203 return util.Wrap(err, code.FileLine(), "inject element") 204 } 205 v, err := newPlugin(p.Class, &Node{Label: elemLabel}) 206 if err != nil { 207 return err 208 } 209 children = append(children, v) 210 } 211 212 switch fv.Kind() { 213 case reflect.Slice: 214 slice := reflect.MakeSlice(ft.Type, 0, len(children)) 215 for j := 0; j < len(children); j++ { 216 slice = reflect.Append(slice, children[j]) 217 } 218 fv.Set(slice) 219 case reflect.Interface: 220 if len(children) > 1 { 221 return fmt.Errorf("found %d plugin elements for struct field %s", len(children), ft.Name) 222 } 223 fv.Set(children[0]) 224 default: 225 return fmt.Errorf("unsupported inject type %s for struct field %s", ft.Type.String(), ft.Name) 226 } 227 return nil 228 }