Compare commits

..

No commits in common. "master" and "v1.5.0" have entirely different histories.

28 changed files with 122 additions and 567 deletions

View File

@ -2,8 +2,8 @@ package bitmask
import (
"bytes"
"encoding/binary"
"errors"
"encoding/binary"
"math/bits"
)
@ -56,7 +56,7 @@ func (m *MaskBit) AddFlag(flag MaskBit) {
// ClearFlag removes MaskBit flag from m.
func (m *MaskBit) ClearFlag(flag MaskBit) {
*m &^= flag
*m &= flag
return
}
@ -109,15 +109,6 @@ func (m *MaskBit) Bytes(trim bool) (b []byte) {
return
}
// Copy returns a pointer to a (new) copy of a MaskBit.
func (m *MaskBit) Copy() (newM *MaskBit) {
newM = new(MaskBit)
*newM = *m
return
}
// Value returns the current raw uint value of a MaskBit.
func (m *MaskBit) Value() (v uint) {

View File

@ -1,9 +1,3 @@
- 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

View File

@ -12,16 +12,3 @@ const (
// appendFlags are the flags used for testing the file (and opening/writing).
appendFlags int = os.O_APPEND | os.O_CREATE | os.O_WRONLY
)
const PriorityNone logPrio = 0
const (
PriorityEmergency logPrio = 1 << iota
PriorityAlert
PriorityCritical
PriorityError
PriorityWarning
PriorityNotice
PriorityInformational
PriorityDebug
)
const PriorityAll logPrio = PriorityEmergency | PriorityAlert | PriorityCritical | PriorityError | PriorityWarning | PriorityNotice | PriorityInformational | PriorityDebug

View File

@ -1,9 +0,0 @@
package logging
var (
// defLogPaths indicates default log paths.
defLogPaths = []string{
"/var/log/golang/program.log",
"~/Library/Logs/Golang/program.log",
}
)

View File

@ -1,5 +1,32 @@
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{

View File

@ -1,34 +0,0 @@
//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
)

View File

@ -7,7 +7,7 @@ These particular loggers (logging.Logger) available are:
StdLogger
FileLogger
SystemDLogger (Linux only)
SyslogLogger (Linux/macOS/other *NIX-like only)
SyslogLogger (Linux 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.

View File

@ -1,10 +1,18 @@
package logging
import (
"errors"
`errors`
`fmt`
)
var (
// ErrNoSysD indicates that the user attempted to add a SystemDLogger to a MultiLogger but systemd is unavailable.
ErrNoSysD error = errors.New("a systemd (journald) Logger was requested but systemd is unavailable on this system")
// ErrNoSyslog indicates that the user attempted to add a SyslogLogger to a MultiLogger but syslog's logger device is unavailable.
ErrNoSyslog error = errors.New("a Syslog Logger was requested but Syslog is unavailable on this system")
/*
ErrInvalidDevLog indicates that the user attempted to add a SyslogLogger to a MultiLogger but
the Syslog char device file is... not actually a char device file.
*/
ErrInvalidDevLog error = errors.New(fmt.Sprintf("a Syslog Logger was requested but %v is not a valid logger handle", devlog))
)

View File

@ -1,19 +0,0 @@
//go:build !(windows || plan9 || wasip1 || js || ios)
// +build !windows,!plan9,!wasip1,!js,!ios
package logging
import (
"errors"
"fmt"
)
var (
// ErrNoSyslog indicates that the user attempted to add a SyslogLogger to a MultiLogger but syslog's logger device is unavailable.
ErrNoSyslog error = errors.New("a Syslog Logger was requested but Syslog is unavailable on this system")
/*
ErrInvalidDevLog indicates that the user attempted to add a SyslogLogger to a MultiLogger but
the Syslog char device file is... not actually a char device file.
*/
ErrInvalidDevLog error = errors.New(fmt.Sprintf("a Syslog Logger was requested but %v is not a valid logger handle", devlog))
)

View File

@ -220,14 +220,6 @@ func (l *FileLogger) Warning(s string, v ...interface{}) (err error) {
return
}
// ToLogger returns a stdlib log.Logger.
func (l *FileLogger) ToLogger(prio logPrio) (stdLibLog *log.Logger) {
stdLibLog = log.New(&logWriter{backend: l, prio: prio}, "", 0)
return
}
// renderWrite prepares/formats a log message to be written to this FileLogger.
func (l *FileLogger) renderWrite(msg, prio string) {

View File

@ -1,23 +0,0 @@
package logging
import (
`r00t2.io/goutils/bitmask`
)
// HasFlag provides a wrapper for functionality to the underlying bitmask.MaskBit.
func (l *logPrio) HasFlag(prio logPrio) (hasFlag bool) {
var m *bitmask.MaskBit
var p *bitmask.MaskBit
if l == nil {
return
}
m = bitmask.NewMaskBitExplicit(uint(*l))
p = bitmask.NewMaskBitExplicit(uint(prio))
hasFlag = m.HasFlag(*p)
return
}

View File

@ -1,74 +0,0 @@
package logging
import (
`r00t2.io/goutils/multierr`
)
// Write writes bytes b to the underlying Logger's priority level if the logWriter's priority level(s) match.
func (l *logWriter) Write(b []byte) (n int, err error) {
var s string
var mErr *multierr.MultiError = multierr.NewMultiError(nil)
if b == nil {
return
}
s = string(b)
if l.prio.HasFlag(PriorityEmergency) {
if err = l.backend.Emerg(s); err != nil {
mErr.AddError(err)
err = nil
}
}
if l.prio.HasFlag(PriorityAlert) {
if err = l.backend.Alert(s); err != nil {
mErr.AddError(err)
err = nil
}
}
if l.prio.HasFlag(PriorityCritical) {
if err = l.backend.Crit(s); err != nil {
mErr.AddError(err)
err = nil
}
}
if l.prio.HasFlag(PriorityError) {
if err = l.backend.Err(s); err != nil {
mErr.AddError(err)
err = nil
}
}
if l.prio.HasFlag(PriorityWarning) {
if err = l.backend.Warning(s); err != nil {
mErr.AddError(err)
err = nil
}
}
if l.prio.HasFlag(PriorityNotice) {
if err = l.backend.Notice(s); err != nil {
mErr.AddError(err)
err = nil
}
}
if l.prio.HasFlag(PriorityInformational) {
if err = l.backend.Info(s); err != nil {
mErr.AddError(err)
err = nil
}
}
if l.prio.HasFlag(PriorityDebug) {
if err = l.backend.Debug(s); err != nil {
mErr.AddError(err)
err = nil
}
}
if !mErr.IsEmpty() {
err = mErr
return
}
return
}

View File

@ -3,7 +3,6 @@ package logging
import (
"errors"
"fmt"
`log`
"sync"
"r00t2.io/goutils/multierr"
@ -371,11 +370,3 @@ func (m *MultiLogger) Warning(s string, v ...interface{}) (err error) {
return
}
// ToLogger returns a stdlib log.Logger.
func (m *MultiLogger) ToLogger(prio logPrio) (stdLibLog *log.Logger) {
stdLibLog = log.New(&logWriter{backend: m, prio: prio}, "", 0)
return
}

View File

@ -1,8 +1,11 @@
package logging
import (
sysd "github.com/coreos/go-systemd/journal"
"github.com/google/uuid"
`os`
sysd `github.com/coreos/go-systemd/journal`
`github.com/google/uuid`
`r00t2.io/sysutils/paths`
)
/*
@ -77,3 +80,55 @@ 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
}

View File

@ -1,63 +0,0 @@
//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
}

View File

@ -1,41 +0,0 @@
//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 (
"github.com/google/uuid"
)
/*
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.
See the documentation for GetLogger for details on other arguments.
*/
func (m *MultiLogger) AddDefaultLogger(identifier string, logFlags int, logPaths ...string) (err error) {
var l Logger
var exists bool
if identifier == "" {
identifier = uuid.New().String()
}
if _, exists = m.Loggers[identifier]; exists {
err = ErrExistingLogger
return
}
if l, err = GetLogger(m.EnableDebug, m.Prefix, logFlags, logPaths...); err != nil {
return
}
m.Loggers[identifier] = l
return
}

View File

@ -1,9 +1,5 @@
package logging
import (
`log`
)
// Setup does nothing at all; it's here for interface compat. 🙃
func (l *NullLogger) Setup() (err error) {
return
@ -76,11 +72,3 @@ func (l *NullLogger) Notice(s string, v ...interface{}) (err error) {
func (l *NullLogger) Warning(s string, v ...interface{}) (err error) {
return
}
// ToLogger returns a stdlib log.Logger (that doesn't actually write to anything).
func (l *NullLogger) ToLogger(prio logPrio) (stdLibLog *log.Logger) {
stdLibLog = log.New(&nullWriter{}, "", 0)
return
}

View File

@ -1,12 +0,0 @@
package logging
// nulLWriter writes... nothing. To avoid errors, however, in downstream code it pretends it does (n will *always* == len(b)).
func (nw *nullWriter) Write(b []byte) (n int, err error) {
if b == nil {
return
}
n = len(b)
return
}

View File

@ -1,138 +0,0 @@
//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 (
_ = 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
}

View File

@ -244,11 +244,3 @@ func (l *StdLogger) renderWrite(msg, prio string) {
return
}
// ToLogger returns a stdlib log.Logger.
func (l *StdLogger) ToLogger(prio logPrio) (stdLibLog *log.Logger) {
stdLibLog = log.New(&logWriter{backend: l, prio: prio}, "", 0)
return
}

View File

@ -223,11 +223,3 @@ func (l *SystemDLogger) renderWrite(msg string, prio journal.Priority) {
return
}
// ToLogger returns a stdlib log.Logger.
func (l *SystemDLogger) ToLogger(prio logPrio) (stdLibLog *log.Logger) {
stdLibLog = log.New(&logWriter{backend: l, prio: prio}, "", 0)
return
}

View File

@ -1,6 +1,3 @@
//go:build !(windows || plan9 || wasip1 || js || ios)
// +build !windows,!plan9,!wasip1,!js,!ios
package logging
import (
@ -269,11 +266,3 @@ func (l *SyslogLogger) Warning(s string, v ...interface{}) (err error) {
return
}
// ToLogger returns a stdlib log.Logger.
func (l *SyslogLogger) ToLogger(prio logPrio) (stdLibLog *log.Logger) {
stdLibLog = log.New(&logWriter{backend: l, prio: prio}, "", 0)
return
}

View File

@ -3,7 +3,6 @@ package logging
import (
"errors"
"fmt"
`log`
"os"
"os/exec"
"syscall"
@ -343,11 +342,3 @@ func (l *WinLogger) Warning(s string, v ...interface{}) (err error) {
return
}
// ToLogger returns a stdlib log.Logger.
func (l *WinLogger) ToLogger(prio logPrio) (stdLibLog *log.Logger) {
stdLibLog = log.New(&logWriter{backend: l, prio: prio}, "", 0)
return
}

View File

@ -3,12 +3,8 @@ package logging
import (
"log"
"os"
`r00t2.io/goutils/bitmask`
)
type logPrio bitmask.MaskBit
/*
Logger is one of the various loggers offered by this module.
*/
@ -27,7 +23,6 @@ type Logger interface {
GetPrefix() (p string, err error)
Setup() (err error)
Shutdown() (err error)
ToLogger(prio logPrio) (stdLibLog *log.Logger)
}
/*
@ -110,12 +105,3 @@ type MultiLogger struct {
*/
Loggers map[string]Logger
}
// logWriter is used as a log.Logger and is returned by <Logger>.ToLogger.
type logWriter struct {
backend Logger
prio logPrio
}
// nullWriter is used as a shortcut by NullLogger.ToLogger.
type nullWriter struct{}

View File

@ -1,5 +1,9 @@
package logging
import (
`log/syslog`
)
/*
SystemDLogger (yes, I'm aware it's actually written as "systemd") writes to journald on systemd-enabled systems.
*/
@ -7,3 +11,17 @@ 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
}

View File

@ -1,22 +0,0 @@
//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
}

View File

@ -69,9 +69,6 @@ func (e *MultiError) Error() (errStr string) {
numErrs = len(e.Errors)
}
e.lock.Lock()
defer e.lock.Unlock()
for idx, err := range e.Errors {
if (idx + 1) < numErrs {
errStr += fmt.Sprintf("%v%v", err.Error(), e.ErrorSep)
@ -90,9 +87,6 @@ func (e *MultiError) AddError(err error) {
return
}
e.lock.Lock()
defer e.lock.Unlock()
e.Errors = append(e.Errors, err)
}

View File

@ -1,14 +1,9 @@
package multierr
import (
`sync`
)
// MultiError is a type of error.Error that can contain multiple errors.
type MultiError struct {
// Errors is a slice of errors to combine/concatenate when .Error() is called.
Errors []error `json:"errors"`
// ErrorSep is a string to use to separate errors for .Error(). The default is "\n".
ErrorSep string `json:"separator"`
lock sync.Mutex
}