app/vlogscli: preserve the original order of fields in the displayed responses

This commit is contained in:
Aliaksandr Valialkin 2024-10-05 21:25:43 +02:00
parent 4d9ad9654f
commit 596e4de248
No known key found for this signature in database
GPG key ID: 52C003EE2BCDB9EB
2 changed files with 91 additions and 7 deletions

View file

@ -46,15 +46,11 @@ func (jp *jsonPrettifier) closePipesWithError(err error) {
func (jp *jsonPrettifier) prettifyJSONLines() error {
for jp.d.More() {
var v any
if err := jp.d.Decode(&v); err != nil {
kvs, err := readNextJSONObject(jp.d)
if err != nil {
return err
}
line, err := json.MarshalIndent(v, "", " ")
if err != nil {
panic(fmt.Errorf("BUG: cannot marshal %v to JSON: %w", v, err))
}
if _, err := fmt.Fprintf(jp.pw, "%s\n", line); err != nil {
if err := writeJSONObject(jp.pw, kvs); err != nil {
return err
}
}
@ -71,3 +67,89 @@ func (jp *jsonPrettifier) Close() error {
func (jp *jsonPrettifier) Read(p []byte) (int, error) {
return jp.pr.Read(p)
}
func readNextJSONObject(d *json.Decoder) ([]kv, error) {
t, err := d.Token()
if err != nil {
return nil, fmt.Errorf("cannot read '{': %w", err)
}
delim, ok := t.(json.Delim)
if !ok || delim.String() != "{" {
return nil, fmt.Errorf("unexpected token read; got %q; want '{'", delim)
}
var kvs []kv
for {
// Read object key
t, err := d.Token()
if err != nil {
return nil, fmt.Errorf("cannot read JSON object key or closing brace: %w", err)
}
delim, ok := t.(json.Delim)
if ok {
if delim.String() == "}" {
return kvs, nil
}
return nil, fmt.Errorf("unexpected delimiter read; got %q; want '}'", delim)
}
key, ok := t.(string)
if !ok {
return nil, fmt.Errorf("unexpected token read for object key: %v; want string or '}'", t)
}
// read object value
t, err = d.Token()
if err != nil {
return nil, fmt.Errorf("cannot read JSON object value: %w", err)
}
value, ok := t.(string)
if !ok {
return nil, fmt.Errorf("unexpected token read for oject value: %v; want string", t)
}
kvs = append(kvs, kv{
key: key,
value: value,
})
}
}
func writeJSONObject(w io.Writer, kvs []kv) error {
if len(kvs) == 0 {
fmt.Fprintf(w, "{}\n")
return nil
}
fmt.Fprintf(w, "{\n")
if err := writeJSONObjectKeyValue(w, kvs[0]); err != nil {
return err
}
for _, kv := range kvs[1:] {
fmt.Fprintf(w, ",\n")
if err := writeJSONObjectKeyValue(w, kv); err != nil {
return err
}
}
fmt.Fprintf(w, "\n}\n")
return nil
}
func writeJSONObjectKeyValue(w io.Writer, kv kv) error {
key := getJSONString(kv.key)
value := getJSONString(kv.value)
_, err := fmt.Fprintf(w, " %s: %s", key, value)
return err
}
func getJSONString(s string) string {
data, err := json.Marshal(s)
if err != nil {
panic(fmt.Errorf("unexpected error when marshaling string to JSON: %w", err))
}
return string(data)
}
type kv struct {
key string
value string
}

View file

@ -18,6 +18,8 @@ according to [these docs](https://docs.victoriametrics.com/victorialogs/quicksta
* FEATURE: [vlogscli](https://docs.victoriametrics.com/victorialogs/querying/vlogscli/): preserve `less` output after the exit from scrolling mode. This should help re-using previous query results in subsequent queries.
* FEATURE: add [`len` pipe](https://docs.victoriametrics.com/victorialogs/logsql/#len-pipe) for calculating the length for the given [log field](https://docs.victoriametrics.com/victorialogs/keyconcepts/#data-model) value in bytes.
* BUGFIX: [vlogscli](https://docs.victoriametrics.com/victorialogs/querying/vlogscli/): preserve the original order of fields in the displayed query responses. Previously fields were sorted by name.
## [v0.33.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v0.33.0-victorialogs)
Released at 2024-10-01