github.com/GuanceCloud/cliutils@v1.1.21/point/point.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 implements datakits basic data structure.
     7  package point
     8  
     9  import (
    10  	bytes "bytes"
    11  	"encoding/base64"
    12  	"fmt"
    13  	"sort"
    14  	"strings"
    15  	"time"
    16  
    17  	protojson "github.com/gogo/protobuf/jsonpb"
    18  	influxm "github.com/influxdata/influxdb1-client/models"
    19  )
    20  
    21  const (
    22  	Psent   = 1 << iota // The Point has been sent
    23  	Ppb                 // the point is Protobuf point
    24  	Pcheck              // checked
    25  	Ppooled             // from point pool
    26  	// more...
    27  )
    28  
    29  type Callback func(*Point) (*Point, error)
    30  
    31  type Point struct {
    32  	flags uint64
    33  	pt    *PBPoint
    34  }
    35  
    36  // ClearFlag clear specific bit.
    37  func (p *Point) ClearFlag(f uint64) {
    38  	mask := ^(uint64(1) << f)
    39  	p.flags &= mask
    40  }
    41  
    42  // SetFlag set specific bit.
    43  func (p *Point) SetFlag(f uint) {
    44  	p.flags |= (1 << f)
    45  }
    46  
    47  // HasFlag test if specific bit set.
    48  func (p *Point) HasFlag(f uint) bool {
    49  	return (p.flags & (1 << f)) > 0
    50  }
    51  
    52  // WrapPoint wrap lagacy line-protocol point into Point.
    53  func WrapPoint(pts []influxm.Point) (arr []*Point) {
    54  	for _, pt := range pts {
    55  		if x := FromModelsLP(pt); x != nil {
    56  			arr = append(arr, x)
    57  		}
    58  	}
    59  	return
    60  }
    61  
    62  // NewLPPoint create Point based on a lineproto point.
    63  func NewLPPoint(lp influxm.Point) *Point {
    64  	return FromModelsLP(lp)
    65  }
    66  
    67  func (p *Point) MustLPPoint() influxm.Point {
    68  	lppt, err := p.LPPoint()
    69  	if err != nil {
    70  		panic(err.Error())
    71  	}
    72  
    73  	return lppt
    74  }
    75  
    76  // LPPoint get line-protocol part of the point.
    77  func (p *Point) LPPoint() (influxm.Point, error) {
    78  	return influxm.NewPoint(p.pt.Name, p.InfluxTags(), p.InfluxFields(), time.Unix(0, p.pt.Time))
    79  }
    80  
    81  // InfluxFields convert fields to map structure.
    82  func (p *Point) InfluxFields() map[string]any {
    83  	kvs := KVs(p.pt.Fields)
    84  	return kvs.InfluxFields()
    85  }
    86  
    87  // InfluxTags convert tags to map structure.
    88  func (p *Point) InfluxTags() influxm.Tags {
    89  	kvs := KVs(p.pt.Fields)
    90  	return kvs.InfluxTags()
    91  }
    92  
    93  // MapTags convert all key-value to map.
    94  func (p *Point) MapTags() map[string]string {
    95  	res := map[string]string{}
    96  
    97  	for _, kv := range p.pt.Fields {
    98  		if !kv.IsTag {
    99  			continue
   100  		}
   101  
   102  		res[kv.Key] = kv.GetS()
   103  	}
   104  
   105  	return res
   106  }
   107  
   108  // KVMap return all key-value in map.
   109  func (p *Point) KVMap() map[string]any {
   110  	res := map[string]any{}
   111  
   112  	for _, kv := range p.pt.Fields {
   113  		res[kv.Key] = kv.Raw()
   114  	}
   115  
   116  	return res
   117  }
   118  
   119  // Pretty get string representation of point, all key-valus will be sorted in output.
   120  func (p *Point) Pretty() string {
   121  	kvs := KVs(p.pt.Fields)
   122  
   123  	arr := []string{
   124  		"\n",
   125  		p.Name(),
   126  		"-----------",
   127  		kvs.Pretty(),
   128  		"-----------",
   129  		fmt.Sprintf("%s | %d",
   130  			p.Time().String(),
   131  			p.Time().UnixNano()),
   132  	}
   133  
   134  	if len(p.pt.Warns) > 0 {
   135  		arr = append(arr, "-----------")
   136  	}
   137  
   138  	// only pbpoint attached with warns
   139  	for _, w := range p.pt.Warns {
   140  		arr = append(arr, fmt.Sprintf("[W] %s: %s", w.Type, w.Msg))
   141  	}
   142  
   143  	// only pbpoint attached with debugs
   144  	for _, d := range p.pt.Debugs {
   145  		arr = append(arr, fmt.Sprintf("[D] %s", d.Info))
   146  	}
   147  
   148  	return strings.Join(arr, "\n")
   149  }
   150  
   151  // Warns return warnning info when build the point.
   152  func (p *Point) Warns() []*Warn {
   153  	return p.pt.Warns
   154  }
   155  
   156  // WarnsPretty return human readable warnning info.
   157  func (p *Point) WarnsPretty() string {
   158  	var arr []string
   159  	for _, w := range p.pt.Warns {
   160  		arr = append(arr, w.String())
   161  	}
   162  	return strings.Join(arr, "\n")
   163  }
   164  
   165  // makeLineproto build lineproto from @p's raw data(name/tag/field/time).
   166  func (p *Point) makeLineproto(prec ...Precision) string {
   167  	lp, err := p.LPPoint()
   168  	if err != nil {
   169  		return ""
   170  	}
   171  
   172  	if len(prec) == 0 {
   173  		return lp.String()
   174  	} else {
   175  		return lp.PrecisionString(prec[0].String())
   176  	}
   177  }
   178  
   179  func MustFromPBJson(j []byte) *Point {
   180  	if pts, err := FromPBJson(j); err != nil {
   181  		panic(err.Error())
   182  	} else {
   183  		return pts
   184  	}
   185  }
   186  
   187  func FromPBJson(j []byte) (*Point, error) {
   188  	var pbpt PBPoint
   189  	m := &protojson.Unmarshaler{}
   190  	buf := bytes.NewBuffer(j)
   191  	if err := m.Unmarshal(buf, &pbpt); err != nil {
   192  		return nil, err
   193  	}
   194  
   195  	return FromPB(&pbpt), nil
   196  }
   197  
   198  func FromModelsLP(lp influxm.Point) *Point {
   199  	lpfs, err := lp.Fields()
   200  	if err != nil {
   201  		return nil
   202  	}
   203  
   204  	var kvs KVs
   205  	kvs = NewKVs(lpfs)
   206  
   207  	tags := lp.Tags()
   208  	for _, t := range tags {
   209  		kvs = kvs.MustAddTag(string(t.Key), string(t.Value))
   210  	}
   211  
   212  	return NewPointV2(string(lp.Name()), kvs, WithTime(lp.Time()))
   213  }
   214  
   215  func FromPB(pb *PBPoint) *Point {
   216  	pt := NewPointV2(pb.Name, pb.Fields, WithTime(time.Unix(0, pb.Time)))
   217  	if len(pb.Warns) > 0 {
   218  		pt.pt.Warns = pb.Warns
   219  	}
   220  
   221  	if len(pb.Debugs) > 0 {
   222  		pt.pt.Debugs = pb.Debugs
   223  	}
   224  
   225  	pt.SetFlag(Ppb)
   226  	return pt
   227  }
   228  
   229  // LineProto convert point to text lineprotocol(both for
   230  // lineproto point and protobuf point).
   231  func (p *Point) LineProto(prec ...Precision) string {
   232  	return p.makeLineproto(prec...)
   233  }
   234  
   235  func (p *Point) PBJson() ([]byte, error) {
   236  	pbpt := p.PBPoint()
   237  	m := protojson.Marshaler{}
   238  	buf := &bytes.Buffer{}
   239  	if err := m.Marshal(buf, pbpt); err != nil {
   240  		return nil, err
   241  	}
   242  	return buf.Bytes(), nil
   243  }
   244  
   245  func (p *Point) PBJsonPretty() ([]byte, error) {
   246  	pbpt := p.PBPoint()
   247  
   248  	m := &protojson.Marshaler{Indent: "  "}
   249  	buf := &bytes.Buffer{}
   250  	if err := m.Marshal(buf, pbpt); err != nil {
   251  		return nil, err
   252  	}
   253  
   254  	return buf.Bytes(), nil
   255  }
   256  
   257  // Tags return point's key-values except fields.
   258  func (p *Point) Tags() (arr KVs) {
   259  	kvs := KVs(p.pt.Fields)
   260  	return kvs.Tags()
   261  }
   262  
   263  // Fields return point's key-values except tags.
   264  func (p *Point) Fields() (arr KVs) {
   265  	kvs := KVs(p.pt.Fields)
   266  	return kvs.Fields()
   267  }
   268  
   269  // KVs return point's all key-values.
   270  func (p *Point) KVs() (arr KVs) {
   271  	return KVs(p.pt.Fields)
   272  }
   273  
   274  // AddKVs add(shallow-copy) kvs, if keys exist, do nothing.
   275  func (p *Point) AddKVs(kvs ...*Field) {
   276  	old := KVs(p.pt.Fields)
   277  	for _, kv := range kvs {
   278  		old = old.AddKV(kv, false)
   279  	}
   280  	p.pt.Fields = old
   281  }
   282  
   283  // CopyTags deep-copy tag kvs, if keys exist, do nothing.
   284  func (p *Point) CopyTags(kvs ...*Field) {
   285  	old := KVs(p.pt.Fields)
   286  	for _, kv := range kvs {
   287  		old = old.AddTag(kv.Key, kv.GetS())
   288  	}
   289  	p.pt.Fields = old
   290  }
   291  
   292  // CopyField deep-copy field kvs, if keys exist, do nothing.
   293  func (p *Point) CopyField(kvs ...*Field) {
   294  	old := KVs(p.pt.Fields)
   295  	for _, kv := range kvs {
   296  		old = old.Add(kv.Key, kv.Raw(), false, false)
   297  	}
   298  	p.pt.Fields = old
   299  }
   300  
   301  // SetKVs set kvs as p's new KVs.
   302  func (p *Point) SetKVs(kvs ...*Field) {
   303  	old := KVs(p.pt.Fields)
   304  
   305  	for _, kv := range kvs {
   306  		old = old.AddKV(kv, false)
   307  	}
   308  	p.pt.Fields = old
   309  }
   310  
   311  // MustAddKVs add kv, if the key exist, override it.
   312  func (p *Point) MustAddKVs(kvs ...*Field) {
   313  	old := KVs(p.pt.Fields)
   314  	for _, kv := range kvs {
   315  		old = old.AddKV(kv, true)
   316  	}
   317  	p.pt.Fields = old
   318  }
   319  
   320  // Name return point's measurement name.
   321  func (p *Point) Name() string {
   322  	return p.pt.Name
   323  }
   324  
   325  // Time return point's time.
   326  func (p *Point) Time() time.Time {
   327  	return time.Unix(0, p.pt.Time)
   328  }
   329  
   330  // Get get specific key from point.
   331  func (p *Point) Get(k string) any {
   332  	kvs := KVs(p.pt.Fields)
   333  
   334  	if kv := kvs.Get(k); kv != nil {
   335  		return kv.Raw()
   336  	}
   337  	return nil
   338  }
   339  
   340  // GetTag get value of tag k.
   341  // If key k not tag or k not eixst, return nil.
   342  func (p *Point) GetTag(k string) string {
   343  	kvs := KVs(p.pt.Fields)
   344  	return kvs.GetTag(k)
   345  }
   346  
   347  // MustAdd add specific key value to fields, if k exist, override it.
   348  func (p *Point) MustAdd(k string, v any) {
   349  	kvs := KVs(p.pt.Fields)
   350  	kvs = kvs.Add(k, v, false, true)
   351  	p.pt.Fields = kvs
   352  }
   353  
   354  // Add add specific key value to fields, if k exist, do nothing.
   355  func (p *Point) Add(k string, v any) {
   356  	kvs := KVs(p.pt.Fields)
   357  	p.pt.Fields = kvs.Add(k, v, false, false)
   358  }
   359  
   360  // MustAddTag add specific key value to fields, if k exist, override it.
   361  func (p *Point) MustAddTag(k, v string) {
   362  	kvs := KVs(p.pt.Fields)
   363  	p.pt.Fields = kvs.Add(k, v, true, true)
   364  }
   365  
   366  // AddTag add specific key value to fields, if k exist, do nothing.
   367  func (p *Point) AddTag(k, v string) {
   368  	kvs := KVs(p.pt.Fields)
   369  	p.pt.Fields = kvs.Add(k, v, true, false)
   370  }
   371  
   372  // Del delete specific key from tags/fields.
   373  func (p *Point) Del(k string) {
   374  	kvs := KVs(p.pt.Fields)
   375  	p.pt.Fields = kvs.Del(k)
   376  }
   377  
   378  func (p *Point) AddDebug(d *Debug) {
   379  	p.pt.Debugs = append(p.pt.Debugs, d)
   380  }
   381  
   382  // PBPoint create Point based on a protobuf point.
   383  func (p *Point) PBPoint() *PBPoint {
   384  	return p.pt
   385  }
   386  
   387  // Keys get points all keys.
   388  func (p *Point) Keys() *Keys {
   389  	kvs := KVs(p.pt.Fields)
   390  
   391  	res := &Keys{
   392  		hash: uint64(0),
   393  		arr:  kvs.Keys().arr,
   394  	}
   395  
   396  	sort.Sort(res)
   397  
   398  	return res
   399  }
   400  
   401  // Size get underling data size in byte(exclude warning/debug info).
   402  func (p *Point) Size() int {
   403  	n := len(p.pt.Name)
   404  	for _, kv := range p.pt.Fields {
   405  		n += len(kv.Key)
   406  		n += 1 // IsTag
   407  		n += 8 // time
   408  		n += 4 // MetricType: uint32
   409  		n += len(kv.Unit)
   410  
   411  		switch kv.Val.(type) {
   412  		case *Field_I,
   413  			*Field_F,
   414  			*Field_U:
   415  			n += 8
   416  
   417  		case *Field_B:
   418  			n += 1
   419  
   420  		case *Field_D:
   421  			n += len(kv.GetD())
   422  
   423  		case *Field_S:
   424  			n += len(kv.GetS())
   425  
   426  		case *Field_A:
   427  			if a := kv.GetA(); a != nil {
   428  				n += (len(a.TypeUrl) + len(a.Value))
   429  			}
   430  
   431  		default:
   432  			// ignored
   433  		}
   434  	}
   435  
   436  	for _, w := range p.pt.Warns {
   437  		n += (len(w.Type) + len(w.Msg))
   438  	}
   439  
   440  	for _, d := range p.pt.Debugs {
   441  		n += (len(d.Info))
   442  	}
   443  
   444  	return n
   445  }
   446  
   447  // LPSize get point line-protocol size.
   448  func (p *Point) LPSize() int {
   449  	lppt, err := p.LPPoint()
   450  	if err != nil {
   451  		return 0
   452  	}
   453  
   454  	return len(lppt.String())
   455  }
   456  
   457  // PBSize get point protobuf size.
   458  func (p *Point) PBSize() int {
   459  	pbpt := p.PBPoint()
   460  
   461  	m := protojson.Marshaler{}
   462  	buf := bytes.Buffer{}
   463  
   464  	if err := m.Marshal(&buf, pbpt); err != nil {
   465  		return 0
   466  	}
   467  
   468  	return buf.Len()
   469  }
   470  
   471  func b64(x []byte) string {
   472  	return base64.StdEncoding.EncodeToString(x)
   473  }