github.com/netdata/go.d.plugin@v0.58.1/pkg/logs/ltsv.go (about)

     1  // SPDX-License-Identifier: GPL-3.0-or-later
     2  
     3  package logs
     4  
     5  import (
     6  	"bufio"
     7  	"errors"
     8  	"fmt"
     9  	"io"
    10  	"strconv"
    11  	"unsafe"
    12  
    13  	"github.com/Wing924/ltsv"
    14  )
    15  
    16  type (
    17  	LTSVConfig struct {
    18  		FieldDelimiter string            `yaml:"field_delimiter"`
    19  		ValueDelimiter string            `yaml:"value_delimiter"`
    20  		Mapping        map[string]string `yaml:"mapping"`
    21  	}
    22  
    23  	LTSVParser struct {
    24  		r       *bufio.Reader
    25  		parser  ltsv.Parser
    26  		mapping map[string]string
    27  	}
    28  )
    29  
    30  func NewLTSVParser(config LTSVConfig, in io.Reader) (*LTSVParser, error) {
    31  	p := ltsv.Parser{
    32  		FieldDelimiter: ltsv.DefaultParser.FieldDelimiter,
    33  		ValueDelimiter: ltsv.DefaultParser.ValueDelimiter,
    34  		StrictMode:     false,
    35  	}
    36  	if config.FieldDelimiter != "" {
    37  		if d, err := parseLTSVDelimiter(config.FieldDelimiter); err == nil {
    38  			p.FieldDelimiter = d
    39  		}
    40  	}
    41  	if config.ValueDelimiter != "" {
    42  		if d, err := parseLTSVDelimiter(config.ValueDelimiter); err == nil {
    43  			p.ValueDelimiter = d
    44  		}
    45  	}
    46  	parser := &LTSVParser{
    47  		r:       bufio.NewReader(in),
    48  		parser:  p,
    49  		mapping: config.Mapping,
    50  	}
    51  	return parser, nil
    52  }
    53  
    54  func (p *LTSVParser) ReadLine(line LogLine) error {
    55  	row, err := p.r.ReadSlice('\n')
    56  	if err != nil && len(row) == 0 {
    57  		return err
    58  	}
    59  	if len(row) > 0 && row[len(row)-1] == '\n' {
    60  		row = row[:len(row)-1]
    61  	}
    62  	return p.Parse(row, line)
    63  }
    64  
    65  func (p *LTSVParser) Parse(row []byte, line LogLine) error {
    66  	err := p.parser.ParseLine(row, func(label []byte, value []byte) error {
    67  		s := *(*string)(unsafe.Pointer(&label)) // no alloc, same as in fmt.Builder.String()
    68  		if v, ok := p.mapping[s]; ok {
    69  			s = v
    70  		}
    71  		return line.Assign(s, string(value))
    72  	})
    73  	if err != nil {
    74  		return &ParseError{msg: fmt.Sprintf("ltsv parse: %v", err), err: err}
    75  	}
    76  	return nil
    77  }
    78  
    79  func (p LTSVParser) Info() string {
    80  	return fmt.Sprintf("ltsv: %q", p.mapping)
    81  }
    82  
    83  func parseLTSVDelimiter(s string) (byte, error) {
    84  	if isNumber(s) {
    85  		d, err := strconv.ParseUint(s, 10, 8)
    86  		if err != nil {
    87  			return 0, fmt.Errorf("invalid LTSV delimiter: %v", err)
    88  		}
    89  		return byte(d), nil
    90  	}
    91  	if len(s) != 1 {
    92  		return 0, errors.New("invalid LTSV delimiter: must be a single character")
    93  	}
    94  	return s[0], nil
    95  }