github.com/GuanceCloud/cliutils@v1.1.21/point/decode.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  	"encoding/json"
    10  	sync "sync"
    11  	"time"
    12  )
    13  
    14  var decPool sync.Pool
    15  
    16  // DecodeFn used to iterate on []*Point payload, if error returned, the iterate terminated.
    17  type DecodeFn func([]*Point) error
    18  
    19  type DecoderOption func(e *Decoder)
    20  
    21  func WithDecEncoding(enc Encoding) DecoderOption {
    22  	return func(d *Decoder) { d.enc = enc }
    23  }
    24  
    25  func WithDecFn(fn DecodeFn) DecoderOption {
    26  	return func(d *Decoder) { d.fn = fn }
    27  }
    28  
    29  func WithDecEasyproto(on bool) DecoderOption {
    30  	return func(d *Decoder) { d.easyproto = on }
    31  }
    32  
    33  type Decoder struct {
    34  	enc Encoding
    35  	fn  DecodeFn
    36  
    37  	easyproto bool
    38  
    39  	// For line-protocol parsing, keep original error.
    40  	detailedError error
    41  }
    42  
    43  func GetDecoder(opts ...DecoderOption) *Decoder {
    44  	v := decPool.Get()
    45  	if v == nil {
    46  		v = newDecoder()
    47  	}
    48  
    49  	x := v.(*Decoder)
    50  
    51  	for _, opt := range opts {
    52  		if opt != nil {
    53  			opt(x)
    54  		}
    55  	}
    56  
    57  	return x
    58  }
    59  
    60  func PutDecoder(d *Decoder) {
    61  	d.reset()
    62  	decPool.Put(d)
    63  }
    64  
    65  func newDecoder() *Decoder {
    66  	return &Decoder{}
    67  }
    68  
    69  func (d *Decoder) reset() {
    70  	d.enc = 0
    71  	d.fn = nil
    72  	d.detailedError = nil
    73  	d.easyproto = false
    74  }
    75  
    76  func detectTimestampPrecision(ts int64) int64 {
    77  	if ts/1e9 < 10 { // sec
    78  		return ts * int64(time.Second)
    79  	} else if ts/1e12 < 10 { // milli-sec
    80  		return ts * int64(time.Millisecond)
    81  	} else if ts/1e15 < 10 { // micro-sec
    82  		return ts * int64(time.Microsecond)
    83  	} else { // nano-sec
    84  		return ts
    85  	}
    86  }
    87  
    88  func (d *Decoder) doDecode(data []byte, c *cfg) ([]*Point, error) {
    89  	var (
    90  		pts []*Point
    91  		err error
    92  	)
    93  
    94  	switch d.enc {
    95  	case JSON:
    96  		if err := json.Unmarshal(data, &pts); err != nil {
    97  			return nil, err
    98  		}
    99  
   100  	case Protobuf:
   101  		if d.easyproto {
   102  			pts, err = unmarshalPoints(data)
   103  			if err != nil {
   104  				return nil, err
   105  			}
   106  		} else {
   107  			var pbpts PBPoints
   108  			if err = pbpts.Unmarshal(data); err != nil {
   109  				return nil, err
   110  			}
   111  
   112  			for _, pbpt := range pbpts.Arr {
   113  				// NOTE: under gogo Unmarshal, nothing comes from point pool, so
   114  				// we create Point without NewPointV2(), and make the point escaped
   115  				// from point pool(if defaultPTPool set).
   116  				//
   117  				// Although put back points that not originally from the pool is
   118  				// possible, we still distinguish this behavior for better
   119  				// observability of actions(these escaped point counter are export
   120  				// by metrics).
   121  				pt := &Point{
   122  					pt: pbpt,
   123  				}
   124  				pt.SetFlag(Ppb)
   125  				pts = append(pts, pt)
   126  			}
   127  		}
   128  
   129  	case LineProtocol:
   130  		pts, err = parseLPPoints(data, c)
   131  		if err != nil {
   132  			d.detailedError = err
   133  			return nil, simplifyLPError(err)
   134  		}
   135  	}
   136  
   137  	return pts, err
   138  }
   139  
   140  func decodeAdjustPoints(pts []*Point, c *cfg) ([]*Point, error) {
   141  	var (
   142  		chk       *checker
   143  		newPoints []*Point
   144  
   145  		// set point's default timestamp
   146  		nowNano = c.timestamp
   147  	)
   148  
   149  	if nowNano == 0 { // not set
   150  		nowNano = time.Now().UnixNano()
   151  	}
   152  
   153  	if c.precheck {
   154  		chk = &checker{cfg: c}
   155  	}
   156  
   157  	// adjust and check the point.
   158  	for idx, pt := range pts {
   159  		// use current time
   160  		if pt.pt.Time == 0 {
   161  			pt.pt.Time = nowNano
   162  		} else { // adjust point's timestamp
   163  			switch c.precision {
   164  			case PrecDyn:
   165  				pt.pt.Time = detectTimestampPrecision(pt.pt.Time)
   166  			case PrecUS:
   167  				pt.pt.Time *= int64(time.Microsecond)
   168  			case PrecMS:
   169  				pt.pt.Time *= int64(time.Millisecond)
   170  			case PrecS:
   171  				pt.pt.Time *= int64(time.Second)
   172  			case PrecM:
   173  				pt.pt.Time *= int64(time.Minute)
   174  			case PrecH:
   175  				pt.pt.Time *= int64(time.Hour)
   176  			case PrecNS: // pass
   177  			case PrecW, PrecD: // not used
   178  			default: // pass
   179  			}
   180  		}
   181  
   182  		if c.precheck {
   183  			pts[idx] = chk.check(pts[idx])
   184  			chk.reset()
   185  		}
   186  
   187  		// Applied the callback on each point, the callback used to check if the
   188  		// point is valid for usage, for example:
   189  		//  - Is the measurement name are expected?
   190  		//  - Is point's key or value are expected?
   191  		//  - Is any warning on the point?
   192  		//  - ...
   193  		if c.callback != nil {
   194  			if x, err := c.callback(pts[idx]); err != nil {
   195  				return nil, err
   196  			} else if x != nil {
   197  				newPoints = append(newPoints, x)
   198  			}
   199  		}
   200  	}
   201  
   202  	if len(newPoints) > 0 {
   203  		pts = newPoints
   204  	}
   205  
   206  	return pts, nil
   207  }
   208  
   209  func (d *Decoder) Decode(data []byte, opts ...Option) ([]*Point, error) {
   210  	// point options
   211  	c := GetCfg(opts...)
   212  	defer PutCfg(c)
   213  
   214  	pts, err := d.doDecode(data, c)
   215  	if err != nil {
   216  		return nil, err
   217  	}
   218  
   219  	pts, err = decodeAdjustPoints(pts, c)
   220  	if err != nil {
   221  		return nil, err
   222  	}
   223  
   224  	if d.fn != nil {
   225  		return pts, d.fn(pts)
   226  	}
   227  	return pts, nil
   228  }
   229  
   230  func (d *Decoder) DetailedError() error {
   231  	return d.detailedError
   232  }