diff --git a/logging/TODO b/logging/TODO new file mode 100644 index 0000000..319b432 --- /dev/null +++ b/logging/TODO @@ -0,0 +1,3 @@ +- Implement code line/func/etc. (only for debug?): + https://stackoverflow.com/a/24809646 + https://golang.org/pkg/runtime/#Caller diff --git a/logging/consts.go b/logging/consts.go index b59f348..6f5c8d8 100644 --- a/logging/consts.go +++ b/logging/consts.go @@ -1,6 +1,7 @@ package logging import ( + `log/syslog` `os` `r00t2.io/goutils/types` @@ -9,6 +10,9 @@ import ( const ( devlog string = "/dev/log" logPerm os.FileMode = 0600 + logPrefix string = "GOLANG PROGRAM" + appendFlags int = os.O_APPEND|os.O_CREATE|os.O_WRONLY + syslogFacility syslog.Priority = syslog.LOG_USER ) // Flags for logger configuration diff --git a/logging/funcs.go b/logging/funcs.go index 10beada..e85c969 100644 --- a/logging/funcs.go +++ b/logging/funcs.go @@ -4,7 +4,6 @@ import ( native `log` `os` `path` - `strings` "r00t2.io/goutils/types" @@ -16,10 +15,15 @@ var ( _ = sysd.Enabled() _ = native.Logger{} _ = os.Interrupt - _ = types.Env{} ) -func GetLogger(enableDebug bool, prefix string) (logger Logger, err error) { +// GetLogger returns an instance of Logger that best suits your system's capabilities. +// If enableDebug is true, debug messages (which according to your program may or may not contain sensitive data) are rendered and written. +// If prefix is "\x00" (a null byte), then the default logging prefix will be used. If anything else, even an empty string, +// is specified then that will be used instead for the prefix. +// logpaths is an (optional) list of strings to use as paths to test for writing. If the file can be created/written to, +// it will be used (assuming you have no higher-level loggers available). Only the first logpaths entry that "works" will be used, later entries will be ignored. +func GetLogger(enableDebug bool, prefix string, logpaths ...string) (logger Logger, err error) { var logPath string var logflags types.MaskBit @@ -40,8 +44,13 @@ func GetLogger(enableDebug bool, prefix string) (logger Logger, err error) { if hasSyslog && !stat.Mode().IsRegular() {logflags.AddFlag(LogSyslog)} else { var exists bool var success bool + var ckLogPaths []string logflags.AddFlag(LogStdout) - for _, p := range defLogPaths { + ckLogPaths = defLogPaths + if logpaths != nil { + ckLogPaths = logpaths + } + for _, p := range ckLogPaths { if exists, _ = paths.RealPathExists(&p); exists { if success, err = testOpen(p); err != nil { continue @@ -72,30 +81,34 @@ func GetLogger(enableDebug bool, prefix string) (logger Logger, err error) { if logflags.HasFlag(LogJournald) { logger = &SystemDLogger{ Prefix: logPrefix, + EnableDebug: enableDebug, } } else { if logflags.HasFlag(LogSyslog) { logger = &SyslogLogger{ Prefix: logPrefix, + EnableDebug: enableDebug, } } else { if logflags.HasFlag(LogFile) { logger = &FileLogger{ StdLogger: StdLogger{ Prefix: logPrefix, + EnableDebug: enableDebug, }, Path: logPath, } } else { logger = &StdLogger{ Prefix: logPrefix, + EnableDebug: enableDebug, } } } } - logger.doDebug(enableDebug) - if strings.TrimSpace(prefix) != "" { + logger.Setup() + if prefix != "\x00" { logger.setPrefix(prefix) } @@ -106,7 +119,7 @@ func testOpen(path string) (success bool, err error) { var f *os.File // Example #2, https://golang.org/pkg/os/#OpenFile - if f, err = os.OpenFile(path, os.O_RDWR | os.O_CREATE, logPerm); err != nil { + if f, err = os.OpenFile(path, appendFlags, logPerm); err != nil { return } defer f.Close() diff --git a/logging/funcs_file.go b/logging/funcs_file.go index a302c92..afae21e 100644 --- a/logging/funcs_file.go +++ b/logging/funcs_file.go @@ -1,41 +1,176 @@ package logging +import ( + `fmt` + `io` + `log` + `os` +) + +func (l *FileLogger) Setup() { + + var err error + + l.Logger = log.Default() + l.Logger.SetPrefix(l.Prefix) + if l.writer, err = os.OpenFile(l.Path, appendFlags, logPerm); err != nil { + log.Panicf("could not open log file \"%v\" for writing: %v\n", l.Path, err) + } + // https://stackoverflow.com/a/36719588/733214 + multi := io.MultiWriter(os.Stdout, l.writer) + l.Logger.SetOutput(multi) +} + +func (l *FileLogger) Shutdown() { + + var err error + + if err = l.writer.Close(); err != nil { + log.Panicf("could not close log file \"%v\": %v\n", l.Path, err) + } + +} + func (l *FileLogger) doDebug(d bool) { l.EnableDebug = d } func (l *FileLogger) setPrefix(prefix string) { l.Prefix = prefix + l.Logger.SetPrefix(prefix) } func (l *FileLogger) Alert(s string, v ...interface{}) (err error) { + + var msg string + + if v != nil { + msg = fmt.Sprintf(s, v...) + } else { + msg = s + } + + l.renderWrite(msg, "ALERT") + return } func (l *FileLogger) Crit(s string, v ...interface{}) (err error) { + + var msg string + + if v != nil { + msg = fmt.Sprintf(s, v...) + } else { + msg = s + } + + l.renderWrite(msg, "CRITICAL") + return } func (l *FileLogger) Debug(s string, v ...interface{}) (err error) { + + if !l.EnableDebug { + return + } + + var msg string + + if v != nil { + msg = fmt.Sprintf(s, v...) + } else { + msg = s + } + + l.renderWrite(msg, "DEBUG") + return } func (l *FileLogger) Emerg(s string, v ...interface{}) (err error) { + + + var msg string + + if v != nil { + msg = fmt.Sprintf(s, v...) + } else { + msg = s + } + + l.renderWrite(msg, "EMERGENCY") + return } func (l *FileLogger) Err(s string, v ...interface{}) (err error) { + + var msg string + + if v != nil { + msg = fmt.Sprintf(s, v...) + } else { + msg = s + } + + l.renderWrite(msg, "ERROR") + return } func (l *FileLogger) Info(s string, v ...interface{}) (err error) { + + var msg string + + if v != nil { + msg = fmt.Sprintf(s, v...) + } else { + msg = s + } + + l.renderWrite(msg, "INFO") + return } func (l *FileLogger) Notice(s string, v ...interface{}) (err error) { + + var msg string + + if v != nil { + msg = fmt.Sprintf(s, v...) + } else { + msg = s + } + + l.renderWrite(msg, "NOTICE") + return } func (l *FileLogger) Warning(s string, v ...interface{}) (err error) { + + + var msg string + + if v != nil { + msg = fmt.Sprintf(s, v...) + } else { + msg = s + } + + l.renderWrite(msg, "WARNING") + + return +} + +func (l *FileLogger) renderWrite(msg, prio string) { + + s := fmt.Sprintf("[%v] %v", prio, msg) + + l.Logger.Println(s) + return } diff --git a/logging/funcs_std.go b/logging/funcs_std.go index 05b69e7..20b686c 100644 --- a/logging/funcs_std.go +++ b/logging/funcs_std.go @@ -1,41 +1,163 @@ package logging +import ( + `fmt` + `log` +) + +func (l *StdLogger) Setup() { + + l.Logger = log.Default() + l.Logger.SetPrefix(l.Prefix) +} + +func (l *StdLogger) Shutdown() { + + // NOOP + _ = "" + +} + func (l *StdLogger) doDebug(d bool) { l.EnableDebug = d } func (l *StdLogger) setPrefix(prefix string) { l.Prefix = prefix + l.Logger.SetPrefix(prefix) } func (l *StdLogger) Alert(s string, v ...interface{}) (err error) { + + var msg string + + if v != nil { + msg = fmt.Sprintf(s, v...) + } else { + msg = s + } + + l.renderWrite(msg, "ALERT") + return } func (l *StdLogger) Crit(s string, v ...interface{}) (err error) { + + var msg string + + if v != nil { + msg = fmt.Sprintf(s, v...) + } else { + msg = s + } + + l.renderWrite(msg, "CRITICAL") + return } func (l *StdLogger) Debug(s string, v ...interface{}) (err error) { + + if !l.EnableDebug { + return + } + + var msg string + + if v != nil { + msg = fmt.Sprintf(s, v...) + } else { + msg = s + } + + l.renderWrite(msg, "DEBUG") + return } func (l *StdLogger) Emerg(s string, v ...interface{}) (err error) { + + + var msg string + + if v != nil { + msg = fmt.Sprintf(s, v...) + } else { + msg = s + } + + l.renderWrite(msg, "EMERGENCY") + return } func (l *StdLogger) Err(s string, v ...interface{}) (err error) { + + var msg string + + if v != nil { + msg = fmt.Sprintf(s, v...) + } else { + msg = s + } + + l.renderWrite(msg, "ERROR") + return } func (l *StdLogger) Info(s string, v ...interface{}) (err error) { + + var msg string + + if v != nil { + msg = fmt.Sprintf(s, v...) + } else { + msg = s + } + + l.renderWrite(msg, "INFO") + return } func (l *StdLogger) Notice(s string, v ...interface{}) (err error) { + + var msg string + + if v != nil { + msg = fmt.Sprintf(s, v...) + } else { + msg = s + } + + l.renderWrite(msg, "NOTICE") + return } func (l *StdLogger) Warning(s string, v ...interface{}) (err error) { + + + var msg string + + if v != nil { + msg = fmt.Sprintf(s, v...) + } else { + msg = s + } + + l.renderWrite(msg, "WARNING") + + return +} + +func (l *StdLogger) renderWrite(msg, prio string) { + + s := fmt.Sprintf("[%v] %v", prio, msg) + + l.Logger.Println(s) + return } diff --git a/logging/funcs_sysd.go b/logging/funcs_sysd.go index 0892830..42e842d 100644 --- a/logging/funcs_sysd.go +++ b/logging/funcs_sysd.go @@ -1,5 +1,26 @@ package logging +import ( + `fmt` + `log` + + `github.com/coreos/go-systemd/journal` +) + +func (l *SystemDLogger) Setup() { + + // NOOP + _ = "" + +} + +func (l *SystemDLogger) Shutdown() { + + // NOOP + _ = "" + +} + func (l *SystemDLogger) doDebug(d bool) { l.EnableDebug = d } @@ -9,33 +30,146 @@ func (l *SystemDLogger) setPrefix(prefix string) { } func (l *SystemDLogger) Alert(s string, v ...interface{}) (err error) { + + var msg string + + if v != nil { + msg = fmt.Sprintf(s, v...) + } else { + msg = s + } + + l.renderWrite(msg, journal.PriAlert) + return } func (l *SystemDLogger) Crit(s string, v ...interface{}) (err error) { + + var msg string + + if v != nil { + msg = fmt.Sprintf(s, v...) + } else { + msg = s + } + + l.renderWrite(msg, journal.PriCrit) + return } func (l *SystemDLogger) Debug(s string, v ...interface{}) (err error) { + + if !l.EnableDebug { + return + } + + var msg string + + if v != nil { + msg = fmt.Sprintf(s, v...) + } else { + msg = s + } + + l.renderWrite(msg, journal.PriDebug) + return } func (l *SystemDLogger) Emerg(s string, v ...interface{}) (err error) { + + + var msg string + + if v != nil { + msg = fmt.Sprintf(s, v...) + } else { + msg = s + } + + l.renderWrite(msg, journal.PriEmerg) + return } func (l *SystemDLogger) Err(s string, v ...interface{}) (err error) { + + var msg string + + if v != nil { + msg = fmt.Sprintf(s, v...) + } else { + msg = s + } + + l.renderWrite(msg, journal.PriErr) + return } func (l *SystemDLogger) Info(s string, v ...interface{}) (err error) { + + var msg string + + if v != nil { + msg = fmt.Sprintf(s, v...) + } else { + msg = s + } + + l.renderWrite(msg, journal.PriInfo) + return } func (l *SystemDLogger) Notice(s string, v ...interface{}) (err error) { + + var msg string + + if v != nil { + msg = fmt.Sprintf(s, v...) + } else { + msg = s + } + + l.renderWrite(msg, journal.PriNotice) + return } func (l *SystemDLogger) Warning(s string, v ...interface{}) (err error) { + + + var msg string + + if v != nil { + msg = fmt.Sprintf(s, v...) + } else { + msg = s + } + + l.renderWrite(msg, journal.PriWarning) + + return +} + +func (l *SystemDLogger) renderWrite(msg string, prio journal.Priority) { + // TODO: implement code line, etc. + // https://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html + // CODE_FILE=, CODE_LINE=, CODE_FUNC= + var err error + + vars := map[string]string{ + "DOCUMENTATION": "https://git.r00t2.io/Go_GoUtils/", + "SYSLOG_IDENTIFIER": l.Prefix, + "SYSLOG_FACILITY": "1", // USER + } + + if err = journal.Send(msg, prio, vars); err != nil { + log.Panicln("could not send to Journald") + } + return } diff --git a/logging/funcs_syslog.go b/logging/funcs_syslog.go index 1122202..522c553 100644 --- a/logging/funcs_syslog.go +++ b/logging/funcs_syslog.go @@ -1,41 +1,198 @@ package logging +import ( + `fmt` + `log` + `log/syslog` +) + +func (l *SyslogLogger) Setup() { + + var err error + + if l.alert, err = syslog.New(syslog.LOG_ALERT | syslogFacility, l.Prefix); err != nil { + log.Panicln("could not open log for Alert") + } + if l.crit, err = syslog.New(syslog.LOG_CRIT | syslogFacility, l.Prefix); err != nil { + log.Panicln("could not open log for Crit") + } + if l.debug, err = syslog.New(syslog.LOG_DEBUG | syslogFacility, l.Prefix); err != nil { + log.Panicln("could not open log for Debug") + } + if l.emerg, err = syslog.New(syslog.LOG_EMERG | syslogFacility, l.Prefix); err != nil { + log.Panicln("could not open log for Emerg") + } + if l.err, err = syslog.New(syslog.LOG_ERR | syslogFacility, l.Prefix); err != nil { + log.Panicln("could not open log for Err") + } + if l.info, err = syslog.New(syslog.LOG_INFO | syslogFacility, l.Prefix); err != nil { + log.Panicln("could not open log for Info") + } + if l.notice, err = syslog.New(syslog.LOG_NOTICE | syslogFacility, l.Prefix); err != nil { + log.Panicln("could not open log for Notice") + } + if l.warning, err = syslog.New(syslog.LOG_WARNING | syslogFacility, l.Prefix); err != nil { + log.Panicln("could not open log for Warning") + } + +} + +func (l *SyslogLogger) Shutdown() { + + var err error + + for _, i := range []*syslog.Writer{l.alert, l.crit, l.debug, l.emerg, l.err, l.info, l.notice, l.warning} { + if err = i.Close(); err != nil { + log.Panicf("could not close log %#v\n", i) + } + } + +} + func (l *SyslogLogger) doDebug(d bool) { l.EnableDebug = d } func (l *SyslogLogger) setPrefix(prefix string) { l.Prefix = prefix + l.Setup() } func (l *SyslogLogger) Alert(s string, v ...interface{}) (err error) { + + var msg string + + if v != nil { + msg = fmt.Sprintf(s, v...) + } else { + msg = s + } + + if _, err = l.alert.Write([]byte(msg)); err != nil { + log.Panicf("could not write %v to syslog USER:ALERT facility/level", msg) + } + return } func (l *SyslogLogger) Crit(s string, v ...interface{}) (err error) { + + var msg string + + if v != nil { + msg = fmt.Sprintf(s, v...) + } else { + msg = s + } + + if _, err = l.crit.Write([]byte(msg)); err != nil { + log.Panicf("could not write %v to syslog USER:CRIT facility/level", msg) + } + return } func (l *SyslogLogger) Debug(s string, v ...interface{}) (err error) { + + if !l.EnableDebug { + return + } + + var msg string + + if v != nil { + msg = fmt.Sprintf(s, v...) + } else { + msg = s + } + + if _, err = l.debug.Write([]byte(msg)); err != nil { + log.Panicf("could not write %v to syslog USER:DEBUG facility/level", msg) + } + return } func (l *SyslogLogger) Emerg(s string, v ...interface{}) (err error) { + + var msg string + + if v != nil { + msg = fmt.Sprintf(s, v...) + } else { + msg = s + } + + if _, err = l.emerg.Write([]byte(msg)); err != nil { + log.Panicf("could not write %v to syslog USER:EMERG facility/level", msg) + } + return } func (l *SyslogLogger) Err(s string, v ...interface{}) (err error) { + + var msg string + + if v != nil { + msg = fmt.Sprintf(s, v...) + } else { + msg = s + } + + if _, err = l.err.Write([]byte(msg)); err != nil { + log.Panicf("could not write %v to syslog USER:ERR facility/level", msg) + } + return } func (l *SyslogLogger) Info(s string, v ...interface{}) (err error) { + + var msg string + + if v != nil { + msg = fmt.Sprintf(s, v...) + } else { + msg = s + } + + if _, err = l.info.Write([]byte(msg)); err != nil { + log.Panicf("could not write %v to syslog USER:INFO facility/level", msg) + } + return } func (l *SyslogLogger) Notice(s string, v ...interface{}) (err error) { + + var msg string + + if v != nil { + msg = fmt.Sprintf(s, v...) + } else { + msg = s + } + + if _, err = l.notice.Write([]byte(msg)); err != nil { + log.Panicf("could not write %v to syslog USER:NOTICE facility/level", msg) + } + return } func (l *SyslogLogger) Warning(s string, v ...interface{}) (err error) { + var msg string + + if v != nil { + msg = fmt.Sprintf(s, v...) + } else { + msg = s + } + + if _, err = l.warning.Write([]byte(msg)); err != nil { + log.Panicf("could not write %v to syslog USER:WARNING facility/level", msg) + } + return } diff --git a/logging/types.go b/logging/types.go index 1ee8cb9..a157414 100644 --- a/logging/types.go +++ b/logging/types.go @@ -3,6 +3,7 @@ package logging import ( `log` `log/syslog` + `os` ) type Logger interface { @@ -16,6 +17,8 @@ type Logger interface { Warning(string, ...interface{}) error doDebug(bool) setPrefix(string) + Setup() + Shutdown() } type SystemDLogger struct { @@ -24,21 +27,26 @@ type SystemDLogger struct { } type SyslogLogger struct { - syslog.Writer EnableDebug bool Prefix string + alert, + crit, + debug, + emerg, + err, + info, + notice, + warning *syslog.Writer } type StdLogger struct { - log.Logger + *log.Logger EnableDebug bool Prefix string } type FileLogger struct { - log.Logger StdLogger - EnableDebug bool - Path string - Prefix string + Path string + writer *os.File } diff --git a/types/bitmasks.go b/types/bitmasks.go index 5c5451f..f5888b7 100644 --- a/types/bitmasks.go +++ b/types/bitmasks.go @@ -10,17 +10,6 @@ type BitMask interface { // BitMasks type MaskBit uint8 -// LDAP Connection flags -const ( - LdapBindUndefined MaskBit = 1 << iota - LdapBindNone // GSSAPI via SASL or (TODO) Anonymous bind - LdapBindNet - LdapBindTls - LdapBindStartTls - LdapBindSasl - LdapBindNoPassword -) - func (f MaskBit) HasFlag(flag MaskBit) (r bool) { if f&flag != 0 { r = true