Skip to content

Commit

Permalink
gosimlog: store unknown fields and show in viewer
Browse files Browse the repository at this point in the history
  • Loading branch information
jellevandenhooff committed Dec 2, 2024
1 parent 5c5d227 commit 75f05a2
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 1 deletion.
90 changes: 89 additions & 1 deletion internal/gosimlog/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ package gosimlog
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"log/slog"
"reflect"
"runtime"
"slices"
"strings"
"time"
)

Expand All @@ -15,6 +19,82 @@ type Stackframe struct {
Line int `json:"line"`
}

func collectJsonKeys(typ reflect.Type) map[string]int {
if typ.Kind() != reflect.Struct {
panic("bad kind " + typ.Kind().String())
}

known := make(map[string]int)

for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i)
if field.Anonymous {
panic("anonymous field " + field.Name)
}
value, ok := field.Tag.Lookup("json")
if !ok {
panic("bad struct tag " + field.Tag)
}
if strings.Contains(value, ",") {
panic("bad struct tag value " + value)
}
known[value] = i
}

return known
}

func unmarshalWithExtraFields(data []byte, value any, extra *[]UnknownField, known map[string]int) error {
reflectValue := reflect.ValueOf(value).Elem()
if reflectValue.Kind() != reflect.Struct {
return errors.New("expected ptr to struct value")
}

d := json.NewDecoder(bytes.NewReader(data))
tok, err := d.Token()
if err != nil {
return err
}
if tok != json.Delim('{') {
return errors.New("expected {")
}
for d.More() {
tok, err = d.Token()
if err != nil {
return err
}
key, ok := tok.(string)
if !ok {
return fmt.Errorf("expected string key, got %q", tok)
}
if idx, ok := known[key]; ok {
target := reflectValue.Field(idx).Addr().Interface()
if err := d.Decode(target); err != nil {
return err
}
} else {
var value json.RawMessage
if err := d.Decode(&value); err != nil {
return err
}
*extra = append(*extra, UnknownField{Key: key, Value: string(value)})
}
}
tok, err = d.Token()
if err != nil {
return err
}
if tok != json.Delim('}') {
return errors.New("expected }")
}
return nil
}

type UnknownField struct {
Key string
Value string
}

type Log struct {
Index int `json:"-"`

Expand All @@ -29,7 +109,15 @@ type Log struct {
Goroutine int `json:"goroutine"`
TraceKind string `json:"traceKind"`

// map[string]any for extra fields
Unknown []UnknownField `json:"-"` // for extra fields
}

// sync.Once this?
var knownLogInfo = collectJsonKeys(reflect.TypeFor[Log]())

func (l *Log) UnmarshalJSON(b []byte) error {
type Plain Log
return unmarshalWithExtraFields(b, (*Plain)(l), &l.Unknown, knownLogInfo)
}

func ParseLog(logs []byte) []*Log {
Expand Down
48 changes: 48 additions & 0 deletions internal/gosimlog/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package gosimlog_test

import (
"encoding/json"
"testing"

"github.com/google/go-cmp/cmp"

"github.com/jellevandenhooff/gosim/internal/gosimlog"
)

func TestUnmarshalLog(t *testing.T) {
testcases := []struct {
json string
expected *gosimlog.Log
}{
{
json: `{
"msg": "hello",
"extra": "1",
"extra2": "2"
}`,
expected: &gosimlog.Log{
Msg: "hello",
Unknown: []gosimlog.UnknownField{
{
Key: "extra",
Value: `"1"`,
},
{
Key: "extra2",
Value: `"2"`,
},
},
},
},
}

for _, tc := range testcases {
var got gosimlog.Log
if err := json.Unmarshal([]byte(tc.json), &got); err != nil {
t.Error(err)
}
if diff := cmp.Diff(&got, tc.expected); diff != "" {
t.Errorf("unexpected diff unmarshaling %q: %s", tc.json, diff)
}
}
}
3 changes: 3 additions & 0 deletions internal/gosimviewer/related.html.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
<a href="#" onclick="show({index: {{ .Index }}, source: {{ .Source }} })">{{ .Source | Source }}</a>
&gt;
<pre style="display: inline; text-wrap: wrap;">{{ .Msg }}</pre>
{{ range .Unknown }}
<pre style="display: inline; text-wrap: wrap;">{{ .Key }}={{ .Value }}</pre>
{{ end }}
</span><br>
{{ end }}
{{ if .Truncated }}and more...{{ end }}

0 comments on commit 75f05a2

Please sign in to comment.