Add syslog CEE support (#262)
This commit is contained in:
parent
72acd6cfe8
commit
e11d470c08
42
syslog.go
42
syslog.go
|
@ -7,6 +7,10 @@ import (
|
||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// See http://cee.mitre.org/language/1.0-beta1/clt.html#syslog
|
||||||
|
// or https://www.rsyslog.com/json-elasticsearch/
|
||||||
|
const ceePrefix = "@cee:"
|
||||||
|
|
||||||
// SyslogWriter is an interface matching a syslog.Writer struct.
|
// SyslogWriter is an interface matching a syslog.Writer struct.
|
||||||
type SyslogWriter interface {
|
type SyslogWriter interface {
|
||||||
io.Writer
|
io.Writer
|
||||||
|
@ -19,17 +23,34 @@ type SyslogWriter interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type syslogWriter struct {
|
type syslogWriter struct {
|
||||||
w SyslogWriter
|
w SyslogWriter
|
||||||
|
prefix string
|
||||||
}
|
}
|
||||||
|
|
||||||
// SyslogLevelWriter wraps a SyslogWriter and call the right syslog level
|
// SyslogLevelWriter wraps a SyslogWriter and call the right syslog level
|
||||||
// method matching the zerolog level.
|
// method matching the zerolog level.
|
||||||
func SyslogLevelWriter(w SyslogWriter) LevelWriter {
|
func SyslogLevelWriter(w SyslogWriter) LevelWriter {
|
||||||
return syslogWriter{w}
|
return syslogWriter{w, ""}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SyslogCEEWriter wraps a SyslogWriter with a SyslogLevelWriter that adds a
|
||||||
|
// MITRE CEE prefix for JSON syslog entries, compatible with rsyslog
|
||||||
|
// and syslog-ng JSON logging support.
|
||||||
|
// See https://www.rsyslog.com/json-elasticsearch/
|
||||||
|
func SyslogCEEWriter(w SyslogWriter) LevelWriter {
|
||||||
|
return syslogWriter{w, ceePrefix}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sw syslogWriter) Write(p []byte) (n int, err error) {
|
func (sw syslogWriter) Write(p []byte) (n int, err error) {
|
||||||
return sw.w.Write(p)
|
var pn int
|
||||||
|
if sw.prefix != "" {
|
||||||
|
pn, err = sw.w.Write([]byte(sw.prefix))
|
||||||
|
if err != nil {
|
||||||
|
return pn, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n, err = sw.w.Write(p)
|
||||||
|
return pn + n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteLevel implements LevelWriter interface.
|
// WriteLevel implements LevelWriter interface.
|
||||||
|
@ -37,22 +58,23 @@ func (sw syslogWriter) WriteLevel(level Level, p []byte) (n int, err error) {
|
||||||
switch level {
|
switch level {
|
||||||
case TraceLevel:
|
case TraceLevel:
|
||||||
case DebugLevel:
|
case DebugLevel:
|
||||||
err = sw.w.Debug(string(p))
|
err = sw.w.Debug(sw.prefix + string(p))
|
||||||
case InfoLevel:
|
case InfoLevel:
|
||||||
err = sw.w.Info(string(p))
|
err = sw.w.Info(sw.prefix + string(p))
|
||||||
case WarnLevel:
|
case WarnLevel:
|
||||||
err = sw.w.Warning(string(p))
|
err = sw.w.Warning(sw.prefix + string(p))
|
||||||
case ErrorLevel:
|
case ErrorLevel:
|
||||||
err = sw.w.Err(string(p))
|
err = sw.w.Err(sw.prefix + string(p))
|
||||||
case FatalLevel:
|
case FatalLevel:
|
||||||
err = sw.w.Emerg(string(p))
|
err = sw.w.Emerg(sw.prefix + string(p))
|
||||||
case PanicLevel:
|
case PanicLevel:
|
||||||
err = sw.w.Crit(string(p))
|
err = sw.w.Crit(sw.prefix + string(p))
|
||||||
case NoLevel:
|
case NoLevel:
|
||||||
err = sw.w.Info(string(p))
|
err = sw.w.Info(sw.prefix + string(p))
|
||||||
default:
|
default:
|
||||||
panic("invalid level")
|
panic("invalid level")
|
||||||
}
|
}
|
||||||
|
// Any CEE prefix is not part of the message, so we don't include its length
|
||||||
n = len(p)
|
n = len(p)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,9 @@
|
||||||
package zerolog
|
package zerolog
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -68,3 +70,39 @@ func TestSyslogWriter(t *testing.T) {
|
||||||
t.Errorf("Invalid syslog message routing: want %v, got %v", want, got)
|
t.Errorf("Invalid syslog message routing: want %v, got %v", want, got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type testCEEwriter struct {
|
||||||
|
buf *bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only implement one method as we're just testing the prefixing
|
||||||
|
func (c testCEEwriter) Debug(m string) error { return nil }
|
||||||
|
|
||||||
|
func (c testCEEwriter) Info(m string) error {
|
||||||
|
_, err := c.buf.Write([]byte(m))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c testCEEwriter) Warning(m string) error { return nil }
|
||||||
|
|
||||||
|
func (c testCEEwriter) Err(m string) error { return nil }
|
||||||
|
|
||||||
|
func (c testCEEwriter) Emerg(m string) error { return nil }
|
||||||
|
|
||||||
|
func (c testCEEwriter) Crit(m string) error { return nil }
|
||||||
|
|
||||||
|
func (c testCEEwriter) Write(b []byte) (int, error) {
|
||||||
|
return c.buf.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSyslogWriter_WithCEE(t *testing.T) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
sw := testCEEwriter{&buf}
|
||||||
|
log := New(SyslogCEEWriter(sw))
|
||||||
|
log.Info().Str("key", "value").Msg("message string")
|
||||||
|
got := string(buf.Bytes())
|
||||||
|
want := "@cee:{"
|
||||||
|
if !strings.HasPrefix(got, want) {
|
||||||
|
t.Errorf("Bad CEE message start: want %v, got %v", want, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue