github.com/GuanceCloud/cliutils@v1.1.21/point/check.go (about) 1 // Unless explicitly stated otherwise all files in this repository are licensed 2 // under the MIT License. 3 // This product includes software developed at Guance Cloud (https://www.guance.com/). 4 // Copyright 2021-present Guance, Inc. 5 6 package point 7 8 import ( 9 "fmt" 10 "math" 11 "reflect" 12 "strings" 13 ) 14 15 type checker struct { 16 *cfg 17 warns []*Warn 18 } 19 20 func (c *checker) reset() { 21 c.warns = c.warns[:0] 22 } 23 24 func (c *checker) check(pt *Point) *Point { 25 pt.pt.Name = c.checkMeasurement(pt.pt.Name) 26 pt.pt.Fields = c.checkKVs(pt.pt.Fields) 27 pt.pt.Warns = append(pt.pt.Warns, c.warns...) 28 29 // Add more checkings... 30 return pt 31 } 32 33 func (c *checker) addWarn(t, msg string) { 34 c.warns = append(c.warns, &Warn{ 35 Type: t, Msg: msg, 36 }) 37 } 38 39 func (c *checker) checkMeasurement(m string) string { 40 if len(m) == 0 { 41 c.addWarn(WarnInvalidMeasurement, 42 fmt.Sprintf("empty measurement, use %s", DefaultMeasurementName)) 43 m = DefaultMeasurementName 44 } 45 46 if c.cfg.maxMeasurementLen > 0 && len(m) > c.cfg.maxMeasurementLen { 47 c.addWarn(WarnInvalidMeasurement, 48 fmt.Sprintf("exceed max measurement length(%d), got length %d, trimmed", 49 c.cfg.maxMeasurementLen, len(m))) 50 return m[:c.cfg.maxMeasurementLen] 51 } else { 52 return m 53 } 54 } 55 56 func (c *checker) checkKVs(kvs KVs) KVs { 57 tcnt := kvs.TagCount() 58 fcnt := kvs.FieldCount() 59 60 // delete extra fields 61 if c.cfg.maxFields > 0 && fcnt > c.cfg.maxFields { 62 c.addWarn(WarnMaxFields, 63 fmt.Sprintf("exceed max field count(%d), got %d fields, extra fields deleted", 64 c.cfg.maxFields, fcnt)) 65 66 kvs = kvs.TrimFields(c.cfg.maxFields) 67 } 68 69 // delete extra tags 70 if c.cfg.maxTags > 0 && tcnt > c.cfg.maxTags { 71 c.addWarn(WarnMaxFields, 72 fmt.Sprintf("exceed max tag count(%d), got %d tags, extra tags deleted", 73 c.cfg.maxTags, tcnt)) 74 75 kvs = kvs.TrimTags(c.cfg.maxTags) 76 } 77 78 // check each kv valid 79 idx := 0 80 for _, kv := range kvs { 81 if x, ok := c.checkKV(kv, kvs); ok { 82 kvs[idx] = x 83 idx++ 84 } else if defaultPTPool != nil { 85 // When point-pool enabled, on drop f, we should put-back to pool. 86 defaultPTPool.PutKV(x) 87 } 88 } 89 90 for j := idx; j < len(kvs); j++ { // remove deleted elems 91 kvs[j] = nil 92 } 93 94 kvs = kvs[:idx] 95 96 // check required keys 97 kvs = c.keyMiss(kvs) 98 99 return kvs 100 } 101 102 // Remove all `\` suffix on key/val 103 // Replace all `\n` with ` `. 104 func adjustKV(x string) string { 105 if strings.HasSuffix(x, `\`) { 106 x = trimSuffixAll(x, `\`) 107 } 108 109 if strings.Contains(x, "\n") { 110 x = strings.ReplaceAll(x, "\n", " ") 111 } 112 113 return x 114 } 115 116 func (c *checker) checkKV(f *Field, kvs KVs) (*Field, bool) { 117 if f.IsTag { 118 return c.checkTag(f, kvs) 119 } else { 120 return c.checkField(f, kvs) 121 } 122 } 123 124 func (c *checker) keyConflict(key string, kvs KVs) bool { 125 i := 0 126 for _, kv := range kvs { 127 if kv.Key == key { 128 i++ 129 } 130 } 131 132 if i > 1 { // key exist more than once. 133 c.addWarn(WarnKeyNameConflict, fmt.Sprintf("same key (%q)", key)) 134 return true 135 } 136 137 return false 138 } 139 140 // checkTag try to auto modify the f. If we need to drop 141 // f, we return false. 142 func (c *checker) checkTag(f *Field, kvs KVs) (*Field, bool) { 143 if c.cfg.maxTagKeyLen > 0 && len(f.Key) > c.cfg.maxTagKeyLen { 144 c.addWarn(WarnMaxTagKeyLen, 145 fmt.Sprintf("exceed max tag key length(%d), got %d, key truncated", 146 c.cfg.maxTagKeyLen, len(f.Key))) 147 148 if newKey := f.Key[:c.cfg.maxTagKeyLen]; kvs.Get(newKey) != nil { 149 c.addWarn(WarnInvalidTagKey, fmt.Sprintf(`tag key %q exist after truncate`, newKey)) 150 return f, false 151 } else { 152 f.Key = newKey 153 } 154 } 155 156 // check tag key '\', '\n' 157 if newKey := adjustKV(f.Key); newKey != f.Key { 158 c.addWarn(WarnInvalidTagKey, fmt.Sprintf(`invalid tag key %q, replace \ or \n with ''`, f.Key)) 159 160 if kvs.Get(newKey) != nil { 161 c.addWarn(WarnInvalidTagKey, fmt.Sprintf(`tag key %q exist after adjust`, newKey)) 162 return f, false 163 } else { 164 f.Key = newKey 165 } 166 } 167 168 // replace `.' with `_' in tag keys 169 if !c.cfg.enableDotInKey && strings.Contains(f.Key, ".") { 170 c.addWarn(WarnInvalidTagKey, fmt.Sprintf("invalid tag key `%s': found `.'", f.Key)) 171 172 newKey := strings.ReplaceAll(f.Key, ".", "_") 173 174 if kvs.Get(newKey) != nil { 175 c.addWarn(WarnInvalidTagKey, fmt.Sprintf(`tag key %q exist after adjust`, newKey)) 176 return f, false 177 } else { 178 f.Key = newKey 179 } 180 } 181 182 // check if key ok after all checking. 183 if c.keyDisabled(f.Key) { 184 return f, false 185 } 186 187 if c.keyConflict(f.Key, kvs) { 188 return f, false 189 } 190 191 x := f.Val.(*Field_S) 192 // check tag value: '\', '\n' 193 if str := f.GetS(); str != "" { 194 if s := adjustKV(str); str != s { 195 c.addWarn(WarnNROrTailEscape, fmt.Sprintf(`invalid tag value %q, found \n or tail \`, str)) 196 x.S = s 197 f.Val = x 198 } 199 } 200 201 if c.cfg.maxTagValLen > 0 && len(x.S) > c.cfg.maxTagValLen { 202 c.addWarn(WarnMaxTagValueLen, 203 fmt.Sprintf("exceed max tag value length(%d), got %d, value truncated", 204 c.cfg.maxTagValLen, len(x.S))) 205 206 x.S = x.S[:c.cfg.maxTagValLen] // trim value length 207 f.Val = x 208 } 209 210 return f, true 211 } 212 213 // checkField try to auto modify the f. If we need to drop 214 // f, we return false. 215 func (c *checker) checkField(f *Field, kvs KVs) (*Field, bool) { 216 // trim key 217 if c.cfg.maxFieldKeyLen > 0 && len(f.Key) > c.cfg.maxFieldKeyLen { 218 c.addWarn(WarnMaxFieldKeyLen, 219 fmt.Sprintf("exceed max field key length(%d), got length %d, key truncated", c.cfg.maxFieldKeyLen, len(f.Key))) 220 221 if newKey := f.Key[:c.cfg.maxFieldKeyLen]; kvs.Get(newKey) != nil { 222 c.addWarn(WarnInvalidTagKey, fmt.Sprintf(`field key %q exist after truncate`, newKey)) 223 return f, false 224 } else { 225 f.Key = newKey 226 } 227 } 228 229 if strings.Contains(f.Key, ".") && !c.cfg.enableDotInKey { 230 c.addWarn(WarnDotInkey, fmt.Sprintf("invalid field key `%s': found `.'", f.Key)) 231 232 if newKey := strings.ReplaceAll(f.Key, ".", "_"); kvs.Get(newKey) != nil { 233 c.addWarn(WarnInvalidTagKey, fmt.Sprintf("field key %q exist after adjust", newKey)) 234 return f, false 235 } else { 236 f.Key = newKey 237 } 238 } 239 240 if newKey := adjustKV(f.Key); newKey != f.Key { 241 c.addWarn(WarnNROrTailEscape, fmt.Sprintf(`invalid field key %q: found \n, replaced with ' '`, f.Key)) 242 243 if kvs.Get(newKey) != nil { 244 c.addWarn(WarnInvalidTagKey, fmt.Sprintf("field key %q exist after adjust", newKey)) 245 return f, false 246 } else { 247 f.Key = newKey 248 } 249 } 250 251 // check if key ok after all checking. 252 if c.keyDisabled(f.Key) { 253 return f, false 254 } 255 256 if c.keyConflict(f.Key, kvs) { 257 return f, false 258 } 259 260 switch x := f.Val.(type) { 261 case *Field_U: 262 if !c.cfg.enableU64Field { 263 if x.U > uint64(math.MaxInt64) { 264 c.addWarn(WarnMaxFieldValueInt, 265 fmt.Sprintf("too large int field: key=%s, value=%d(> %d)", f.Key, x.U, uint64(math.MaxInt64))) 266 return f, false 267 } else { 268 // Force convert uint64 to int64: to disable line proto like 269 // `abc,tag=1 f1=32u` 270 // expected is: 271 // `abc,tag=1 f1=32i` 272 if defaultPTPool != nil { 273 defaultPTPool.PutKV(f) 274 f = defaultPTPool.GetKV(f.Key, int64(x.U)) 275 } else { 276 f.Val = &Field_I{I: int64(x.U)} 277 } 278 } 279 } 280 281 case *Field_F: 282 if math.IsInf(x.F, 1) { 283 c.addWarn(WarnInfConvertToMaxValue, 284 fmt.Sprintf("+inf value from %q convert to max-uint64", f.Key)) 285 x.F = math.MaxUint64 286 } else if math.IsInf(x.F, -1) { 287 c.addWarn(WarnInfConvertToMinValue, 288 fmt.Sprintf("+inf value from %q convert to min-int64", f.Key)) 289 x.F = math.MinInt64 290 } 291 292 case *Field_B, *Field_I, *Field_A: 293 // pass: they are ok 294 295 case nil: 296 c.addWarn(WarnNilField, fmt.Sprintf("nil field(%s)", f.Key)) 297 298 case *Field_D: // same as []uint8 299 300 if !c.cfg.enableStrField { 301 c.addWarn(WarnInvalidFieldValueType, 302 fmt.Sprintf("field(%s) dropped with string value, when [DisableStringField] enabled", f.Key)) 303 return f, false 304 } 305 306 if c.cfg.maxFieldValLen > 0 && len(x.D) > c.cfg.maxFieldValLen { 307 c.addWarn(WarnMaxFieldValueLen, 308 fmt.Sprintf("field (%s) exceed max field value length(%d), got %d, value truncated", 309 f.Key, c.cfg.maxFieldValLen, len(x.D))) 310 311 x.D = x.D[:c.cfg.maxFieldValLen] 312 f.Val = x 313 } 314 315 case *Field_S: // same as Field_D 316 317 if !c.cfg.enableStrField { 318 c.addWarn(WarnInvalidFieldValueType, 319 fmt.Sprintf("field(%s) dropped with string value, when [DisableStringField] enabled", f.Key)) 320 return f, false 321 } 322 323 if c.cfg.maxFieldValLen > 0 && len(x.S) > c.cfg.maxFieldValLen { 324 c.addWarn(WarnMaxFieldValueLen, 325 fmt.Sprintf("field (%s) exceed max field value length(%d), got %d, value truncated", 326 f.Key, c.cfg.maxFieldValLen, len(x.S))) 327 328 x.S = x.S[:c.cfg.maxFieldValLen] 329 f.Val = x 330 } 331 332 default: 333 c.addWarn(WarnInvalidFieldValueType, 334 fmt.Sprintf("invalid field (%s), value: %s, type: %s", 335 f.Key, f.Val, reflect.TypeOf(f.Val))) 336 return f, false 337 } 338 339 return f, true 340 } 341 342 func trimSuffixAll(s, sfx string) string { 343 var x string 344 for { 345 x = strings.TrimSuffix(s, sfx) 346 if x == s { 347 break 348 } 349 s = x 350 } 351 return x 352 } 353 354 func (c *checker) keyDisabled(k string) bool { 355 if k == "" { 356 c.addWarn(WarnTagDisabled, "empty tag key disabled") 357 return true 358 } 359 360 for _, item := range c.cfg.disabledKeys { 361 if k == item.key { 362 c.addWarn(WarnTagDisabled, fmt.Sprintf("tag key %q disabled", k)) 363 return true 364 } 365 } 366 367 return false 368 } 369 370 func (c *checker) keyMiss(kvs KVs) KVs { 371 if c.cfg.requiredKeys == nil { 372 return kvs 373 } 374 375 for _, rk := range c.cfg.requiredKeys { 376 if !kvs.Has(rk.Key()) { 377 if def := rk.Default(); def != nil { 378 kvs = kvs.MustAddKV(NewKV(rk.Key(), def)) 379 380 c.addWarn(WarnAddRequiredKV, 381 fmt.Sprintf("add required key-value %q: %q", rk.Key(), def)) 382 } // NOTE: if no default-value, the required key not added 383 } 384 } 385 386 return kvs 387 } 388 389 // CheckPoints used to check pts on various opts. 390 func CheckPoints(pts []*Point, opts ...Option) (arr []*Point) { 391 c := GetCfg(opts...) 392 defer PutCfg(c) 393 394 chk := checker{cfg: c} 395 396 arr = pts[:0] 397 398 for _, pt := range pts { 399 if pt.pt == nil { 400 continue 401 } 402 403 pt = chk.check(pt) 404 pt.SetFlag(Pcheck) 405 pt.pt.Warns = chk.warns 406 arr = append(arr, pt) 407 chk.reset() 408 } 409 410 return arr 411 }