github.com/observiq/carbon@v0.9.11-0.20200820160507-1b872e368a5e/operator/helper/severity_builder.go (about) 1 package helper 2 3 import ( 4 "fmt" 5 "strconv" 6 "strings" 7 8 "github.com/observiq/carbon/entry" 9 "github.com/observiq/carbon/operator" 10 ) 11 12 const minSeverity = 0 13 const maxSeverity = 100 14 15 // map[string or int input]sev-level 16 func getBuiltinMapping(name string) severityMap { 17 switch name { 18 case "none": 19 return map[string]entry.Severity{} 20 case "aliases": 21 return map[string]entry.Severity{ 22 "default": entry.Default, 23 "trace": entry.Trace, 24 "debug": entry.Debug, 25 "info": entry.Info, 26 "notice": entry.Notice, 27 "warning": entry.Warning, 28 "error": entry.Error, 29 "critical": entry.Critical, 30 "alert": entry.Alert, 31 "emergency": entry.Emergency, 32 "catastrophe": entry.Catastrophe, 33 } 34 default: 35 mapping := getBuiltinMapping("aliases") 36 mapping.add(entry.Warning, "warn") 37 mapping.add(entry.Error, "err") 38 mapping.add(entry.Critical, "crit") 39 return mapping 40 } 41 } 42 43 func (s severityMap) add(severity entry.Severity, parseableValues ...string) { 44 for _, str := range parseableValues { 45 s[str] = severity 46 } 47 } 48 49 const ( 50 // HTTP2xx is a special key that is represents a range from 200 to 299. Literal value is "2xx" 51 HTTP2xx = "2xx" 52 53 // HTTP3xx is a special key that is represents a range from 300 to 399. Literal value is "3xx" 54 HTTP3xx = "3xx" 55 56 // HTTP4xx is a special key that is represents a range from 400 to 499. Literal value is "4xx" 57 HTTP4xx = "4xx" 58 59 // HTTP5xx is a special key that is represents a range from 500 to 599. Literal value is "5xx" 60 HTTP5xx = "5xx" 61 ) 62 63 func NewSeverityParserConfig() SeverityParserConfig { 64 return SeverityParserConfig{} 65 } 66 67 // SeverityParserConfig allows users to specify how to parse a severity from a field. 68 type SeverityParserConfig struct { 69 ParseFrom *entry.Field `json:"parse_from,omitempty" yaml:"parse_from,omitempty"` 70 Preserve bool `json:"preserve,omitempty" yaml:"preserve,omitempty"` 71 Preset string `json:"preset,omitempty" yaml:"preset,omitempty"` 72 Mapping map[interface{}]interface{} `json:"mapping,omitempty" yaml:"mapping,omitempty"` 73 } 74 75 // Build builds a SeverityParser from a SeverityParserConfig 76 func (c *SeverityParserConfig) Build(context operator.BuildContext) (SeverityParser, error) { 77 operatorMapping := getBuiltinMapping(c.Preset) 78 79 for severity, unknown := range c.Mapping { 80 sev, err := validateSeverity(severity) 81 if err != nil { 82 return SeverityParser{}, err 83 } 84 85 switch u := unknown.(type) { 86 case []interface{}: // check before interface{} 87 for _, value := range u { 88 v, err := parseableValues(value) 89 if err != nil { 90 return SeverityParser{}, err 91 } 92 operatorMapping.add(sev, v...) 93 } 94 case interface{}: 95 v, err := parseableValues(u) 96 if err != nil { 97 return SeverityParser{}, err 98 } 99 operatorMapping.add(sev, v...) 100 } 101 } 102 103 if c.ParseFrom == nil { 104 return SeverityParser{}, fmt.Errorf("missing required field 'parse_from'") 105 } 106 107 p := SeverityParser{ 108 ParseFrom: *c.ParseFrom, 109 Preserve: c.Preserve, 110 Mapping: operatorMapping, 111 } 112 113 return p, nil 114 } 115 116 func validateSeverity(severity interface{}) (entry.Severity, error) { 117 if sev, err := getBuiltinMapping("aliases").find(severity); err != nil { 118 return entry.Nil, err 119 } else if sev != entry.Nil { 120 return sev, nil 121 } 122 123 // If integer between 0 and 100 124 var intSev int 125 switch s := severity.(type) { 126 case int: 127 intSev = s 128 case string: 129 i, err := strconv.ParseInt(s, 10, 8) 130 if err != nil { 131 return entry.Nil, fmt.Errorf("%s cannot be used as a severity", severity) 132 } 133 intSev = int(i) 134 default: 135 return entry.Nil, fmt.Errorf("type %T cannot be used as a severity (%v)", severity, severity) 136 } 137 138 if intSev < minSeverity || intSev > maxSeverity { 139 return entry.Nil, fmt.Errorf("severity must be between %d and %d", minSeverity, maxSeverity) 140 } 141 return entry.Severity(intSev), nil 142 } 143 144 func isRange(value interface{}) (int, int, bool) { 145 rawMap, ok := value.(map[interface{}]interface{}) 146 if !ok { 147 return 0, 0, false 148 } 149 150 min, minOK := rawMap["min"] 151 max, maxOK := rawMap["max"] 152 if !minOK || !maxOK { 153 return 0, 0, false 154 } 155 156 minInt, minOK := min.(int) 157 maxInt, maxOK := max.(int) 158 if !minOK || !maxOK { 159 return 0, 0, false 160 } 161 162 return minInt, maxInt, true 163 } 164 165 func expandRange(min, max int) []string { 166 if min > max { 167 min, max = max, min 168 } 169 170 rangeOfStrings := []string{} 171 for i := min; i <= max; i++ { 172 rangeOfStrings = append(rangeOfStrings, strconv.Itoa(i)) 173 } 174 return rangeOfStrings 175 } 176 177 func parseableValues(value interface{}) ([]string, error) { 178 switch v := value.(type) { 179 case int: 180 return []string{strconv.Itoa(v)}, nil // store as string because we will compare as string 181 case string: 182 switch v { 183 case HTTP2xx: 184 return expandRange(200, 299), nil 185 case HTTP3xx: 186 return expandRange(300, 399), nil 187 case HTTP4xx: 188 return expandRange(400, 499), nil 189 case HTTP5xx: 190 return expandRange(500, 599), nil 191 default: 192 return []string{strings.ToLower(v)}, nil 193 } 194 case []byte: 195 return []string{strings.ToLower(string(v))}, nil 196 default: 197 min, max, ok := isRange(v) 198 if ok { 199 return expandRange(min, max), nil 200 } 201 return nil, fmt.Errorf("type %T cannot be parsed as a severity", v) 202 } 203 }