Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lokiexporter: support serialization of all body types into JSON #6639

Merged
merged 2 commits into from
Dec 10, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 55 additions & 7 deletions exporter/lokiexporter/encode_json.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,43 @@ import (

type lokiEntry struct {
Name string `json:"name,omitempty"`
Body string `json:"body,omitempty"`
Body json.RawMessage `json:"body,omitempty"`
TraceID string `json:"traceid,omitempty"`
SpanID string `json:"spanid,omitempty"`
Severity string `json:"severity,omitempty"`
Attributes map[string]interface{} `json:"attributes,omitempty"`
Resources map[string]interface{} `json:"resources,omitempty"`
}

func serializeBody(body pdata.AttributeValue) (string, error) {
str := ""
func serializeBody(body pdata.AttributeValue) ([]byte, error) {
var str []byte
var err error
if body.Type() == pdata.AttributeValueTypeString {
str = body.StringVal()
} else {
switch body.Type() {
case pdata.AttributeValueTypeEmpty:
// no body

case pdata.AttributeValueTypeString:
str, err = json.Marshal(body.StringVal())

case pdata.AttributeValueTypeInt:
str, err = json.Marshal(body.IntVal())

case pdata.AttributeValueTypeDouble:
str, err = json.Marshal(body.DoubleVal())

case pdata.AttributeValueTypeBool:
str, err = json.Marshal(body.BoolVal())

case pdata.AttributeValueTypeMap:
str, err = json.Marshal(body.MapVal().AsRaw())

case pdata.AttributeValueTypeArray:
str, err = json.Marshal(attributeValueSliceAsRaw(body.SliceVal()))

case pdata.AttributeValueTypeBytes:
str, err = json.Marshal(body.BytesVal())

default:
err = fmt.Errorf("unsuported body type to serialize")
}
return str, err
Expand All @@ -48,7 +71,7 @@ func encodeJSON(lr pdata.LogRecord, res pdata.Resource) (string, error) {
var logRecord lokiEntry
var jsonRecord []byte
var err error
var body string
var body []byte

body, err = serializeBody(lr.Body())
if err != nil {
Expand All @@ -71,3 +94,28 @@ func encodeJSON(lr pdata.LogRecord, res pdata.Resource) (string, error) {
}
return string(jsonRecord), nil
}

// Copied from pdata (es AttributeValueSlice) asRaw() since its not exported
func attributeValueSliceAsRaw(es pdata.AttributeValueSlice) []interface{} {
rawSlice := make([]interface{}, 0, es.Len())
for i := 0; i < es.Len(); i++ {
v := es.At(i)
switch v.Type() {
case pdata.AttributeValueTypeString:
rawSlice = append(rawSlice, v.StringVal())
case pdata.AttributeValueTypeInt:
rawSlice = append(rawSlice, v.IntVal())
case pdata.AttributeValueTypeDouble:
rawSlice = append(rawSlice, v.DoubleVal())
case pdata.AttributeValueTypeBool:
rawSlice = append(rawSlice, v.BoolVal())
case pdata.AttributeValueTypeBytes:
rawSlice = append(rawSlice, v.BytesVal())
case pdata.AttributeValueTypeEmpty:
rawSlice = append(rawSlice, nil)
default:
rawSlice = append(rawSlice, "<Invalid array value>")
}
}
return rawSlice
}
75 changes: 62 additions & 13 deletions exporter/lokiexporter/encode_json_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,29 +38,78 @@ func exampleLog() (pdata.LogRecord, pdata.Resource) {
return buffer, resource
}

func exampleJSON() string {
jsonExample := `{"name":"name","body":"Example log","traceid":"01020304000000000000000000000000","spanid":"0506070800000000","severity":"error","attributes":{"attr1":"1","attr2":"2"},"resources":{"host.name":"something"}}`
return jsonExample
}
func TestConvertWithStringBody(t *testing.T) {
in := `{"name":"name","body":"Example log","traceid":"01020304000000000000000000000000","spanid":"0506070800000000","severity":"error","attributes":{"attr1":"1","attr2":"2"},"resources":{"host.name":"something"}}`

func TestConvertString(t *testing.T) {
in := exampleJSON()
out, err := encodeJSON(exampleLog())
t.Log(in)
t.Log(out, err)
assert.NoError(t, err)
assert.Equal(t, in, out)
}

func TestConvertNonString(t *testing.T) {
in := exampleJSON()
func TestConvertWithMapBody(t *testing.T) {
in := `{"name":"name","body":{"key1":"value","key2":"value"},"traceid":"01020304000000000000000000000000","spanid":"0506070800000000","severity":"error","attributes":{"attr1":"1","attr2":"2"},"resources":{"host.name":"something"}}`

log, resource := exampleLog()
mapVal := pdata.NewAttributeValueMap()
mapVal.MapVal().Insert("key1", pdata.NewAttributeValueString("value"))
mapVal.MapVal().Insert("key2", pdata.NewAttributeValueString("value"))
mapVal.CopyTo(log.Body())

out, err := encodeJSON(log, resource)
t.Log(in)
t.Log(out, err)
assert.EqualError(t, err, "unsuported body type to serialize")
assert.NoError(t, err)
assert.Equal(t, in, out)
}

func TestSerializeBody(t *testing.T) {

mapval := pdata.NewAttributeValueMap()
mapval.MapVal().InsertString("key", "val")

arrayval := pdata.NewAttributeValueArray()
arrayval.SliceVal().AppendEmpty().SetStringVal("a")
arrayval.SliceVal().AppendEmpty().SetStringVal("b")

testcases := []struct {
input pdata.AttributeValue
expected []byte
}{
{
pdata.NewAttributeValueEmpty(),
nil,
},
{
pdata.NewAttributeValueString("a"),
[]byte(`"a"`),
},
{
pdata.NewAttributeValueInt(1),
[]byte(`1`),
},
{
pdata.NewAttributeValueDouble(1.1),
[]byte(`1.1`),
},
{
pdata.NewAttributeValueBool(true),
[]byte(`true`),
},
{
mapval,
[]byte(`{"key":"val"}`),
},
{
arrayval,
[]byte(`["a","b"]`),
},
{
pdata.NewAttributeValueBytes([]byte(`abc`)),
[]byte(`"YWJj"`),
},
}

for _, test := range testcases {
out, err := serializeBody(test.input)
assert.NoError(t, err)
assert.Equal(t, test.expected, out)
}
}