Skip to content

Commit

Permalink
Add support for hex-encoded of byte slice (#42)
Browse files Browse the repository at this point in the history
  • Loading branch information
Max Wolter authored and rs committed Mar 15, 2018
1 parent b62d797 commit 1c575db
Show file tree
Hide file tree
Showing 10 changed files with 139 additions and 8 deletions.
6 changes: 6 additions & 0 deletions array.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ func (a *Array) Bytes(val []byte) *Array {
return a
}

// Hex append the val as a hex string to the array.
func (a *Array) Hex(val []byte) *Array {
a.buf = json.AppendHex(append(a.buf, ','), val)
return a
}

// Err append the err as a string to the array.
func (a *Array) Err(err error) *Array {
a.buf = json.AppendError(append(a.buf, ','), err)
Expand Down
4 changes: 3 additions & 1 deletion array_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ func TestArray(t *testing.T) {
Float32(11).
Float64(12).
Str("a").
Bytes([]byte("b")).
Hex([]byte{0x1f}).
Time(time.Time{}).
Dur(0)
want := `[true,1,2,3,4,5,6,7,8,9,10,11,12,"a","0001-01-01T00:00:00Z",0]`
want := `[true,1,2,3,4,5,6,7,8,9,10,11,12,"a","b","1f","0001-01-01T00:00:00Z",0]`
if got := string(a.write([]byte{})); got != want {
t.Errorf("Array.write()\ngot: %s\nwant: %s", got, want)
}
Expand Down
6 changes: 6 additions & 0 deletions context.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ func (c Context) Bytes(key string, val []byte) Context {
return c
}

// Hex adds the field key with val as a hex string to the logger context.
func (c Context) Hex(key string, val []byte) Context {
c.l.context = json.AppendHex(json.AppendKey(c.l.context, key), val)
return c
}

// RawJSON adds already encoded JSON to context.
//
// No sanity check is performed on b; it must not contain carriage returns and
Expand Down
9 changes: 9 additions & 0 deletions event.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,15 @@ func (e *Event) Bytes(key string, val []byte) *Event {
return e
}

// Hex adds the field key with val as a hex string to the *Event context.
func (e *Event) Hex(key string, val []byte) *Event {
if e == nil {
return e
}
e.buf = json.AppendHex(json.AppendKey(e.buf, key), val)
return e
}

// RawJSON adds already encoded JSON to the log line under key.
//
// No sanity check is performed on b; it must not contain carriage returns and
Expand Down
5 changes: 5 additions & 0 deletions internal/json/base.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package json

// AppendKey appends a new key to the output JSON.
func AppendKey(dst []byte, key string) []byte {
if len(dst) > 1 {
dst = append(dst, ',')
Expand All @@ -8,13 +9,17 @@ func AppendKey(dst []byte, key string) []byte {
return append(dst, ':')
}

// AppendError encodes the error string to json and appends
// the encoded string to the input byte slice.
func AppendError(dst []byte, err error) []byte {
if err == nil {
return append(dst, `null`...)
}
return AppendString(dst, err.Error())
}

// AppendErrors encodes the error strings to json and
// appends the encoded string list to the input byte slice.
func AppendErrors(dst []byte, errs []error) []byte {
if len(errs) == 0 {
return append(dst, '[', ']')
Expand Down
15 changes: 15 additions & 0 deletions internal/json/string.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import "unicode/utf8"

const hex = "0123456789abcdef"

// AppendStrings encodes the input strings to json and
// appends the encoded string list to the input byte slice.
func AppendStrings(dst []byte, vals []string) []byte {
if len(vals) == 0 {
return append(dst, '[', ']')
Expand Down Expand Up @@ -123,6 +125,19 @@ func AppendBytes(dst, s []byte) []byte {
return append(dst, '"')
}

// AppendHex encodes the input bytes to a hex string and appends
// the encoded string to the input byte slice.
//
// The operation loops though each byte and encodes it as hex using
// the hex lookup table.
func AppendHex(dst, s []byte) []byte {
dst = append(dst, '"')
for _, v := range s {
dst = append(dst, hex[v>>4], hex[v&0x0f])
}
return append(dst, '"')
}

// appendBytesComplex is a mirror of the appendStringComplex
// with []byte arg
func appendBytesComplex(dst, s []byte, i int) []byte {
Expand Down
28 changes: 24 additions & 4 deletions internal/json/string_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,18 @@ var encodeStringTests = []struct {
{"emoji \u2764\ufe0f!", `"emoji ❤️!"`},
}

func TestappendString(t *testing.T) {
var encodeHexTests = []struct {
in byte
out string
}{
{0x00, `"00"`},
{0x0f, `"0f"`},
{0x10, `"10"`},
{0xf0, `"f0"`},
{0xff, `"ff"`},
}

func TestAppendString(t *testing.T) {
for _, tt := range encodeStringTests {
b := AppendString([]byte{}, tt.in)
if got, want := string(b), tt.out; got != want {
Expand All @@ -62,7 +73,7 @@ func TestappendString(t *testing.T) {
}
}

func TestappendBytes(t *testing.T) {
func TestAppendBytes(t *testing.T) {
for _, tt := range encodeStringTests {
b := AppendBytes([]byte{}, []byte(tt.in))
if got, want := string(b), tt.out; got != want {
Expand All @@ -71,6 +82,15 @@ func TestappendBytes(t *testing.T) {
}
}

func TestAppendHex(t *testing.T) {
for _, tt := range encodeHexTests {
b := AppendHex([]byte{}, []byte{tt.in})
if got, want := string(b), tt.out; got != want {
t.Errorf("appendHex(%x) = %s, want %s", tt.in, got, want)
}
}
}

func TestStringBytes(t *testing.T) {
t.Parallel()
// Test that encodeState.stringBytes and encodeState.string use the same encoding.
Expand Down Expand Up @@ -108,7 +128,7 @@ func TestStringBytes(t *testing.T) {
}
}

func BenchmarkappendString(b *testing.B) {
func BenchmarkAppendString(b *testing.B) {
tests := map[string]string{
"NoEncoding": `aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`,
"EncodingFirst": `"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`,
Expand All @@ -128,7 +148,7 @@ func BenchmarkappendString(b *testing.B) {
}
}

func BenchmarkappendBytes(b *testing.B) {
func BenchmarkAppendBytes(b *testing.B) {
tests := map[string]string{
"NoEncoding": `aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`,
"EncodingFirst": `"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`,
Expand Down
8 changes: 8 additions & 0 deletions internal/json/time.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@ import (
"time"
)

// AppendTime formats the input time with the given format
// and appends the encoded string to the input byte slice.
func AppendTime(dst []byte, t time.Time, format string) []byte {
if format == "" {
return AppendInt64(dst, t.Unix())
}
return append(t.AppendFormat(append(dst, '"'), format), '"')
}

// AppendTimes converts the input times with the given format
// and appends the encoded string list to the input byte slice.
func AppendTimes(dst []byte, vals []time.Time, format string) []byte {
if format == "" {
return appendUnixTimes(dst, vals)
Expand Down Expand Up @@ -45,13 +49,17 @@ func appendUnixTimes(dst []byte, vals []time.Time) []byte {
return dst
}

// AppendDuration formats the input duration with the given unit & format
// and appends the encoded string to the input byte slice.
func AppendDuration(dst []byte, d time.Duration, unit time.Duration, useInt bool) []byte {
if useInt {
return strconv.AppendInt(dst, int64(d/unit), 10)
}
return AppendFloat64(dst, float64(d)/float64(unit))
}

// AppendDurations formats the input durations with the given unit & format
// and appends the encoded string list to the input byte slice.
func AppendDurations(dst []byte, vals []time.Duration, unit time.Duration, useInt bool) []byte {
if len(vals) == 0 {
return append(dst, '[', ']')
Expand Down
Loading

0 comments on commit 1c575db

Please sign in to comment.