diff --git a/logging/TODO b/logging/TODO index 9d64324..89b356f 100644 --- a/logging/TODO +++ b/logging/TODO @@ -1,3 +1,9 @@ +- macOS support beyond the legacy NIX stuff. it apparently uses something called "ULS", "Unified Logging System". +-- https://developer.apple.com/documentation/os/logging +-- https://developer.apple.com/documentation/os/generating-log-messages-from-your-code +-- no native Go support (yet)? +--- https://developer.apple.com/forums/thread/773369 + - Implement code line/func/etc. (only for debug?): https://stackoverflow.com/a/24809646 https://golang.org/pkg/runtime/#Caller diff --git a/logging/consts_darwin.go b/logging/consts_darwin.go new file mode 100644 index 0000000..c155eaf --- /dev/null +++ b/logging/consts_darwin.go @@ -0,0 +1,9 @@ +package logging + +var ( + // defLogPaths indicates default log paths. + defLogPaths = []string{ + "/var/log/golang/program.log", + "~/Library/Logs/Golang/program.log", + } +) diff --git a/logging/consts_linux.go b/logging/consts_linux.go index 354947d..6b2c635 100644 --- a/logging/consts_linux.go +++ b/logging/consts_linux.go @@ -1,32 +1,5 @@ package logging -import ( - `log/syslog` - - `r00t2.io/goutils/bitmask` -) - -const ( - // devlog is the path to the syslog char device. - devlog string = "/dev/log" - // syslogFacility is the facility to use; it's a little like a context or scope if you think of it in those terms. - syslogFacility syslog.Priority = syslog.LOG_USER -) - -// Flags for logger configuration. These are used internally. -const ( - // LogUndefined indicates an undefined Logger type. - LogUndefined bitmask.MaskBit = 1 << iota - // LogJournald flags a SystemDLogger Logger type. - LogJournald - // LogSyslog flags a SyslogLogger Logger type. - LogSyslog - // LogFile flags a FileLogger Logger type. - LogFile - // LogStdout flags a StdLogger Logger type. - LogStdout -) - var ( // defLogPaths indicates default log paths. defLogPaths = []string{ diff --git a/logging/consts_nix.go b/logging/consts_nix.go new file mode 100644 index 0000000..485c445 --- /dev/null +++ b/logging/consts_nix.go @@ -0,0 +1,34 @@ +//go:build !(windows || plan9 || wasip1 || js || ios) +// +build !windows,!plan9,!wasip1,!js,!ios + +// I mean maybe it works for plan9 and ios, I don't know. + +package logging + +import ( + "log/syslog" + + "r00t2.io/goutils/bitmask" +) + +const ( + // devlog is the path to the syslog char device. + devlog string = "/dev/log" + // syslogFacility is the facility to use; it's a little like a context or scope if you think of it in those terms. + syslogFacility syslog.Priority = syslog.LOG_USER +) + +// Flags for logger configuration. These are used internally. + +// LogUndefined indicates an undefined Logger type. +const LogUndefined bitmask.MaskBit = iota +const ( + // LogJournald flags a SystemDLogger Logger type. + LogJournald = 1 << iota + // LogSyslog flags a SyslogLogger Logger type. + LogSyslog + // LogFile flags a FileLogger Logger type. + LogFile + // LogStdout flags a StdLogger Logger type. + LogStdout +) diff --git a/logging/doc.go b/logging/doc.go index 0e42005..44fc129 100644 --- a/logging/doc.go +++ b/logging/doc.go @@ -7,7 +7,7 @@ These particular loggers (logging.Logger) available are: StdLogger FileLogger SystemDLogger (Linux only) - SyslogLogger (Linux only) + SyslogLogger (Linux/macOS/other *NIX-like only) WinLogger (Windows only) There is a seventh type of logging.Logger, MultiLogger, that allows for multiple loggers to be written to with a single call. diff --git a/logging/funcs_multilogger_mgr_linux.go b/logging/funcs_multilogger_mgr_linux.go index 3a1ab90..0205b3b 100644 --- a/logging/funcs_multilogger_mgr_linux.go +++ b/logging/funcs_multilogger_mgr_linux.go @@ -1,20 +1,17 @@ package logging import ( - `os` - - sysd `github.com/coreos/go-systemd/journal` - `github.com/google/uuid` - `r00t2.io/sysutils/paths` + sysd "github.com/coreos/go-systemd/journal" + "github.com/google/uuid" ) /* - AddDefaultLogger adds a default Logger (as would be determined by GetLogger) to a MultiLogger. +AddDefaultLogger adds a default Logger (as would be determined by GetLogger) to a MultiLogger. - identifier is a string to use to identify the added Logger in MultiLogger.Loggers. - If empty, one will be automatically generated. +identifier is a string to use to identify the added Logger in MultiLogger.Loggers. +If empty, one will be automatically generated. - See the documentation for GetLogger for details on other arguments. +See the documentation for GetLogger for details on other arguments. */ func (m *MultiLogger) AddDefaultLogger(identifier string, logFlags int, logPaths ...string) (err error) { @@ -40,10 +37,10 @@ func (m *MultiLogger) AddDefaultLogger(identifier string, logFlags int, logPaths } /* - AddSysdLogger adds a SystemDLogger to a MultiLogger. +AddSysdLogger adds a SystemDLogger to a MultiLogger. - identifier is a string to use to identify the added SystemDLogger in MultiLogger.Loggers. - If empty, one will be automatically generated. +identifier is a string to use to identify the added SystemDLogger in MultiLogger.Loggers. +If empty, one will be automatically generated. */ func (m *MultiLogger) AddSysdLogger(identifier string) (err error) { @@ -80,55 +77,3 @@ func (m *MultiLogger) AddSysdLogger(identifier string) (err error) { return } - -/* - AddSyslogLogger adds a SyslogLogger to a MultiLogger. - - identifier is a string to use to identify the added SyslogLogger in MultiLogger.Loggers. - If empty, one will be automatically generated. -*/ -func (m *MultiLogger) AddSyslogLogger(identifier string) (err error) { - - var exists bool - var hasSyslog bool - var stat os.FileInfo - var devlogPath string = devlog - var prefix string - - if identifier == "" { - identifier = uuid.New().String() - } - - if _, exists = m.Loggers[identifier]; exists { - err = ErrExistingLogger - return - } - - if hasSyslog, stat, err = paths.RealPathExistsStat(&devlogPath); hasSyslog && err != nil { - return - } else if !hasSyslog { - err = ErrNoSyslog - return - } - - if stat.Mode().IsRegular() { - err = ErrInvalidDevLog - return - } - - m.Loggers[identifier] = &SyslogLogger{ - EnableDebug: m.EnableDebug, - Prefix: m.Prefix, - } - if err = m.Loggers[identifier].Setup(); err != nil { - return - } - - if prefix, err = m.Loggers[identifier].GetPrefix(); err != nil { - return - } - - m.Loggers[identifier].Debug("logger initialized of type %T with prefix %v", m.Loggers[identifier], prefix) - - return -} diff --git a/logging/funcs_multilogger_mgr_nix.go b/logging/funcs_multilogger_mgr_nix.go new file mode 100644 index 0000000..32007a9 --- /dev/null +++ b/logging/funcs_multilogger_mgr_nix.go @@ -0,0 +1,63 @@ +//go:build !(windows || plan9 || wasip1 || js || ios) +// +build !windows,!plan9,!wasip1,!js,!ios + +package logging + +import ( + "os" + + "github.com/google/uuid" + "r00t2.io/sysutils/paths" +) + +/* +AddSyslogLogger adds a SyslogLogger to a MultiLogger. + +identifier is a string to use to identify the added SyslogLogger in MultiLogger.Loggers. +If empty, one will be automatically generated. +*/ +func (m *MultiLogger) AddSyslogLogger(identifier string) (err error) { + + var exists bool + var hasSyslog bool + var stat os.FileInfo + var devlogPath string = devlog + var prefix string + + if identifier == "" { + identifier = uuid.New().String() + } + + if _, exists = m.Loggers[identifier]; exists { + err = ErrExistingLogger + return + } + + if hasSyslog, stat, err = paths.RealPathExistsStat(&devlogPath); hasSyslog && err != nil { + return + } else if !hasSyslog { + err = ErrNoSyslog + return + } + + if stat.Mode().IsRegular() { + err = ErrInvalidDevLog + return + } + + m.Loggers[identifier] = &SyslogLogger{ + EnableDebug: m.EnableDebug, + Prefix: m.Prefix, + } + if err = m.Loggers[identifier].Setup(); err != nil { + return + } + + if prefix, err = m.Loggers[identifier].GetPrefix(); err != nil { + return + } + + m.Loggers[identifier].Debug("logger initialized of type %T with prefix %v", m.Loggers[identifier], prefix) + + return +} diff --git a/logging/funcs_oldnix.go b/logging/funcs_oldnix.go new file mode 100644 index 0000000..2416986 --- /dev/null +++ b/logging/funcs_oldnix.go @@ -0,0 +1,139 @@ +//go:build !(windows || plan9 || wasip1 || js || ios || linux) +// +build !windows,!plan9,!wasip1,!js,!ios,!linux + +// Linux is excluded because it has its own. + +package logging + +import ( + native "log" + "os" + "path" + + "r00t2.io/goutils/bitmask" + "r00t2.io/sysutils/paths" +) + +var ( + _ = sysd.Enabled() + _ = native.Logger{} + _ = os.Interrupt +) + +/* +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. + +logConfigFlags is the corresponding flag(s) OR'd for StdLogger.LogFlags / FileLogger.StdLogger.LogFlags if either is selected. See StdLogger.LogFlags and +https://pkg.go.dev/log#pkg-constants for details. + +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. +If you want to log to multiple files simultaneously, use a MultiLogger instead. + +If you call GetLogger, you will only get a single ("best") logger your system supports. +If you want to log to multiple Logger destinations at once (or want to log to an explicit Logger type), +use GetMultiLogger. +*/ +func GetLogger(enableDebug bool, prefix string, logConfigFlags int, logPaths ...string) (logger Logger, err error) { + + var logPath string + var logFlags bitmask.MaskBit + var currentPrefix string + + // Configure system-supported logger(s). + + // If we can detect syslog, use that. If not, try to use a file logger (+ stdout). + // Last ditch, stdout. + var hasSyslog bool + var stat os.FileInfo + var devlogPath string = devlog + + if hasSyslog, stat, err = paths.RealPathExistsStat(&devlogPath); hasSyslog && err != nil { + return + } + + if hasSyslog && !stat.Mode().IsRegular() { + logFlags.AddFlag(LogSyslog) + } else { + var exists bool + var success bool + var ckLogPaths []string + + logFlags.AddFlag(LogStdout) + 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 + } else if !success { + continue + } + logFlags.AddFlag(LogFile) + logPath = p + break + } else { + dirPath := path.Dir(p) + if err = paths.MakeDirIfNotExist(dirPath); err != nil { + continue + } + if success, err = testOpen(p); err != nil { + continue + } else if !success { + continue + } + logFlags.AddFlag(LogFile) + logPath = p + break + } + } + } + + if logFlags.HasFlag(LogSyslog) { + logger = &SyslogLogger{ + Prefix: logPrefix, + EnableDebug: enableDebug, + } + } else { + if logFlags.HasFlag(LogFile) { + logger = &FileLogger{ + StdLogger: StdLogger{ + Prefix: logPrefix, + EnableDebug: enableDebug, + LogFlags: logConfigFlags, + }, + Path: logPath, + } + } else { + logger = &StdLogger{ + Prefix: logPrefix, + EnableDebug: enableDebug, + LogFlags: logConfigFlags, + } + } + } + + if prefix != "\x00" { + if err = logger.SetPrefix(prefix); err != nil { + return + } + } + if err = logger.Setup(); err != nil { + return + } + + if currentPrefix, err = logger.GetPrefix(); err != nil { + return + } + + logger.Debug("logger initialized of type %T with prefix %v", logger, currentPrefix) + + return +} diff --git a/logging/funcs_syslog_linux.go b/logging/funcs_syslog_nix.go similarity index 98% rename from logging/funcs_syslog_linux.go rename to logging/funcs_syslog_nix.go index a75d429..e7e05dd 100644 --- a/logging/funcs_syslog_linux.go +++ b/logging/funcs_syslog_nix.go @@ -1,3 +1,6 @@ +//go:build !(windows || plan9 || wasip1 || js || ios) +// +build !windows,!plan9,!wasip1,!js,!ios + package logging import ( diff --git a/logging/types_linux.go b/logging/types_linux.go index 03f93b2..0b4908a 100644 --- a/logging/types_linux.go +++ b/logging/types_linux.go @@ -1,27 +1,13 @@ package logging import ( - `log/syslog` + "log/syslog" ) /* - SystemDLogger (yes, I'm aware it's actually written as "systemd") writes to journald on systemd-enabled systems. +SystemDLogger (yes, I'm aware it's actually written as "systemd") writes to journald on systemd-enabled systems. */ type SystemDLogger struct { EnableDebug bool Prefix string } - -// SyslogLogger writes to syslog on syslog-enabled systems. -type SyslogLogger struct { - EnableDebug bool - Prefix string - alert, - crit, - debug, - emerg, - err, - info, - notice, - warning *syslog.Writer -} diff --git a/logging/types_nix.go b/logging/types_nix.go new file mode 100644 index 0000000..1d60f4e --- /dev/null +++ b/logging/types_nix.go @@ -0,0 +1,22 @@ +//go:build !(windows || plan9 || wasip1 || js || ios) +// +build !windows,!plan9,!wasip1,!js,!ios + +package logging + +import ( + "log/syslog" +) + +// SyslogLogger writes to syslog on syslog-enabled systems. +type SyslogLogger struct { + EnableDebug bool + Prefix string + alert, + crit, + debug, + emerg, + err, + info, + notice, + warning *syslog.Writer +}