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

     1  // SPDX-License-Identifier: GPL-3.0-or-later
     2  
     3  package logs
     4  
     5  import (
     6  	"bufio"
     7  	"fmt"
     8  	"io"
     9  	"strconv"
    10  
    11  	"github.com/valyala/fastjson"
    12  )
    13  
    14  type JSONConfig struct {
    15  	Mapping map[string]string `yaml:"mapping"`
    16  }
    17  
    18  type JSONParser struct {
    19  	reader  *bufio.Reader
    20  	parser  fastjson.Parser
    21  	buf     []byte
    22  	mapping map[string]string
    23  }
    24  
    25  func NewJSONParser(config JSONConfig, in io.Reader) (*JSONParser, error) {
    26  	parser := &JSONParser{
    27  		reader:  bufio.NewReader(in),
    28  		mapping: config.Mapping,
    29  		buf:     make([]byte, 0, 100),
    30  	}
    31  	return parser, nil
    32  }
    33  
    34  func (p *JSONParser) ReadLine(line LogLine) error {
    35  	row, err := p.reader.ReadSlice('\n')
    36  	if err != nil && len(row) == 0 {
    37  		return err
    38  	}
    39  	if len(row) > 0 && row[len(row)-1] == '\n' {
    40  		row = row[:len(row)-1]
    41  	}
    42  	return p.Parse(row, line)
    43  }
    44  
    45  func (p *JSONParser) Parse(row []byte, line LogLine) error {
    46  	val, err := p.parser.ParseBytes(row)
    47  	if err != nil {
    48  		return err
    49  	}
    50  
    51  	if err := p.parseObject("", val, line); err != nil {
    52  		return &ParseError{msg: fmt.Sprintf("json parse: %v", err), err: err}
    53  	}
    54  
    55  	return nil
    56  }
    57  
    58  func (p *JSONParser) parseObject(prefix string, val *fastjson.Value, line LogLine) error {
    59  	obj, err := val.Object()
    60  	if err != nil {
    61  		return err
    62  	}
    63  
    64  	obj.Visit(func(key []byte, v *fastjson.Value) {
    65  		if err != nil {
    66  			return
    67  		}
    68  
    69  		k := jsonObjKey(prefix, string(key))
    70  
    71  		switch v.Type() {
    72  		case fastjson.TypeString, fastjson.TypeNumber:
    73  			err = p.parseStringNumber(k, v, line)
    74  		case fastjson.TypeArray:
    75  			err = p.parseArray(k, v, line)
    76  		case fastjson.TypeObject:
    77  			err = p.parseObject(k, v, line)
    78  		default:
    79  			return
    80  		}
    81  	})
    82  
    83  	return err
    84  }
    85  
    86  func jsonObjKey(prefix, key string) string {
    87  	if prefix == "" {
    88  		return key
    89  	}
    90  	return prefix + "." + key
    91  }
    92  
    93  func (p *JSONParser) parseArray(key string, val *fastjson.Value, line LogLine) error {
    94  	arr, err := val.Array()
    95  	if err != nil {
    96  		return err
    97  	}
    98  
    99  	for i, v := range arr {
   100  		k := jsonObjKey(key, strconv.Itoa(i))
   101  
   102  		switch v.Type() {
   103  		case fastjson.TypeString, fastjson.TypeNumber:
   104  			err = p.parseStringNumber(k, v, line)
   105  		case fastjson.TypeArray:
   106  			err = p.parseArray(k, v, line)
   107  		case fastjson.TypeObject:
   108  			err = p.parseObject(k, v, line)
   109  		default:
   110  			continue
   111  		}
   112  
   113  		if err != nil {
   114  			return err
   115  		}
   116  	}
   117  
   118  	return err
   119  }
   120  
   121  func (p *JSONParser) parseStringNumber(key string, val *fastjson.Value, line LogLine) error {
   122  	if mapped, ok := p.mapping[key]; ok {
   123  		key = mapped
   124  	}
   125  
   126  	p.buf = p.buf[:0]
   127  	if p.buf = val.MarshalTo(p.buf); len(p.buf) == 0 {
   128  		return nil
   129  	}
   130  
   131  	if val.Type() == fastjson.TypeString {
   132  		// trim "
   133  		return line.Assign(key, string(p.buf[1:len(p.buf)-1]))
   134  	}
   135  	return line.Assign(key, string(p.buf))
   136  }
   137  
   138  func (p *JSONParser) Info() string {
   139  	return fmt.Sprintf("json: %q", p.mapping)
   140  }