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  }