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 }