format.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. package formatter
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "hash/fnv"
  6. "io"
  7. "text/template"
  8. "time"
  9. "git.lattuga.net/boyska/circolog/data"
  10. "github.com/mgutz/ansi"
  11. "gopkg.in/mgo.v2/bson"
  12. )
  13. // Formatter is an interface, so that multiple implementations can exist
  14. type Formatter func(data.Message) string
  15. var tmplFuncs template.FuncMap
  16. var syslogTmpl *template.Template
  17. func init() {
  18. tmplFuncs := template.FuncMap{
  19. "dateFormat": func(dt time.Time, fmt string) string {
  20. return dt.Format(fmt)
  21. },
  22. "rfc822": func(dt time.Time) string {
  23. return dt.Format(time.RFC822)
  24. },
  25. "sevName": func(s int) string {
  26. names := []string{"emerg ", "alert ", "crit ", "err ", "warn ", "notice", "info ", "dbg "}
  27. switch {
  28. case s < 2: // emerg..alert
  29. return ansi.Color(names[s], "red+b")
  30. case s < 4: // emerg..err
  31. return ansi.Color(names[s], "red")
  32. case s < 6: // warn..notice
  33. return ansi.Color(names[s], "white+b")
  34. case s >= len(names):
  35. return "???"
  36. default:
  37. return names[s]
  38. }
  39. },
  40. "color": func(color, text string) string {
  41. return ansi.Color(text, color) // slow; should use colorfunc
  42. },
  43. "red": ansi.ColorFunc("red+b"),
  44. "autoColor": func(s string) string {
  45. // from https://weechat.org/blog/post/2011/08/28/Beautify-your-WeeChat
  46. 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"}
  47. hash := fnv.New32()
  48. hash.Write([]byte(s))
  49. picked := palette[int(hash.Sum32())%len(palette)]
  50. return ansi.Color(s, picked)
  51. },
  52. }
  53. syslogTmpl = template.Must(template.New("syslog").Funcs(tmplFuncs).Parse(
  54. "{{color \"yellow\" (rfc822 (index . \"time\")) }} {{index . \"host\"}} " +
  55. "{{index . \"prog\" | autoColor}}" +
  56. "{{ if (ne (index . \"proc_id\") \"-\")}}[{{index . \"proc_id\"}}]{{end}}: " +
  57. "{{ sevName (index . \"sev\") }} " +
  58. "{{index . \"msg\"}}",
  59. ))
  60. }
  61. type Format int
  62. func (rf *Format) Set(v string) error {
  63. newvalue, err := parseFormat(v)
  64. if err != nil {
  65. return err
  66. }
  67. *rf = newvalue
  68. return nil
  69. }
  70. func (rf Format) String() string {
  71. switch rf {
  72. case FormatJSON:
  73. return "json"
  74. case FormatSyslog:
  75. return "syslog"
  76. case FormatBSON:
  77. return "bson"
  78. }
  79. return ""
  80. }
  81. func (rf Format) WriteFormatted(w io.Writer, msg data.Message) error {
  82. return WriteFormatted(w, rf, msg)
  83. }
  84. func parseFormat(s string) (Format, error) {
  85. switch s {
  86. case "json":
  87. return FormatJSON, nil
  88. case "syslog":
  89. return FormatSyslog, nil
  90. case "bson":
  91. return FormatBSON, nil
  92. default:
  93. return 0, fmt.Errorf("Undefined format `%s`", s)
  94. }
  95. }
  96. const (
  97. FormatSyslog = iota // 0
  98. FormatJSON = iota
  99. FormatBSON = iota
  100. )
  101. func WriteFormatted(w io.Writer, f Format, msg data.Message) error {
  102. switch f {
  103. case FormatSyslog:
  104. return syslogTmpl.Execute(w, msg)
  105. case FormatJSON:
  106. enc := json.NewEncoder(w)
  107. enc.SetIndent("", " ")
  108. return enc.Encode(msg)
  109. case FormatBSON:
  110. enc, err := bson.Marshal(msg)
  111. if err != nil {
  112. return err
  113. }
  114. _, err = w.Write(enc)
  115. return err
  116. }
  117. return nil
  118. }