mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-12-22 16:36:27 +01:00
7f4fb34182
It is better developing vmctl tool in VictoriaMetrics repository, so it could be released together with the rest of vmutils tools such as vmalert, vmagent, vmbackup, vmrestore and vmauth.
92 lines
2.0 KiB
Go
92 lines
2.0 KiB
Go
package log
|
|
|
|
import (
|
|
"encoding"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"reflect"
|
|
)
|
|
|
|
type jsonLogger struct {
|
|
io.Writer
|
|
}
|
|
|
|
// NewJSONLogger returns a Logger that encodes keyvals to the Writer as a
|
|
// single JSON object. Each log event produces no more than one call to
|
|
// w.Write. The passed Writer must be safe for concurrent use by multiple
|
|
// goroutines if the returned Logger will be used concurrently.
|
|
func NewJSONLogger(w io.Writer) Logger {
|
|
return &jsonLogger{w}
|
|
}
|
|
|
|
func (l *jsonLogger) Log(keyvals ...interface{}) error {
|
|
n := (len(keyvals) + 1) / 2 // +1 to handle case when len is odd
|
|
m := make(map[string]interface{}, n)
|
|
for i := 0; i < len(keyvals); i += 2 {
|
|
k := keyvals[i]
|
|
var v interface{} = ErrMissingValue
|
|
if i+1 < len(keyvals) {
|
|
v = keyvals[i+1]
|
|
}
|
|
merge(m, k, v)
|
|
}
|
|
enc := json.NewEncoder(l.Writer)
|
|
enc.SetEscapeHTML(false)
|
|
return enc.Encode(m)
|
|
}
|
|
|
|
func merge(dst map[string]interface{}, k, v interface{}) {
|
|
var key string
|
|
switch x := k.(type) {
|
|
case string:
|
|
key = x
|
|
case fmt.Stringer:
|
|
key = safeString(x)
|
|
default:
|
|
key = fmt.Sprint(x)
|
|
}
|
|
|
|
// We want json.Marshaler and encoding.TextMarshaller to take priority over
|
|
// err.Error() and v.String(). But json.Marshall (called later) does that by
|
|
// default so we force a no-op if it's one of those 2 case.
|
|
switch x := v.(type) {
|
|
case json.Marshaler:
|
|
case encoding.TextMarshaler:
|
|
case error:
|
|
v = safeError(x)
|
|
case fmt.Stringer:
|
|
v = safeString(x)
|
|
}
|
|
|
|
dst[key] = v
|
|
}
|
|
|
|
func safeString(str fmt.Stringer) (s string) {
|
|
defer func() {
|
|
if panicVal := recover(); panicVal != nil {
|
|
if v := reflect.ValueOf(str); v.Kind() == reflect.Ptr && v.IsNil() {
|
|
s = "NULL"
|
|
} else {
|
|
panic(panicVal)
|
|
}
|
|
}
|
|
}()
|
|
s = str.String()
|
|
return
|
|
}
|
|
|
|
func safeError(err error) (s interface{}) {
|
|
defer func() {
|
|
if panicVal := recover(); panicVal != nil {
|
|
if v := reflect.ValueOf(err); v.Kind() == reflect.Ptr && v.IsNil() {
|
|
s = nil
|
|
} else {
|
|
panic(panicVal)
|
|
}
|
|
}
|
|
}()
|
|
s = err.Error()
|
|
return
|
|
}
|