mirror of
https://github.com/zrepl/zrepl.git
synced 2024-11-25 09:54:47 +01:00
175 lines
3.7 KiB
Go
175 lines
3.7 KiB
Go
package cmd
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"github.com/go-logfmt/logfmt"
|
|
"github.com/pkg/errors"
|
|
"github.com/zrepl/zrepl/logger"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type EntryFormatter interface {
|
|
Format(e *logger.Entry) ([]byte, error)
|
|
}
|
|
|
|
const (
|
|
FieldLevel = "level"
|
|
FieldMessage = "msg"
|
|
FieldTime = "time"
|
|
)
|
|
|
|
const (
|
|
logJobField string = "job"
|
|
logTaskField string = "task"
|
|
logFSField string = "filesystem"
|
|
logMapFromField string = "map_from"
|
|
logMapToField string = "map_to"
|
|
logIncFromField string = "inc_from"
|
|
logIncToField string = "inc_to"
|
|
)
|
|
|
|
type NoFormatter struct{}
|
|
|
|
func (f NoFormatter) Format(e *logger.Entry) ([]byte, error) {
|
|
return []byte(e.Message), nil
|
|
}
|
|
|
|
type HumanFormatter struct {
|
|
NoMetadata bool
|
|
}
|
|
|
|
var _ SetNoMetadataFormatter = &HumanFormatter{}
|
|
|
|
func (f *HumanFormatter) SetNoMetadata(noMetadata bool) {
|
|
f.NoMetadata = noMetadata
|
|
}
|
|
|
|
func (f *HumanFormatter) Format(e *logger.Entry) (out []byte, err error) {
|
|
|
|
var line bytes.Buffer
|
|
|
|
if !f.NoMetadata {
|
|
fmt.Fprintf(&line, "[%s]", e.Level.Short())
|
|
}
|
|
|
|
prefixFields := []string{logJobField, logTaskField, logFSField}
|
|
prefixed := make(map[string]bool, len(prefixFields)+2)
|
|
for _, field := range prefixFields {
|
|
val, ok := e.Fields[field].(string)
|
|
if ok {
|
|
fmt.Fprintf(&line, "[%s]", val)
|
|
prefixed[field] = true
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
// even more prefix fields
|
|
mapFrom, mapFromOk := e.Fields[logMapFromField].(string)
|
|
mapTo, mapToOk := e.Fields[logMapToField].(string)
|
|
if mapFromOk && mapToOk {
|
|
fmt.Fprintf(&line, "[%s => %s]", mapFrom, mapTo)
|
|
prefixed[logMapFromField], prefixed[logMapToField] = true, true
|
|
}
|
|
incFrom, incFromOk := e.Fields[logIncFromField].(string)
|
|
incTo, incToOk := e.Fields[logIncToField].(string)
|
|
if incFromOk && incToOk {
|
|
fmt.Fprintf(&line, "[%s => %s]", incFrom, incTo)
|
|
prefixed[logIncFromField], prefixed[logIncToField] = true, true
|
|
}
|
|
|
|
if line.Len() > 0 {
|
|
fmt.Fprint(&line, ": ")
|
|
}
|
|
fmt.Fprint(&line, e.Message)
|
|
|
|
for field, value := range e.Fields {
|
|
|
|
if prefixed[field] {
|
|
continue
|
|
}
|
|
|
|
if strings.ContainsAny(field, " \t") {
|
|
return nil, errors.Errorf("field must not contain whitespace: '%s'", field)
|
|
}
|
|
fmt.Fprintf(&line, " %s=\"%s\"", field, value)
|
|
}
|
|
|
|
return line.Bytes(), nil
|
|
}
|
|
|
|
type JSONFormatter struct{}
|
|
|
|
func (f *JSONFormatter) Format(e *logger.Entry) ([]byte, error) {
|
|
data := make(logger.Fields, len(e.Fields)+3)
|
|
for k, v := range e.Fields {
|
|
switch v := v.(type) {
|
|
case error:
|
|
// Otherwise errors are ignored by `encoding/json`
|
|
// https://github.com/sirupsen/logrus/issues/137
|
|
data[k] = v.Error()
|
|
default:
|
|
_, err := json.Marshal(v)
|
|
if err != nil {
|
|
return nil, errors.Errorf("field is not JSON encodable: %s", k)
|
|
}
|
|
data[k] = v
|
|
}
|
|
}
|
|
|
|
data[FieldMessage] = e.Message
|
|
data[FieldTime] = e.Time.Format(time.RFC3339)
|
|
data[FieldLevel] = e.Level
|
|
|
|
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
|
|
}
|