github.com/GuanceCloud/cliutils@v1.1.21/lineproto/lp.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 lineproto 7 8 import ( 9 "bytes" 10 "fmt" 11 "reflect" 12 "sort" 13 "time" 14 "unsafe" 15 16 "github.com/influxdata/influxdb1-client/models" 17 lp "github.com/influxdata/line-protocol/v2/lineprotocol" 18 ) 19 20 func unsafeBytesToString(runes []byte) string { 21 return *(*string)(unsafe.Pointer(&runes)) //nolint:gosec 22 } 23 24 // GetSafeString get a copy of string s. 25 func GetSafeString(s string) string { 26 if s == "" { 27 return "" 28 } 29 return string(*(*[]byte)(unsafe.Pointer(&s))) //nolint:gosec 30 } 31 32 type Precision = lp.Precision 33 34 const ( 35 Nanosecond = lp.Nanosecond 36 Microsecond = lp.Microsecond 37 Millisecond = lp.Millisecond 38 Second = lp.Second 39 ) 40 41 // ConvertPrecisionToV2 map version 1 precision to version 2. 42 func ConvertPrecisionToV2(precision string) (Precision, error) { 43 switch precision { 44 case "u": 45 return Microsecond, nil 46 case "ms": 47 return Millisecond, nil 48 case "s": 49 return Second, nil 50 case "m": 51 return 0, fmt.Errorf("line protocol v2 precision do not support minute") 52 case "h": 53 return 0, fmt.Errorf("line protocol v2 precision do not support hour") 54 default: 55 return Nanosecond, nil 56 } 57 } 58 59 type Point struct { 60 Name string 61 Tags map[string]string 62 Fields map[string]interface{} 63 Time time.Time 64 } 65 66 func NewPoint(name string, 67 tags map[string]string, 68 fields map[string]interface{}, 69 t ...time.Time, 70 ) (*Point, error) { 71 var tm time.Time 72 if len(t) > 0 { 73 tm = t[0] 74 } 75 return &Point{ 76 Name: name, 77 Tags: tags, 78 Fields: fields, 79 Time: tm, 80 }, nil 81 } 82 83 func (p *Point) ToInfluxdbPoint(defaultTime time.Time) (models.Point, error) { 84 tm := p.Time 85 if tm.IsZero() { 86 tm = defaultTime 87 } 88 return models.NewPoint(p.Name, models.NewTags(p.Tags), p.Fields, tm) 89 } 90 91 func (p *Point) AddTag(key, val string) { 92 p.Tags[key] = val 93 } 94 95 func (p *Point) AddField(key string, val interface{}) error { 96 // check the val value 97 if _, ok := InterfaceToValue(val); !ok { 98 return fmt.Errorf("unsupported line protocol field interface{}: %T, [%v]", val, val) 99 } 100 p.Fields[key] = val 101 return nil 102 } 103 104 func (p *Point) String() (string, error) { 105 encoder := NewLineEncoder() 106 107 if err := encoder.AppendPoint(p); err != nil { 108 return "", fmt.Errorf("encoder append point err: %w", err) 109 } 110 111 line, err := encoder.UnsafeStringWithoutLn() 112 if err != nil { 113 return "", fmt.Errorf("line protocol encoding err: %w", err) 114 } 115 return line, nil 116 } 117 118 func ParseWithOptionSetter(data []byte, optionSetters ...OptionSetter) ([]*Point, error) { 119 option := DefaultOption 120 if len(optionSetters) > 0 { 121 for _, setter := range optionSetters { 122 setter(option) 123 } 124 } 125 return Parse(data, option) 126 } 127 128 func Parse(data []byte, opt *Option) ([]*Point, error) { 129 if len(data) == 0 { 130 return nil, fmt.Errorf("empty data") 131 } 132 133 if opt == nil { 134 opt = DefaultOption 135 } 136 137 if opt.MaxFields <= 0 { 138 opt.MaxFields = 1024 139 } 140 141 if opt.MaxTags <= 0 { 142 opt.MaxTags = 256 143 } 144 145 ptTime := opt.Time 146 if opt.Time.IsZero() { 147 ptTime = time.Now() 148 } 149 150 dec := lp.NewDecoderWithBytes(data) 151 152 var pts []*Point 153 154 for dec.Next() { 155 pt := &Point{ 156 Tags: make(map[string]string), 157 Fields: make(map[string]interface{}), 158 } 159 160 m, err := dec.Measurement() 161 if err != nil { 162 return pts, fmt.Errorf("parse measurement err: %w", err) 163 } 164 165 pt.Name = unsafeBytesToString(m) 166 167 for { 168 k, v, err := dec.NextTag() 169 if err != nil { 170 return pts, fmt.Errorf("read next tag err: %w", err) 171 } 172 173 if k == nil { 174 break 175 } 176 177 pt.Tags[unsafeBytesToString(k)] = unsafeBytesToString(v) 178 } 179 180 for { 181 k, v, err := dec.NextField() 182 if err != nil { 183 return pts, fmt.Errorf("get next field err: %w", err) 184 } 185 186 if k == nil { 187 break 188 } 189 190 pt.Fields[unsafeBytesToString(k)] = v.Interface() 191 } 192 193 t, err := dec.Time(opt.PrecisionV2, ptTime) 194 if err != nil { 195 return pts, err 196 } 197 198 pt.Time = t 199 200 if opt.ExtraTags != nil { 201 for k, v := range opt.ExtraTags { 202 if _, ok := pt.Tags[k]; !ok { 203 pt.AddTag(k, v) 204 } 205 } 206 } 207 208 if opt.CallbackV2 != nil { 209 newPoint, err := opt.CallbackV2(pt) 210 if err != nil { 211 return nil, fmt.Errorf("call callbackv2 err: %w", err) 212 } 213 pt = newPoint 214 } 215 216 if pt == nil { 217 return nil, fmt.Errorf("line point is empty") 218 } 219 220 if err := checkPointV2(pt, opt); err != nil { 221 return nil, err 222 } 223 224 pts = append(pts, pt) 225 } 226 227 return pts, nil 228 } 229 230 func InterfaceToValue(x interface{}) (lp.Value, bool) { 231 switch y := x.(type) { 232 case int64, uint64, float64, bool, string, []byte: 233 // do nothing 234 case int: 235 x = int64(y) 236 case rune: 237 x = int64(y) 238 case int16: 239 x = int64(y) 240 case int8: 241 x = int64(y) 242 case uint: 243 x = uint64(y) 244 case uint32: 245 x = int64(y) 246 case uint16: 247 x = int64(y) 248 case byte: 249 x = int64(y) 250 case float32: 251 x = float64(y) 252 default: 253 k := reflect.ValueOf(y).Kind() 254 if k == reflect.Slice || k == reflect.Map || k == reflect.Array || 255 k == reflect.Struct || k == reflect.Ptr { 256 x = fmt.Sprintf("%v", y) 257 } 258 } 259 return lp.NewValue(x) 260 } 261 262 type LineEncoder struct { 263 Encoder *lp.Encoder 264 Opt *Option 265 } 266 267 func NewLineEncoder(optionSetters ...OptionSetter) *LineEncoder { 268 opt := DefaultOption 269 if len(optionSetters) > 0 { 270 for _, setter := range optionSetters { 271 setter(opt) 272 } 273 } 274 275 encoder := &lp.Encoder{} 276 encoder.SetPrecision(opt.PrecisionV2) 277 278 return &LineEncoder{ 279 Encoder: encoder, 280 Opt: opt, 281 } 282 } 283 284 func (le *LineEncoder) EnableLax() { 285 le.Encoder.SetLax(true) 286 } 287 288 func (le *LineEncoder) AppendPoint(pt *Point) error { 289 le.Encoder.StartLine(pt.Name) 290 291 maxLen := len(pt.Tags) 292 if len(pt.Fields) > maxLen { 293 maxLen = len(pt.Fields) 294 } 295 296 keys := make([]string, 0, maxLen) 297 298 for key, val := range pt.Tags { 299 if val == "" { 300 continue 301 } 302 keys = append(keys, key) 303 } 304 305 sort.Strings(keys) 306 for _, key := range keys { 307 le.Encoder.AddTag(key, pt.Tags[key]) 308 } 309 310 if len(pt.Fields) == 0 { 311 return models.ErrPointMustHaveAField 312 } 313 keys = keys[:0] 314 for key := range pt.Fields { 315 keys = append(keys, key) 316 } 317 318 sort.Strings(keys) 319 for _, key := range keys { 320 value, ok := InterfaceToValue(pt.Fields[key]) 321 if !ok { 322 return fmt.Errorf("unable parse value from interface{}: %T, [%v]", pt.Fields[key], pt.Fields[key]) 323 } 324 le.Encoder.AddField(key, value) 325 } 326 327 le.Encoder.EndLine(pt.Time) 328 329 return le.Encoder.Err() 330 } 331 332 // Bytes return the line protocol bytes 333 // You should be **VERY CAREFUL** when using this function together with the Reset. 334 func (le *LineEncoder) Bytes() ([]byte, error) { 335 return le.Encoder.Bytes(), le.Encoder.Err() 336 } 337 338 // BytesWithoutLn return the line protocol bytes without the trailing new line 339 // You should be **VERY CAREFUL** when using this function together with the Reset. 340 func (le *LineEncoder) BytesWithoutLn() ([]byte, error) { 341 return bytes.TrimRightFunc(le.Encoder.Bytes(), func(r rune) bool { 342 return r == '\r' || r == '\n' 343 }), le.Encoder.Err() 344 } 345 346 // UnsafeString return string with no extra allocation 347 // You should be **VERY CAREFUL** when using this function together with the Reset. 348 func (le *LineEncoder) UnsafeString() (string, error) { 349 lineBytes, err := le.Bytes() 350 if len(lineBytes) > 0 { 351 return *(*string)(unsafe.Pointer(&lineBytes)), err //nolint:gosec 352 } 353 return "", err 354 } 355 356 // UnsafeStringWithoutLn return the line protocol **UNSAFE** string without the trailing new line 357 // You should be **VERY CAREFUL** when using this function together with the Reset. 358 func (le *LineEncoder) UnsafeStringWithoutLn() (string, error) { 359 lineBytes, err := le.BytesWithoutLn() 360 if len(lineBytes) > 0 { 361 return *(*string)(unsafe.Pointer(&lineBytes)), err //nolint:gosec 362 } 363 return "", err 364 } 365 366 func (le *LineEncoder) Reset() { 367 le.Encoder.Reset() 368 } 369 370 func (le *LineEncoder) SetBuffer(buf []byte) { 371 le.Encoder.SetBuffer(buf) 372 } 373 374 func Encode(pts []*Point, opt ...OptionSetter) ([]byte, error) { 375 enc := NewLineEncoder(opt...) 376 377 for _, pt := range pts { 378 err := enc.AppendPoint(pt) 379 if err != nil { 380 return nil, fmt.Errorf("encoder append point err: %w", err) 381 } 382 } 383 384 return enc.Bytes() 385 }