Add event method RawCBOR analogous to RawJSON (#556)
CBOR is encoded as data-url in JSON encoding and tagged in CBOR encoding.
This commit is contained in:
parent
b662f088b9
commit
9070d49a1a
|
@ -24,6 +24,9 @@ func init() {
|
||||||
func appendJSON(dst []byte, j []byte) []byte {
|
func appendJSON(dst []byte, j []byte) []byte {
|
||||||
return cbor.AppendEmbeddedJSON(dst, j)
|
return cbor.AppendEmbeddedJSON(dst, j)
|
||||||
}
|
}
|
||||||
|
func appendCBOR(dst []byte, c []byte) []byte {
|
||||||
|
return cbor.AppendEmbeddedCBOR(dst, c)
|
||||||
|
}
|
||||||
|
|
||||||
// decodeIfBinaryToString - converts a binary formatted log msg to a
|
// decodeIfBinaryToString - converts a binary formatted log msg to a
|
||||||
// JSON formatted String Log message.
|
// JSON formatted String Log message.
|
||||||
|
|
|
@ -6,6 +6,7 @@ package zerolog
|
||||||
// JSON encoded byte stream.
|
// JSON encoded byte stream.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
"github.com/rs/zerolog/internal/json"
|
"github.com/rs/zerolog/internal/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -25,6 +26,17 @@ func init() {
|
||||||
func appendJSON(dst []byte, j []byte) []byte {
|
func appendJSON(dst []byte, j []byte) []byte {
|
||||||
return append(dst, j...)
|
return append(dst, j...)
|
||||||
}
|
}
|
||||||
|
func appendCBOR(dst []byte, cbor []byte) []byte {
|
||||||
|
dst = append(dst, []byte("\"data:application/cbor;base64,")...)
|
||||||
|
l := len(dst)
|
||||||
|
enc := base64.StdEncoding
|
||||||
|
n := enc.EncodedLen(len(cbor))
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
dst = append(dst, '.')
|
||||||
|
}
|
||||||
|
enc.Encode(dst[l:], cbor)
|
||||||
|
return append(dst, '"')
|
||||||
|
}
|
||||||
|
|
||||||
func decodeIfBinaryToString(in []byte) string {
|
func decodeIfBinaryToString(in []byte) string {
|
||||||
return string(in)
|
return string(in)
|
||||||
|
|
12
event.go
12
event.go
|
@ -318,6 +318,18 @@ func (e *Event) RawJSON(key string, b []byte) *Event {
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RawCBOR adds already encoded CBOR to the log line under key.
|
||||||
|
//
|
||||||
|
// No sanity check is performed on b
|
||||||
|
// Note: The full featureset of CBOR is supported as data will not be mapped to json but stored as data-url
|
||||||
|
func (e *Event) RawCBOR(key string, b []byte) *Event {
|
||||||
|
if e == nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
e.buf = appendCBOR(enc.AppendKey(e.buf, key), b)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
// AnErr adds the field key with serialized err to the *Event context.
|
// AnErr adds the field key with serialized err to the *Event context.
|
||||||
// If err is nil, no field is added.
|
// If err is nil, no field is added.
|
||||||
func (e *Event) AnErr(key string, err error) *Event {
|
func (e *Event) AnErr(key string, err error) *Event {
|
||||||
|
|
|
@ -27,6 +27,7 @@ const (
|
||||||
|
|
||||||
// Tag Sub-types.
|
// Tag Sub-types.
|
||||||
additionalTypeTimestamp byte = 01
|
additionalTypeTimestamp byte = 01
|
||||||
|
additionalTypeEmbeddedCBOR byte = 63
|
||||||
|
|
||||||
// Extended Tags - from https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml
|
// Extended Tags - from https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml
|
||||||
additionalTypeTagNetworkAddr uint16 = 260
|
additionalTypeTagNetworkAddr uint16 = 260
|
||||||
|
|
|
@ -5,6 +5,7 @@ package cbor
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
|
@ -213,6 +214,31 @@ func decodeString(src *bufio.Reader, noQuotes bool) []byte {
|
||||||
}
|
}
|
||||||
return append(result, '"')
|
return append(result, '"')
|
||||||
}
|
}
|
||||||
|
func decodeStringToDataUrl(src *bufio.Reader, mimeType string) []byte {
|
||||||
|
pb := readByte(src)
|
||||||
|
major := pb & maskOutAdditionalType
|
||||||
|
minor := pb & maskOutMajorType
|
||||||
|
if major != majorTypeByteString {
|
||||||
|
panic(fmt.Errorf("Major type is: %d in decodeString", major))
|
||||||
|
}
|
||||||
|
length := decodeIntAdditionalType(src, minor)
|
||||||
|
l := int(length)
|
||||||
|
enc := base64.StdEncoding
|
||||||
|
lEnc := enc.EncodedLen(l)
|
||||||
|
result := make([]byte, len("\"data:;base64,\"")+len(mimeType)+lEnc)
|
||||||
|
dest := result
|
||||||
|
u := copy(dest, "\"data:")
|
||||||
|
dest = dest[u:]
|
||||||
|
u = copy(dest, mimeType)
|
||||||
|
dest = dest[u:]
|
||||||
|
u = copy(dest, ";base64,")
|
||||||
|
dest = dest[u:]
|
||||||
|
pbs := readNBytes(src, l)
|
||||||
|
enc.Encode(dest, pbs)
|
||||||
|
dest = dest[lEnc:]
|
||||||
|
dest[0] = '"'
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
func decodeUTF8String(src *bufio.Reader) []byte {
|
func decodeUTF8String(src *bufio.Reader) []byte {
|
||||||
pb := readByte(src)
|
pb := readByte(src)
|
||||||
|
@ -349,6 +375,20 @@ func decodeTagData(src *bufio.Reader) []byte {
|
||||||
switch minor {
|
switch minor {
|
||||||
case additionalTypeTimestamp:
|
case additionalTypeTimestamp:
|
||||||
return decodeTimeStamp(src)
|
return decodeTimeStamp(src)
|
||||||
|
case additionalTypeIntUint8:
|
||||||
|
val := decodeIntAdditionalType(src, minor)
|
||||||
|
switch byte(val) {
|
||||||
|
case additionalTypeEmbeddedCBOR:
|
||||||
|
pb := readByte(src)
|
||||||
|
dataMajor := pb & maskOutAdditionalType
|
||||||
|
if dataMajor != majorTypeByteString {
|
||||||
|
panic(fmt.Errorf("Unsupported embedded Type: %d in decodeEmbeddedCBOR", dataMajor))
|
||||||
|
}
|
||||||
|
src.UnreadByte()
|
||||||
|
return decodeStringToDataUrl(src, "application/cbor")
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("Unsupported Additional Tag Type: %d in decodeTagData", val))
|
||||||
|
}
|
||||||
|
|
||||||
// Tag value is larger than 256 (so uint16).
|
// Tag value is larger than 256 (so uint16).
|
||||||
case additionalTypeIntUint16:
|
case additionalTypeIntUint16:
|
||||||
|
|
|
@ -93,3 +93,25 @@ func AppendEmbeddedJSON(dst, s []byte) []byte {
|
||||||
}
|
}
|
||||||
return append(dst, s...)
|
return append(dst, s...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AppendEmbeddedCBOR adds a tag and embeds input CBOR as such.
|
||||||
|
func AppendEmbeddedCBOR(dst, s []byte) []byte {
|
||||||
|
major := majorTypeTags
|
||||||
|
minor := additionalTypeEmbeddedCBOR
|
||||||
|
|
||||||
|
// Append the TAG to indicate this is Embedded JSON.
|
||||||
|
dst = append(dst, major|additionalTypeIntUint8)
|
||||||
|
dst = append(dst, minor)
|
||||||
|
|
||||||
|
// Append the CBOR Object as Byte String.
|
||||||
|
major = majorTypeByteString
|
||||||
|
|
||||||
|
l := len(s)
|
||||||
|
if l <= additionalMax {
|
||||||
|
lb := byte(l)
|
||||||
|
dst = append(dst, major|lb)
|
||||||
|
} else {
|
||||||
|
dst = appendCborTypePrefix(dst, major, uint64(l))
|
||||||
|
}
|
||||||
|
return append(dst, s...)
|
||||||
|
}
|
||||||
|
|
|
@ -320,6 +320,7 @@ func TestFields(t *testing.T) {
|
||||||
Bytes("bytes", []byte("bar")).
|
Bytes("bytes", []byte("bar")).
|
||||||
Hex("hex", []byte{0x12, 0xef}).
|
Hex("hex", []byte{0x12, 0xef}).
|
||||||
RawJSON("json", []byte(`{"some":"json"}`)).
|
RawJSON("json", []byte(`{"some":"json"}`)).
|
||||||
|
RawCBOR("cbor", []byte{0x83, 0x01, 0x82, 0x02, 0x03, 0x82, 0x04, 0x05}).
|
||||||
Func(func(e *Event) { e.Str("func", "func_output") }).
|
Func(func(e *Event) { e.Str("func", "func_output") }).
|
||||||
AnErr("some_err", nil).
|
AnErr("some_err", nil).
|
||||||
Err(errors.New("some error")).
|
Err(errors.New("some error")).
|
||||||
|
@ -344,7 +345,7 @@ func TestFields(t *testing.T) {
|
||||||
Time("time", time.Time{}).
|
Time("time", time.Time{}).
|
||||||
TimeDiff("diff", now, now.Add(-10*time.Second)).
|
TimeDiff("diff", now, now.Add(-10*time.Second)).
|
||||||
Msg("")
|
Msg("")
|
||||||
if got, want := decodeIfBinaryToString(out.Bytes()), `{"caller":"`+caller+`","string":"foo","stringer":"127.0.0.1","stringer_nil":null,"bytes":"bar","hex":"12ef","json":{"some":"json"},"func":"func_output","error":"some error","bool":true,"int":1,"int8":2,"int16":3,"int32":4,"int64":5,"uint":6,"uint8":7,"uint16":8,"uint32":9,"uint64":10,"IPv4":"192.168.0.100","IPv6":"2001:db8:85a3::8a2e:370:7334","Mac":"00:14:22:01:23:45","Prefix":"192.168.0.100/24","float32":11.1234,"float64":12.321321321,"dur":1000,"time":"0001-01-01T00:00:00Z","diff":10000}`+"\n"; got != want {
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"caller":"`+caller+`","string":"foo","stringer":"127.0.0.1","stringer_nil":null,"bytes":"bar","hex":"12ef","json":{"some":"json"},"cbor":"data:application/cbor;base64,gwGCAgOCBAU=","func":"func_output","error":"some error","bool":true,"int":1,"int8":2,"int16":3,"int32":4,"int64":5,"uint":6,"uint8":7,"uint16":8,"uint32":9,"uint64":10,"IPv4":"192.168.0.100","IPv6":"2001:db8:85a3::8a2e:370:7334","Mac":"00:14:22:01:23:45","Prefix":"192.168.0.100/24","float32":11.1234,"float64":12.321321321,"dur":1000,"time":"0001-01-01T00:00:00Z","diff":10000}`+"\n"; got != want {
|
||||||
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue