diff --git a/Gopkg.lock b/Gopkg.lock index 8451b50..777b497 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -19,6 +19,12 @@ packages = ["."] revision = "c6d800382fff6dc1412f34269f71b7f83bd059ad" +[[projects]] + name = "github.com/go-logfmt/logfmt" + packages = ["."] + revision = "390ab7935ee28ec6b286364bba9b4dd6410cb3d5" + version = "v0.3.0" + [[projects]] branch = "v2" name = "github.com/go-yaml/yaml" @@ -43,6 +49,12 @@ packages = ["."] revision = "db4671f3a9b8df855e993f7c94ec5ef1ffb0a23b" +[[projects]] + branch = "master" + name = "github.com/kr/logfmt" + packages = ["."] + revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0" + [[projects]] branch = "master" name = "github.com/kr/pretty" @@ -160,6 +172,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "bee0c80e0ddaadd7725a31b203d080be9e360fb17514e972ff2023c0b40cb004" + inputs-digest = "7ce2ead5225e4bb72a34132538171a649b26de574596e909730658ddbc904cd5" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 93acc94..5dc7de7 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -60,3 +60,7 @@ [[constraint]] branch = "v2" name = "github.com/go-yaml/yaml" + +[[constraint]] + name = "github.com/go-logfmt/logfmt" + version = "*" diff --git a/cmd/config_logging.go b/cmd/config_logging.go index 92cae32..90bf321 100644 --- a/cmd/config_logging.go +++ b/cmd/config_logging.go @@ -138,6 +138,8 @@ func parseLogFormat(i interface{}) (f EntryFormatter, err error) { switch is { case "human": return &HumanFormatter{}, nil + case "logfmt": + return &LogfmtFormatter{}, nil case "json": return &JSONFormatter{}, nil default: diff --git a/cmd/logging_formatters.go b/cmd/logging_formatters.go index 3f0bf16..7cc58ce 100644 --- a/cmd/logging_formatters.go +++ b/cmd/logging_formatters.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/json" "fmt" + "github.com/go-logfmt/logfmt" "github.com/pkg/errors" "github.com/zrepl/zrepl/logger" "strings" @@ -125,3 +126,49 @@ func (f *JSONFormatter) Format(e *logger.Entry) ([]byte, error) { return json.Marshal(data) } + +type LogfmtFormatter struct { + NoMetadata bool +} + +var _ SetNoMetadataFormatter = &LogfmtFormatter{} + +func (f *LogfmtFormatter) SetNoMetadata(noMetadata bool) { + f.NoMetadata = noMetadata +} + +func (f *LogfmtFormatter) Format(e *logger.Entry) ([]byte, error) { + var buf bytes.Buffer + enc := logfmt.NewEncoder(&buf) + + if !f.NoMetadata { + enc.EncodeKeyval(FieldTime, e.Time) + enc.EncodeKeyval(FieldLevel, e.Level) + } + + // at least try and put job and task in front + prefixed := make(map[string]bool, 2) + prefix := []string{logJobField, logTaskField} + for _, pf := range prefix { + v, ok := e.Fields[pf] + if !ok { + break + } + enc.EncodeKeyval(pf, v) + prefixed[pf] = true + } + + enc.EncodeKeyval(FieldMessage, e.Message) + + for k, v := range e.Fields { + if !prefixed[k] { + enc.EncodeKeyval(k, v) + } + } + + if err := enc.EndRecord(); err != nil { + return nil, err + } + + return buf.Bytes(), nil +}