forked from boyska/circolog
130 lines
3 KiB
Go
130 lines
3 KiB
Go
package formatter
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"hash/fnv"
|
|
"io"
|
|
"text/template"
|
|
"time"
|
|
|
|
"git.lattuga.net/boyska/circolog/data"
|
|
"github.com/mgutz/ansi"
|
|
"gopkg.in/mgo.v2/bson"
|
|
)
|
|
|
|
// Formatter is an interface, so that multiple implementations can exist
|
|
type Formatter func(data.Message) string
|
|
|
|
var tmplFuncs template.FuncMap
|
|
var syslogTmpl *template.Template
|
|
|
|
func init() {
|
|
tmplFuncs := template.FuncMap{
|
|
"dateFormat": func(dt time.Time, fmt string) string {
|
|
return dt.Format(fmt)
|
|
},
|
|
"rfc822": func(dt time.Time) string {
|
|
return dt.Format(time.RFC822)
|
|
},
|
|
"sevName": func(s int) string {
|
|
names := []string{"emerg ", "alert ", "crit ", "err ", "warn ", "notice", "info ", "dbg "}
|
|
switch {
|
|
case s < 2: // emerg..alert
|
|
return ansi.Color(names[s], "red+b")
|
|
case s < 4: // emerg..err
|
|
return ansi.Color(names[s], "red")
|
|
case s < 6: // warn..notice
|
|
return ansi.Color(names[s], "white+b")
|
|
case s >= len(names):
|
|
return "???"
|
|
default:
|
|
return names[s]
|
|
}
|
|
},
|
|
"color": func(color, text string) string {
|
|
return ansi.Color(text, color) // slow; should use colorfunc
|
|
},
|
|
"red": ansi.ColorFunc("red+b"),
|
|
"autoColor": func(s string) string {
|
|
// from https://weechat.org/blog/post/2011/08/28/Beautify-your-WeeChat
|
|
palette := []string{"31", "35", "38", "40", "49", "63", "70", "80", "92", "99", "112", "126", "130", "138", "142", "148", "167", "169", "174", "176", "178", "184", "186", "210", "212", "215", "247"}
|
|
hash := fnv.New32()
|
|
hash.Write([]byte(s))
|
|
picked := palette[int(hash.Sum32())%len(palette)]
|
|
return ansi.Color(s, picked)
|
|
},
|
|
}
|
|
syslogTmpl = template.Must(template.New("syslog").Funcs(tmplFuncs).Parse(
|
|
"{{color \"yellow\" (rfc822 (index . \"time\")) }} {{index . \"host\"}} " +
|
|
"{{index . \"prog\" | autoColor}}" +
|
|
"{{ if (ne (index . \"proc_id\") \"-\")}}[{{index . \"proc_id\"}}]{{end}}: " +
|
|
"{{ sevName (index . \"sev\") }} " +
|
|
"{{index . \"msg\"}}",
|
|
))
|
|
}
|
|
|
|
type Format int
|
|
|
|
func (rf *Format) Set(v string) error {
|
|
newvalue, err := parseFormat(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*rf = newvalue
|
|
return nil
|
|
}
|
|
|
|
func (rf Format) String() string {
|
|
switch rf {
|
|
case FormatJSON:
|
|
return "json"
|
|
case FormatSyslog:
|
|
return "syslog"
|
|
case FormatBSON:
|
|
return "bson"
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func (rf Format) WriteFormatted(w io.Writer, msg data.Message) error {
|
|
return WriteFormatted(w, rf, msg)
|
|
}
|
|
|
|
func parseFormat(s string) (Format, error) {
|
|
switch s {
|
|
case "json":
|
|
return FormatJSON, nil
|
|
case "syslog":
|
|
return FormatSyslog, nil
|
|
case "bson":
|
|
return FormatBSON, nil
|
|
default:
|
|
return 0, fmt.Errorf("Undefined format `%s`", s)
|
|
}
|
|
}
|
|
|
|
const (
|
|
FormatSyslog = iota // 0
|
|
FormatJSON = iota
|
|
FormatBSON = iota
|
|
)
|
|
|
|
func WriteFormatted(w io.Writer, f Format, msg data.Message) error {
|
|
switch f {
|
|
case FormatSyslog:
|
|
return syslogTmpl.Execute(w, msg)
|
|
case FormatJSON:
|
|
enc := json.NewEncoder(w)
|
|
enc.SetIndent("", " ")
|
|
return enc.Encode(msg)
|
|
case FormatBSON:
|
|
enc, err := bson.Marshal(msg)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = w.Write(enc)
|
|
return err
|
|
}
|
|
return nil
|
|
}
|