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  }