Windows Event Log, error output
Adding more Event Log support, and modifying the loggers so they return errors in their operational functions.
This commit is contained in:
parent
3d0d420454
commit
39e0a1fd43
4
.gitignore
vendored
4
.gitignore
vendored
@ -28,6 +28,10 @@
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
# But DO include the actual tests.
|
||||
!_test.go
|
||||
!*_test.go
|
||||
!*_test/
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
@ -2,10 +2,9 @@
|
||||
https://stackoverflow.com/a/24809646
|
||||
https://golang.org/pkg/runtime/#Caller
|
||||
|
||||
- Support simultaneous writing to multiple Loggers.
|
||||
|
||||
- Suport remote loggers? (eventlog, syslog, systemd)
|
||||
|
||||
- DOCS.
|
||||
-- Done, but flesh out.
|
||||
|
||||
- Unit/Integration tests.
|
||||
|
@ -3,7 +3,6 @@ package logging
|
||||
import (
|
||||
`os`
|
||||
`path/filepath`
|
||||
`regexp`
|
||||
|
||||
`r00t2.io/goutils/bitmask`
|
||||
)
|
||||
@ -28,8 +27,21 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
// ptrnSourceExists is a regex pattern to check for a registry entry (Event Log entry) already existing.
|
||||
var ptrnSourceExists *regexp.Regexp = regexp.MustCompile(`registry\skey\salready\sexists$`)
|
||||
/*
|
||||
ptrnSourceExists is a regex pattern to check for a registry entry (Event Log entry) already existing.
|
||||
|
||||
Deprecated: this is handled differently now.
|
||||
*/
|
||||
// var ptrnSourceExists *regexp.Regexp = regexp.MustCompile(`registry\skey\salready\sexists$`)
|
||||
|
||||
const (
|
||||
EIDMin uint32 = 1
|
||||
EIDMax uint32 = 1000
|
||||
)
|
||||
|
||||
const (
|
||||
eventLogRegistryKey string = "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application"
|
||||
)
|
||||
|
||||
// Default WinEventID, (can be) used in GetLogger and MultiLogger.AddWinLogger.
|
||||
var DefaultEventID *WinEventID = &WinEventID{
|
||||
|
12
logging/errs_windows.go
Normal file
12
logging/errs_windows.go
Normal file
@ -0,0 +1,12 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
`errors`
|
||||
`fmt`
|
||||
)
|
||||
|
||||
var (
|
||||
ErrBadBinPath error = errors.New("evaluated binary path does not actually exist")
|
||||
ErrBadPerms error = errors.New("access denied when attempting to register Event Log source")
|
||||
ErrBadEid error = errors.New(fmt.Sprintf("event IDs must be between %v and %v inclusive", EIDMin, EIDMax))
|
||||
)
|
@ -1,61 +1,96 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
`errors`
|
||||
"fmt"
|
||||
"io"
|
||||
`io/fs`
|
||||
"log"
|
||||
"os"
|
||||
`strings`
|
||||
)
|
||||
|
||||
// Setup sets up/configures a FileLogger and prepares it for use.
|
||||
func (l *FileLogger) Setup() {
|
||||
func (l *FileLogger) Setup() (err error) {
|
||||
|
||||
var err error
|
||||
var multi io.Writer
|
||||
|
||||
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)
|
||||
// This uses a shared handle across the import. We don't want that.
|
||||
// l.Logger = log.Default()
|
||||
if l.Prefix != "" {
|
||||
l.Prefix = strings.TrimRight(l.Prefix, " ") + " "
|
||||
// l.Logger.SetPrefix(l.Prefix)
|
||||
}
|
||||
if l.writer, err = os.OpenFile(l.Path, appendFlags, logPerm); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/a/36719588/733214
|
||||
if l.EnableStdOut {
|
||||
switch {
|
||||
case l.EnableStdErr && l.EnableStdOut:
|
||||
multi = io.MultiWriter(os.Stdout, os.Stderr, l.writer)
|
||||
case l.EnableStdErr:
|
||||
multi = io.MultiWriter(os.Stderr, l.writer)
|
||||
case l.EnableStdOut:
|
||||
multi = io.MultiWriter(os.Stdout, l.writer)
|
||||
} else {
|
||||
default:
|
||||
multi = l.writer
|
||||
}
|
||||
l.Logger.SetOutput(multi)
|
||||
|
||||
l.Logger = log.New(multi, l.Prefix, l.LogFlags)
|
||||
// l.Logger.SetOutput(multi)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Shutdown cleanly shuts down a FileLogger.
|
||||
func (l *FileLogger) Shutdown() {
|
||||
|
||||
var err error
|
||||
func (l *FileLogger) Shutdown() (err error) {
|
||||
|
||||
if err = l.writer.Close(); err != nil {
|
||||
log.Panicf("could not close log file \"%v\": %v\n", l.Path, err)
|
||||
if !errors.Is(err, fs.ErrClosed) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetPrefix returns the prefix used by this FileLogger.
|
||||
func (l *FileLogger) GetPrefix() string {
|
||||
return l.Prefix
|
||||
/*
|
||||
GetPrefix returns the prefix used by this FileLogger.
|
||||
err will always be nil; it's there for interface-compat.
|
||||
*/
|
||||
func (l *FileLogger) GetPrefix() (prefix string, err error) {
|
||||
|
||||
prefix = l.Prefix
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
DoDebug sets the debug state of this FileLogger.
|
||||
Note that this merely acts as a *safety filter* for debug messages to avoid sensitive information being written to the log.
|
||||
err will always be nil; it's there for interface-compat.
|
||||
*/
|
||||
func (l *FileLogger) DoDebug(d bool) {
|
||||
func (l *FileLogger) DoDebug(d bool) (err error) {
|
||||
|
||||
l.EnableDebug = d
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// SetPrefix sets the prefix for this FileLogger.
|
||||
func (l *FileLogger) SetPrefix(prefix string) {
|
||||
/*
|
||||
SetPrefix sets the prefix for this FileLogger.
|
||||
err will always be nil; it's there for interface-compat.
|
||||
*/
|
||||
func (l *FileLogger) SetPrefix(prefix string) (err error) {
|
||||
|
||||
l.Prefix = prefix + " "
|
||||
l.Logger.SetPrefix(prefix + " ")
|
||||
l.Prefix = prefix
|
||||
if prefix != "" {
|
||||
l.Prefix = strings.TrimRight(l.Prefix, " ") + " "
|
||||
}
|
||||
l.Logger.SetPrefix(l.Prefix)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Alert writes an ALERT-level message to this FileLogger.
|
||||
|
@ -24,6 +24,9 @@ var (
|
||||
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.
|
||||
@ -32,10 +35,11 @@ var (
|
||||
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, logPaths ...string) (logger Logger, err error) {
|
||||
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 sysd.Enabled() {
|
||||
@ -109,6 +113,7 @@ func GetLogger(enableDebug bool, prefix string, logPaths ...string) (logger Logg
|
||||
StdLogger: StdLogger{
|
||||
Prefix: logPrefix,
|
||||
EnableDebug: enableDebug,
|
||||
LogFlags: logConfigFlags,
|
||||
},
|
||||
Path: logPath,
|
||||
}
|
||||
@ -116,17 +121,26 @@ func GetLogger(enableDebug bool, prefix string, logPaths ...string) (logger Logg
|
||||
logger = &StdLogger{
|
||||
Prefix: logPrefix,
|
||||
EnableDebug: enableDebug,
|
||||
LogFlags: logConfigFlags,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.Setup()
|
||||
if prefix != "\x00" {
|
||||
logger.SetPrefix(prefix)
|
||||
if err = logger.SetPrefix(prefix); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if err = logger.Setup(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
logger.Info("logger initialized of type %T with prefix %v", logger, logger.GetPrefix())
|
||||
if currentPrefix, err = logger.GetPrefix(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
logger.Debug("logger initialized of type %T with prefix %v", logger, currentPrefix)
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -7,41 +7,70 @@ import (
|
||||
)
|
||||
|
||||
// Setup sets up/configures a MultiLogger (and all its MultiLogger.Loggers) and prepares it for use.
|
||||
func (m *MultiLogger) Setup() {
|
||||
func (m *MultiLogger) Setup() (err error) {
|
||||
|
||||
var wg sync.WaitGroup
|
||||
var errs *multierr.MultiError = multierr.NewMultiError(nil)
|
||||
|
||||
for _, l := range m.Loggers {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
var err2 error
|
||||
defer wg.Done()
|
||||
l.Setup()
|
||||
if err2 = l.Setup(); err2 != nil {
|
||||
errs.AddError(err2)
|
||||
err2 = nil
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
if errs.Count() > 0 {
|
||||
err = errs
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Shutdown cleanly shuts down a MultiLogger (and all its MultiLogger.Loggers).
|
||||
func (m *MultiLogger) Shutdown() {
|
||||
func (m *MultiLogger) Shutdown() (err error) {
|
||||
|
||||
var wg sync.WaitGroup
|
||||
var errs *multierr.MultiError = multierr.NewMultiError(nil)
|
||||
|
||||
for _, l := range m.Loggers {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
var err2 error
|
||||
defer wg.Done()
|
||||
l.Shutdown()
|
||||
if err2 = l.Shutdown(); err2 != nil {
|
||||
errs.AddError(err2)
|
||||
err2 = nil
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
if errs.Count() > 0 {
|
||||
err = errs
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetPrefix returns the prefix used by this MultiLogger (and all its MultiLogger.Loggers).
|
||||
func (m *MultiLogger) GetPrefix() string {
|
||||
/*
|
||||
GetPrefix returns the prefix used by this MultiLogger (and all its MultiLogger.Loggers).
|
||||
err will always be nil; it's there for interface-compat.
|
||||
*/
|
||||
func (m *MultiLogger) GetPrefix() (prefix string, err error) {
|
||||
|
||||
return m.Prefix
|
||||
prefix = m.Prefix
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
@ -51,21 +80,33 @@ func (m *MultiLogger) GetPrefix() string {
|
||||
|
||||
If you had a logger-specific EnableDebug set, you will need to re-set it to your desired state after running this method.
|
||||
*/
|
||||
func (m *MultiLogger) DoDebug(d bool) {
|
||||
func (m *MultiLogger) DoDebug(d bool) (err error) {
|
||||
|
||||
var wg sync.WaitGroup
|
||||
var errs *multierr.MultiError = multierr.NewMultiError(nil)
|
||||
|
||||
m.EnableDebug = d
|
||||
|
||||
for _, l := range m.Loggers {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
var err2 error
|
||||
defer wg.Done()
|
||||
l.DoDebug(d)
|
||||
if err2 = l.DoDebug(d); err2 != nil {
|
||||
errs.AddError(err2)
|
||||
err2 = nil
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
if errs.Count() > 0 {
|
||||
err = errs
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
@ -73,21 +114,33 @@ func (m *MultiLogger) DoDebug(d bool) {
|
||||
|
||||
If you had a logger-specific Prefix set, you will need to re-set it to your desired prefix after running this method.
|
||||
*/
|
||||
func (m *MultiLogger) SetPrefix(prefix string) {
|
||||
func (m *MultiLogger) SetPrefix(prefix string) (err error) {
|
||||
|
||||
var wg sync.WaitGroup
|
||||
var errs *multierr.MultiError = multierr.NewMultiError(nil)
|
||||
|
||||
m.Prefix = prefix
|
||||
|
||||
for _, l := range m.Loggers {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
var err2 error
|
||||
defer wg.Done()
|
||||
l.SetPrefix(prefix)
|
||||
if err2 = l.SetPrefix(prefix); err != nil {
|
||||
errs.AddError(err2)
|
||||
err2 = nil
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
if errs.Count() > 0 {
|
||||
err = errs
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Alert writes an ALERT-level message to this MultiLogger (and all its MultiLogger.Loggers).
|
||||
@ -100,16 +153,11 @@ func (m *MultiLogger) Alert(s string, v ...interface{}) (err error) {
|
||||
wg.Add(1)
|
||||
go func(logObj Logger, msg string, rplc ...interface{}) {
|
||||
defer wg.Done()
|
||||
if rplc != nil {
|
||||
err = logObj.Alert(msg, rplc...)
|
||||
} else {
|
||||
err = logObj.Alert(msg)
|
||||
}
|
||||
if err != nil {
|
||||
if err = logObj.Alert(msg, rplc...); err != nil {
|
||||
e.AddError(err)
|
||||
err = nil
|
||||
}
|
||||
}(l, s, v)
|
||||
}(l, s, v...)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
@ -131,16 +179,11 @@ func (m *MultiLogger) Crit(s string, v ...interface{}) (err error) {
|
||||
wg.Add(1)
|
||||
go func(logObj Logger, msg string, rplc ...interface{}) {
|
||||
defer wg.Done()
|
||||
if rplc != nil {
|
||||
err = logObj.Crit(msg, rplc...)
|
||||
} else {
|
||||
err = logObj.Crit(msg)
|
||||
}
|
||||
if err != nil {
|
||||
if err = logObj.Crit(msg, rplc...); err != nil {
|
||||
e.AddError(err)
|
||||
err = nil
|
||||
}
|
||||
}(l, s, v)
|
||||
}(l, s, v...)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
@ -162,16 +205,11 @@ func (m *MultiLogger) Debug(s string, v ...interface{}) (err error) {
|
||||
wg.Add(1)
|
||||
go func(logObj Logger, msg string, rplc ...interface{}) {
|
||||
defer wg.Done()
|
||||
if rplc != nil {
|
||||
err = logObj.Debug(msg, rplc...)
|
||||
} else {
|
||||
err = logObj.Debug(msg)
|
||||
}
|
||||
if err != nil {
|
||||
if err = logObj.Debug(msg, rplc...); err != nil {
|
||||
e.AddError(err)
|
||||
err = nil
|
||||
}
|
||||
}(l, s, v)
|
||||
}(l, s, v...)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
@ -192,16 +230,11 @@ func (m *MultiLogger) Emerg(s string, v ...interface{}) (err error) {
|
||||
wg.Add(1)
|
||||
go func(logObj Logger, msg string, rplc ...interface{}) {
|
||||
defer wg.Done()
|
||||
if rplc != nil {
|
||||
err = logObj.Emerg(msg, rplc...)
|
||||
} else {
|
||||
err = logObj.Emerg(msg)
|
||||
}
|
||||
if err != nil {
|
||||
if err = logObj.Emerg(msg, rplc...); err != nil {
|
||||
e.AddError(err)
|
||||
err = nil
|
||||
}
|
||||
}(l, s, v)
|
||||
}(l, s, v...)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
@ -222,16 +255,11 @@ func (m *MultiLogger) Err(s string, v ...interface{}) (err error) {
|
||||
wg.Add(1)
|
||||
go func(logObj Logger, msg string, rplc ...interface{}) {
|
||||
defer wg.Done()
|
||||
if rplc != nil {
|
||||
err = logObj.Err(msg, rplc...)
|
||||
} else {
|
||||
err = logObj.Err(msg)
|
||||
}
|
||||
if err != nil {
|
||||
if err = logObj.Err(msg, rplc...); err != nil {
|
||||
e.AddError(err)
|
||||
err = nil
|
||||
}
|
||||
}(l, s, v)
|
||||
}(l, s, v...)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
@ -253,16 +281,11 @@ func (m *MultiLogger) Info(s string, v ...interface{}) (err error) {
|
||||
wg.Add(1)
|
||||
go func(logObj Logger, msg string, rplc ...interface{}) {
|
||||
defer wg.Done()
|
||||
if rplc != nil {
|
||||
err = logObj.Info(msg, rplc...)
|
||||
} else {
|
||||
err = logObj.Info(msg)
|
||||
}
|
||||
if err != nil {
|
||||
if err = logObj.Info(msg, rplc...); err != nil {
|
||||
e.AddError(err)
|
||||
err = nil
|
||||
}
|
||||
}(l, s, v)
|
||||
}(l, s, v...)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
@ -284,16 +307,11 @@ func (m *MultiLogger) Notice(s string, v ...interface{}) (err error) {
|
||||
wg.Add(1)
|
||||
go func(logObj Logger, msg string, rplc ...interface{}) {
|
||||
defer wg.Done()
|
||||
if rplc != nil {
|
||||
err = logObj.Notice(msg, rplc...)
|
||||
} else {
|
||||
err = logObj.Notice(msg)
|
||||
}
|
||||
if err != nil {
|
||||
if err = logObj.Notice(msg, rplc...); err != nil {
|
||||
e.AddError(err)
|
||||
err = nil
|
||||
}
|
||||
}(l, s, v)
|
||||
}(l, s, v...)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
@ -315,16 +333,11 @@ func (m *MultiLogger) Warning(s string, v ...interface{}) (err error) {
|
||||
wg.Add(1)
|
||||
go func(logObj Logger, msg string, rplc ...interface{}) {
|
||||
defer wg.Done()
|
||||
if rplc != nil {
|
||||
err = logObj.Warning(msg, rplc...)
|
||||
} else {
|
||||
err = logObj.Warning(msg)
|
||||
}
|
||||
if err != nil {
|
||||
if err = logObj.Warning(msg, rplc...); err != nil {
|
||||
e.AddError(err)
|
||||
err = nil
|
||||
}
|
||||
}(l, s, v)
|
||||
}(l, s, v...)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
@ -21,7 +21,7 @@ func GetMultiLogger(enableDebug bool, prefix string) (m *MultiLogger) {
|
||||
|
||||
m = &MultiLogger{
|
||||
EnableDebug: enableDebug,
|
||||
Prefix: "",
|
||||
Prefix: logPrefix,
|
||||
Loggers: make(map[string]Logger),
|
||||
}
|
||||
if prefix != "\x00" {
|
||||
@ -36,10 +36,13 @@ func GetMultiLogger(enableDebug bool, prefix string) (m *MultiLogger) {
|
||||
|
||||
identifier is a string to use to identify the added StdLogger in MultiLogger.Loggers.
|
||||
If empty, one will be automatically generated.
|
||||
|
||||
See GetLogger's logConfigFlags argument and StdLogger.LogFlags for details on logFlags.
|
||||
*/
|
||||
func (m *MultiLogger) AddStdLogger(identifier string) (err error) {
|
||||
func (m *MultiLogger) AddStdLogger(identifier string, logFlags int) (err error) {
|
||||
|
||||
var exists bool
|
||||
var prefix string
|
||||
|
||||
if identifier == "" {
|
||||
identifier = uuid.New().String()
|
||||
@ -54,10 +57,17 @@ func (m *MultiLogger) AddStdLogger(identifier string) (err error) {
|
||||
Logger: nil,
|
||||
EnableDebug: m.EnableDebug,
|
||||
Prefix: m.Prefix,
|
||||
LogFlags: logFlags,
|
||||
}
|
||||
if err = m.Loggers[identifier].Setup(); err != nil {
|
||||
return
|
||||
}
|
||||
m.Loggers[identifier].Setup()
|
||||
|
||||
m.Loggers[identifier].Info("logger initialized of type %T with prefix %v", m.Loggers[identifier], m.Loggers[identifier].GetPrefix())
|
||||
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
|
||||
}
|
||||
@ -70,11 +80,12 @@ func (m *MultiLogger) AddStdLogger(identifier string) (err error) {
|
||||
|
||||
logfilePath is a string for the path to the desired logfile.
|
||||
*/
|
||||
func (m *MultiLogger) AddFileLogger(identifier, logfilePath string) (err error) {
|
||||
func (m *MultiLogger) AddFileLogger(identifier string, enableStdOut, enableStdErr bool, logFlags int, logfilePath string) (err error) {
|
||||
|
||||
var exists bool
|
||||
var success bool
|
||||
var dirPath string
|
||||
var prefix string
|
||||
|
||||
if identifier == "" {
|
||||
identifier = uuid.New().String()
|
||||
@ -85,7 +96,9 @@ func (m *MultiLogger) AddFileLogger(identifier, logfilePath string) (err error)
|
||||
return
|
||||
}
|
||||
|
||||
if exists, _ = paths.RealPathExists(&logfilePath); !exists {
|
||||
if exists, err = paths.RealPathExists(&logfilePath); err != nil {
|
||||
return
|
||||
} else if !exists {
|
||||
if success, err = testOpen(logfilePath); err != nil {
|
||||
return
|
||||
} else if !success {
|
||||
@ -107,12 +120,21 @@ func (m *MultiLogger) AddFileLogger(identifier, logfilePath string) (err error)
|
||||
Logger: nil,
|
||||
EnableDebug: m.EnableDebug,
|
||||
Prefix: m.Prefix,
|
||||
LogFlags: logFlags,
|
||||
},
|
||||
Path: logfilePath,
|
||||
Path: logfilePath,
|
||||
EnableStdOut: enableStdOut,
|
||||
EnableStdErr: enableStdErr,
|
||||
}
|
||||
if err = m.Loggers[identifier].Setup(); err != nil {
|
||||
return
|
||||
}
|
||||
m.Loggers[identifier].Setup()
|
||||
|
||||
m.Loggers[identifier].Info("logger initialized of type %T with prefix %v", m.Loggers[identifier], m.Loggers[identifier].GetPrefix())
|
||||
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
|
||||
}
|
||||
|
@ -13,8 +13,10 @@ import (
|
||||
|
||||
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, logPaths ...string) (err error) {
|
||||
func (m *MultiLogger) AddDefaultLogger(identifier string, logFlags int, logPaths ...string) (err error) {
|
||||
|
||||
var l Logger
|
||||
var exists bool
|
||||
@ -28,7 +30,7 @@ func (m *MultiLogger) AddDefaultLogger(identifier string, logPaths ...string) (e
|
||||
return
|
||||
}
|
||||
|
||||
if l, err = GetLogger(m.EnableDebug, m.Prefix, logPaths...); err != nil {
|
||||
if l, err = GetLogger(m.EnableDebug, m.Prefix, logFlags, logPaths...); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@ -46,6 +48,7 @@ func (m *MultiLogger) AddDefaultLogger(identifier string, logPaths ...string) (e
|
||||
func (m *MultiLogger) AddSysdLogger(identifier string) (err error) {
|
||||
|
||||
var exists bool
|
||||
var prefix string
|
||||
|
||||
if identifier == "" {
|
||||
identifier = uuid.New().String()
|
||||
@ -65,9 +68,15 @@ func (m *MultiLogger) AddSysdLogger(identifier string) (err error) {
|
||||
EnableDebug: m.EnableDebug,
|
||||
Prefix: m.Prefix,
|
||||
}
|
||||
m.Loggers[identifier].Setup()
|
||||
if err = m.Loggers[identifier].Setup(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
m.Loggers[identifier].Info("logger initialized of type %T with prefix %v", m.Loggers[identifier], m.Loggers[identifier].GetPrefix())
|
||||
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
|
||||
}
|
||||
@ -84,6 +93,7 @@ func (m *MultiLogger) AddSyslogLogger(identifier string) (err error) {
|
||||
var hasSyslog bool
|
||||
var stat os.FileInfo
|
||||
var devlogPath string = devlog
|
||||
var prefix string
|
||||
|
||||
if identifier == "" {
|
||||
identifier = uuid.New().String()
|
||||
@ -110,9 +120,15 @@ func (m *MultiLogger) AddSyslogLogger(identifier string) (err error) {
|
||||
EnableDebug: m.EnableDebug,
|
||||
Prefix: m.Prefix,
|
||||
}
|
||||
m.Loggers[identifier].Setup()
|
||||
if err = m.Loggers[identifier].Setup(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
m.Loggers[identifier].Info("logger initialized of type %T with prefix %v", m.Loggers[identifier], m.Loggers[identifier].GetPrefix())
|
||||
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
|
||||
}
|
||||
|
@ -16,10 +16,12 @@ import (
|
||||
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).
|
||||
|
||||
See the documentation for GetLogger for details on other arguments.
|
||||
|
||||
Only the first logPaths entry that "works" will be used, later entries will be ignored.
|
||||
Currently this will almost always return a WinLogger.
|
||||
*/
|
||||
func (m *MultiLogger) AddDefaultLogger(identifier string, eventIDs *WinEventID, logPaths ...string) (err error) {
|
||||
func (m *MultiLogger) AddDefaultLogger(identifier string, eventIDs *WinEventID, logFlags int, logPaths ...string) (err error) {
|
||||
|
||||
var l Logger
|
||||
var exists bool
|
||||
@ -34,9 +36,9 @@ func (m *MultiLogger) AddDefaultLogger(identifier string, eventIDs *WinEventID,
|
||||
}
|
||||
|
||||
if logPaths != nil {
|
||||
l, err = GetLogger(m.EnableDebug, m.Prefix, eventIDs, logPaths...);
|
||||
l, err = GetLogger(m.EnableDebug, m.Prefix, eventIDs, logFlags, logPaths...)
|
||||
} else {
|
||||
l, err = GetLogger(m.EnableDebug, m.Prefix, eventIDs);
|
||||
l, err = GetLogger(m.EnableDebug, m.Prefix, eventIDs, logFlags)
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
@ -66,6 +68,7 @@ func (m *MultiLogger) AddDefaultLogger(identifier string, eventIDs *WinEventID,
|
||||
func (m *MultiLogger) AddWinLogger(identifier, source string, eventIDs *WinEventID) (err error) {
|
||||
|
||||
var exists bool
|
||||
var prefix string
|
||||
|
||||
if identifier == "" {
|
||||
identifier = uuid.New().String()
|
||||
@ -85,9 +88,15 @@ func (m *MultiLogger) AddWinLogger(identifier, source string, eventIDs *WinEvent
|
||||
EnableDebug: m.EnableDebug,
|
||||
eids: eventIDs,
|
||||
}
|
||||
m.Loggers[identifier].Setup()
|
||||
if err = m.Loggers[identifier].Setup(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
m.Loggers[identifier].Info("logger initialized of type %T with prefix %v", m.Loggers[identifier], m.Loggers[identifier].GetPrefix())
|
||||
if prefix, err = m.Loggers[identifier].GetPrefix(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
m.Loggers[identifier].Info("logger initialized of type %T with prefix %v", m.Loggers[identifier], prefix)
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -2,26 +2,45 @@ package logging
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
`log`
|
||||
`os`
|
||||
`strings`
|
||||
)
|
||||
|
||||
// Setup sets up/configures a StdLogger and prepares it for use.
|
||||
func (l *StdLogger) Setup() {
|
||||
/*
|
||||
Setup sets up/configures a StdLogger and prepares it for use.
|
||||
err will always be nil; it's there for interface-compat.
|
||||
*/
|
||||
func (l *StdLogger) Setup() (err error) {
|
||||
|
||||
l.Logger = log.Default()
|
||||
l.Logger.SetPrefix(l.Prefix + " ")
|
||||
// This uses a shared handle across the import. We don't want that.
|
||||
// l.Logger = log.Default()
|
||||
if l.Prefix != "" {
|
||||
l.Prefix = strings.TrimRight(l.Prefix, " ") + " "
|
||||
// l.Logger.SetPrefix(l.Prefix)
|
||||
}
|
||||
// (stdlib).log.std is returned by log.Default(), which uses os.Stderr.
|
||||
l.Logger = log.New(os.Stderr, l.Prefix, l.LogFlags)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Shutdown cleanly shuts down a StdLogger.
|
||||
func (l *StdLogger) Shutdown() {
|
||||
/*
|
||||
Shutdown cleanly shuts down a StdLogger.
|
||||
err will always be nil; it's there for interface-compat.
|
||||
*/
|
||||
func (l *StdLogger) Shutdown() (err error) {
|
||||
|
||||
// NOOP
|
||||
_ = ""
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetPrefix returns the prefix used by this StdLogger.
|
||||
func (l *StdLogger) GetPrefix() (prefix string) {
|
||||
/*
|
||||
GetPrefix returns the prefix used by this StdLogger.
|
||||
err will always be nil; it's there for interface-compat.
|
||||
*/
|
||||
func (l *StdLogger) GetPrefix() (prefix string, err error) {
|
||||
|
||||
prefix = l.Prefix
|
||||
|
||||
@ -31,15 +50,28 @@ func (l *StdLogger) GetPrefix() (prefix string) {
|
||||
/*
|
||||
DoDebug sets the debug state of this StdLogger.
|
||||
Note that this merely acts as a *safety filter* for debug messages to avoid sensitive information being written to the log.
|
||||
err will always be nil; it's there for interface-compat.
|
||||
*/
|
||||
func (l *StdLogger) DoDebug(d bool) {
|
||||
func (l *StdLogger) DoDebug(d bool) (err error) {
|
||||
|
||||
l.EnableDebug = d
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// SetPrefix sets the prefix for this StdLogger.
|
||||
func (l *StdLogger) SetPrefix(prefix string) {
|
||||
l.Prefix = prefix + " "
|
||||
l.Logger.SetPrefix(prefix + " ")
|
||||
/*
|
||||
SetPrefix sets the prefix for this StdLogger.
|
||||
err will always be nil; it's there for interface-compat.
|
||||
*/
|
||||
func (l *StdLogger) SetPrefix(prefix string) (err error) {
|
||||
|
||||
l.Prefix = prefix
|
||||
if prefix != "" {
|
||||
l.Prefix = strings.TrimRight(l.Prefix, " ") + " "
|
||||
}
|
||||
l.Logger.SetPrefix(l.Prefix)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Alert writes an ALERT-level message to this StdLogger.
|
||||
|
@ -7,24 +7,33 @@ import (
|
||||
"github.com/coreos/go-systemd/journal"
|
||||
)
|
||||
|
||||
// Setup sets up/configures a SystemDLogger and prepares it for use.
|
||||
func (l *SystemDLogger) Setup() {
|
||||
/*
|
||||
Setup sets up/configures a SystemDLogger and prepares it for use.
|
||||
err will always be nil; it's there for interface-compat.
|
||||
*/
|
||||
func (l *SystemDLogger) Setup() (err error) {
|
||||
|
||||
// NOOP
|
||||
_ = ""
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Shutdown cleanly shuts down a SystemDLogger.
|
||||
func (l *SystemDLogger) Shutdown() {
|
||||
/*
|
||||
Shutdown cleanly shuts down a SystemDLogger.
|
||||
err will always be nil; it's there for interface-compat.
|
||||
*/
|
||||
func (l *SystemDLogger) Shutdown() (err error) {
|
||||
|
||||
// NOOP
|
||||
_ = ""
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetPrefix returns the prefix used by this SystemDLogger.
|
||||
func (l *SystemDLogger) GetPrefix() (prefix string) {
|
||||
/*
|
||||
GetPrefix returns the prefix used by this SystemDLogger.
|
||||
err will always be nil; it's there for interface-compat.
|
||||
*/
|
||||
func (l *SystemDLogger) GetPrefix() (prefix string, err error) {
|
||||
|
||||
prefix = l.Prefix
|
||||
|
||||
@ -34,14 +43,24 @@ func (l *SystemDLogger) GetPrefix() (prefix string) {
|
||||
/*
|
||||
DoDebug sets the debug state of this SystemDLogger.
|
||||
Note that this merely acts as a *safety filter* for debug messages to avoid sensitive information being written to the log.
|
||||
err will always be nil; it's there for interface-compat.
|
||||
*/
|
||||
func (l *SystemDLogger) DoDebug(d bool) {
|
||||
func (l *SystemDLogger) DoDebug(d bool) (err error) {
|
||||
|
||||
l.EnableDebug = d
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// SetPrefix sets the prefix for this SystemDLogger.
|
||||
func (l *SystemDLogger) SetPrefix(prefix string) {
|
||||
/*
|
||||
SetPrefix sets the prefix for this SystemDLogger.
|
||||
err will always be nil; it's there for interface-compat.
|
||||
*/
|
||||
func (l *SystemDLogger) SetPrefix(prefix string) (err error) {
|
||||
|
||||
l.Prefix = prefix
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Alert writes an ALERT-level message to this SystemDLogger.
|
||||
|
@ -4,55 +4,79 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"log/syslog"
|
||||
|
||||
`r00t2.io/goutils/multierr`
|
||||
)
|
||||
|
||||
// Setup sets up/configures a SyslogLogger and prepares it for use.
|
||||
func (l *SyslogLogger) Setup() {
|
||||
func (l *SyslogLogger) Setup() (err error) {
|
||||
|
||||
var err error
|
||||
var errs *multierr.MultiError = multierr.NewMultiError(nil)
|
||||
|
||||
if l.alert, err = syslog.New(syslog.LOG_ALERT|syslogFacility, l.Prefix); err != nil {
|
||||
log.Panicln("could not open log for Alert")
|
||||
errs.AddError(err)
|
||||
err = nil
|
||||
}
|
||||
if l.crit, err = syslog.New(syslog.LOG_CRIT|syslogFacility, l.Prefix); err != nil {
|
||||
log.Panicln("could not open log for Crit")
|
||||
errs.AddError(err)
|
||||
err = nil
|
||||
}
|
||||
if l.debug, err = syslog.New(syslog.LOG_DEBUG|syslogFacility, l.Prefix); err != nil {
|
||||
log.Panicln("could not open log for Debug")
|
||||
errs.AddError(err)
|
||||
err = nil
|
||||
}
|
||||
if l.emerg, err = syslog.New(syslog.LOG_EMERG|syslogFacility, l.Prefix); err != nil {
|
||||
log.Panicln("could not open log for Emerg")
|
||||
errs.AddError(err)
|
||||
err = nil
|
||||
}
|
||||
if l.err, err = syslog.New(syslog.LOG_ERR|syslogFacility, l.Prefix); err != nil {
|
||||
log.Panicln("could not open log for Err")
|
||||
errs.AddError(err)
|
||||
err = nil
|
||||
}
|
||||
if l.info, err = syslog.New(syslog.LOG_INFO|syslogFacility, l.Prefix); err != nil {
|
||||
log.Panicln("could not open log for Info")
|
||||
errs.AddError(err)
|
||||
err = nil
|
||||
}
|
||||
if l.notice, err = syslog.New(syslog.LOG_NOTICE|syslogFacility, l.Prefix); err != nil {
|
||||
log.Panicln("could not open log for Notice")
|
||||
errs.AddError(err)
|
||||
err = nil
|
||||
}
|
||||
if l.warning, err = syslog.New(syslog.LOG_WARNING|syslogFacility, l.Prefix); err != nil {
|
||||
log.Panicln("could not open log for Warning")
|
||||
errs.AddError(err)
|
||||
err = nil
|
||||
}
|
||||
|
||||
if errs.Count() > 0 {
|
||||
err = errs
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Shutdown cleanly shuts down a SyslogLogger.
|
||||
func (l *SyslogLogger) Shutdown() {
|
||||
func (l *SyslogLogger) Shutdown() (err error) {
|
||||
|
||||
var err error
|
||||
var errs *multierr.MultiError = multierr.NewMultiError(nil)
|
||||
|
||||
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)
|
||||
errs.AddError(err)
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
|
||||
if errs.Count() > 0 {
|
||||
err = errs
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetPrefix returns the prefix used by this SyslogLogger.
|
||||
func (l *SyslogLogger) GetPrefix() (prefix string) {
|
||||
/*
|
||||
GetPrefix returns the prefix used by this SyslogLogger.
|
||||
err will always be nil; it's there for interface-compat.
|
||||
*/
|
||||
func (l *SyslogLogger) GetPrefix() (prefix string, err error) {
|
||||
|
||||
prefix = l.Prefix
|
||||
|
||||
@ -62,15 +86,30 @@ func (l *SyslogLogger) GetPrefix() (prefix string) {
|
||||
/*
|
||||
DoDebug sets the debug state of this SyslogLogger.
|
||||
Note that this merely acts as a *safety filter* for debug messages to avoid sensitive information being written to the log.
|
||||
err will always be nil; it's there for interface-compat.
|
||||
*/
|
||||
func (l *SyslogLogger) DoDebug(d bool) {
|
||||
func (l *SyslogLogger) DoDebug(d bool) (err error) {
|
||||
|
||||
l.EnableDebug = d
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// SetPrefix sets the prefix for this SyslogLogger.
|
||||
func (l *SyslogLogger) SetPrefix(prefix string) {
|
||||
func (l *SyslogLogger) SetPrefix(prefix string) (err error) {
|
||||
|
||||
l.Prefix = prefix
|
||||
l.Setup()
|
||||
|
||||
// We need to close the current loggers first.
|
||||
if err = l.Shutdown(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = l.Setup(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Alert writes an ALERT-level message to this SyslogLogger.
|
||||
|
1
logging/funcs_test.go
Normal file
1
logging/funcs_test.go
Normal file
@ -0,0 +1 @@
|
||||
package logging
|
@ -22,6 +22,9 @@ import (
|
||||
A pointer to a WinEventID struct may be specified for eventIDs to map extended logging levels (as Windows only supports three levels natively).
|
||||
If it is nil, a default one (DefaultEventID) will be used.
|
||||
|
||||
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).
|
||||
|
||||
@ -32,13 +35,14 @@ import (
|
||||
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, source string, eventIDs *WinEventID, logPaths ...string) (logger Logger, err error) {
|
||||
func GetLogger(enableDebug bool, source string, eventIDs *WinEventID, logConfigFlags int, logPaths ...string) (logger Logger, err error) {
|
||||
|
||||
var logPath string
|
||||
var logFlags bitmask.MaskBit
|
||||
var exists bool
|
||||
var success bool
|
||||
var ckLogPaths []string
|
||||
var prefix string
|
||||
|
||||
if strings.TrimSpace(source) == "" {
|
||||
err = errors.New("invalid source for Windows logging")
|
||||
@ -94,6 +98,7 @@ func GetLogger(enableDebug bool, source string, eventIDs *WinEventID, logPaths .
|
||||
StdLogger: StdLogger{
|
||||
Prefix: source,
|
||||
EnableDebug: enableDebug,
|
||||
LogFlags: logConfigFlags,
|
||||
},
|
||||
Path: logPath,
|
||||
}
|
||||
@ -101,13 +106,25 @@ func GetLogger(enableDebug bool, source string, eventIDs *WinEventID, logPaths .
|
||||
logger = &StdLogger{
|
||||
Prefix: source,
|
||||
EnableDebug: enableDebug,
|
||||
LogFlags: logConfigFlags,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.Setup()
|
||||
if err = logger.Setup(); err != nil {
|
||||
return
|
||||
}
|
||||
if source != "\x00" {
|
||||
if err = logger.SetPrefix(source); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
logger.Info("logger initialized of type %T with source %v", logger, logger.GetPrefix())
|
||||
if prefix, err = logger.GetPrefix(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
logger.Debug("logger initialized of type %T with source %v", logger, prefix)
|
||||
|
||||
return
|
||||
|
||||
|
@ -3,17 +3,29 @@ package logging
|
||||
import (
|
||||
`errors`
|
||||
`fmt`
|
||||
`os`
|
||||
`os/exec`
|
||||
`syscall`
|
||||
|
||||
`golang.org/x/sys/windows/registry`
|
||||
`golang.org/x/sys/windows/svc/eventlog`
|
||||
`r00t2.io/sysutils/paths`
|
||||
)
|
||||
|
||||
// Setup sets up/configures a WinLogger and prepares it for use.
|
||||
func (l *WinLogger) Setup() {
|
||||
/*
|
||||
Setup sets up/configures a WinLogger and prepares it for use.
|
||||
This will fail with an Access Denied (the first time, at least) unless running with elevated permissions unless WinLogger.Prefix is
|
||||
a registered Event Log source.
|
||||
|
||||
var err error
|
||||
If a failure occurs while trying to open the log with the given WinLogger.Prefix ("source"), a new Event Log source will be registered.
|
||||
If WinLogger.Executable is not empty at the time of calling WinLogger.Setup (or WinLogger.ForceService is true),
|
||||
eventlog.Install will be used (with the WinLogger.ExpandKey field).
|
||||
Otherwise eventlog.InstallAsEventCreate will be used.
|
||||
*/
|
||||
func (l *WinLogger) Setup() (err error) {
|
||||
|
||||
/*
|
||||
First a sanity check on the EventIDs.
|
||||
A sanity check on the EventIDs.
|
||||
Since we use eventcreate, all Event IDs must be 1 <= eid <= 1000.
|
||||
*/
|
||||
for _, eid := range []uint32{
|
||||
@ -26,45 +38,100 @@ func (l *WinLogger) Setup() {
|
||||
l.eids.Notice,
|
||||
l.eids.Warning,
|
||||
} {
|
||||
if !((eid <= 1000) && (1 <= eid)) {
|
||||
err = errors.New("event IDs must be between 1 and 1000 inclusive")
|
||||
panic(err)
|
||||
if !((eid <= EIDMax) && (EIDMin <= eid)) {
|
||||
err = ErrBadEid
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err = eventlog.InstallAsEventCreate(l.Prefix, eventlog.Error|eventlog.Warning|eventlog.Info); err != nil {
|
||||
if idx := ptrnSourceExists.FindStringIndex(err.Error()); idx == nil {
|
||||
// It's an error we want to panic on.
|
||||
panic(err)
|
||||
} else {
|
||||
// It already exists, so ignore the error.
|
||||
err = nil
|
||||
}
|
||||
if err = l.Install(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if l.elog, err = eventlog.Open(l.Prefix); err != nil {
|
||||
panic(err)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Install installs/registers the WinLogger Event Log interface. You most likely do not need to run this directly.
|
||||
func (l *WinLogger) Install() (err error) {
|
||||
|
||||
var exists bool
|
||||
var doNotCreate bool
|
||||
var useEventCreate bool = true
|
||||
|
||||
if doNotCreate, err = l.Exists(); err != nil {
|
||||
return
|
||||
} else if !doNotCreate {
|
||||
if l.Executable != "" {
|
||||
if l.Executable, err = exec.LookPath(l.Executable); err != nil {
|
||||
return
|
||||
}
|
||||
useEventCreate = false
|
||||
} else if l.ForceService {
|
||||
if l.Executable, err = exec.LookPath(os.Args[0]); err != nil {
|
||||
return
|
||||
}
|
||||
useEventCreate = false
|
||||
}
|
||||
if !useEventCreate {
|
||||
if exists, err = paths.RealPathExists(&l.Executable); err != nil {
|
||||
return
|
||||
} else if !exists {
|
||||
err = ErrBadBinPath
|
||||
return
|
||||
}
|
||||
if err = eventlog.Install(l.Prefix, l.Executable, l.ExpandKey, eventlog.Error|eventlog.Warning|eventlog.Info); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if err = eventlog.InstallAsEventCreate(l.Prefix, eventlog.Error|eventlog.Warning|eventlog.Info); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
// Shutdown cleanly shuts down a WinLogger.
|
||||
func (l *WinLogger) Shutdown() {
|
||||
|
||||
var err error
|
||||
|
||||
if err = l.elog.Close(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Remove uninstalls a registered WinLogger source.
|
||||
func (l *WinLogger) Remove() (err error) {
|
||||
|
||||
if err = eventlog.Remove(l.Prefix); err != nil {
|
||||
panic(err)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetPrefix returns the prefix used by this WinLogger.
|
||||
func (l *WinLogger) GetPrefix() (prefix string) {
|
||||
/*
|
||||
Shutdown cleanly shuts down a WinLogger but keep the source registered. Use WinLogger.Remove
|
||||
(or set WinLogger.RemoveOnClose to true before calling WinLogger.Shutdown) to remove the registered source.
|
||||
*/
|
||||
func (l *WinLogger) Shutdown() (err error) {
|
||||
|
||||
if err = l.elog.Close(); err != nil {
|
||||
// TODO: check for no access or file not exists syscall errors?
|
||||
return
|
||||
}
|
||||
|
||||
if l.RemoveOnClose {
|
||||
if err = l.Remove(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
GetPrefix returns the prefix used by this WinLogger.
|
||||
err will always be nil; it's there for interface-compat.
|
||||
*/
|
||||
func (l *WinLogger) GetPrefix() (prefix string, err error) {
|
||||
|
||||
prefix = l.Prefix
|
||||
|
||||
@ -74,43 +141,55 @@ func (l *WinLogger) GetPrefix() (prefix string) {
|
||||
/*
|
||||
DoDebug sets the debug state of this WinLogger.
|
||||
Note that this merely acts as a *safety filter* for debug messages to avoid sensitive information being written to the log.
|
||||
err will always be nil; it's there for interface-compat.
|
||||
*/
|
||||
func (l *WinLogger) DoDebug(d bool) {
|
||||
func (l *WinLogger) DoDebug(d bool) (err error) {
|
||||
|
||||
l.EnableDebug = d
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// SetPrefix sets the prefix for this WinLogger.
|
||||
func (l *WinLogger) SetPrefix(prefix string) {
|
||||
func (l *WinLogger) SetPrefix(prefix string) (err error) {
|
||||
|
||||
var err error
|
||||
// To properly change the prefix, we need to tear down the old event log and create a new one.
|
||||
if err = l.Shutdown(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
l.Prefix = prefix
|
||||
|
||||
// To properly change the prefix, we need to tear down the old event log and create a new one.
|
||||
if err = l.elog.Close(); err != nil {
|
||||
panic(err)
|
||||
if err = l.Setup(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = eventlog.Remove(l.Prefix); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err = eventlog.InstallAsEventCreate(l.Prefix, eventlog.Error|eventlog.Warning|eventlog.Info); err != nil {
|
||||
if idx := ptrnSourceExists.FindStringIndex(err.Error()); idx == nil {
|
||||
// It's an error we want to panic on.
|
||||
panic(err)
|
||||
} else {
|
||||
// It already exists, so ignore the error.
|
||||
// Exists indicates if the WinLogger.Prefix is a registered source or not.
|
||||
func (l *WinLogger) Exists() (e bool, err error) {
|
||||
|
||||
var regKey registry.Key
|
||||
var subKey registry.Key
|
||||
|
||||
if regKey, err = registry.OpenKey(registry.LOCAL_MACHINE, eventLogRegistryKey, registry.READ); err != nil {
|
||||
return
|
||||
}
|
||||
defer regKey.Close()
|
||||
|
||||
if subKey, err = registry.OpenKey(regKey, l.Prefix, registry.READ); err != nil {
|
||||
if errors.Is(err, syscall.ERROR_FILE_NOT_FOUND) {
|
||||
e = false
|
||||
err = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
defer subKey.Close()
|
||||
|
||||
if l.elog, err = eventlog.Open(l.Prefix); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
e = true
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Alert writes an ALERT-level message to this WinLogger.
|
||||
|
@ -17,11 +17,11 @@ type Logger interface {
|
||||
Info(s string, v ...interface{}) (err error)
|
||||
Notice(s string, v ...interface{}) (err error)
|
||||
Warning(s string, v ...interface{}) (err error)
|
||||
DoDebug(d bool)
|
||||
SetPrefix(p string)
|
||||
GetPrefix() (p string)
|
||||
Setup()
|
||||
Shutdown()
|
||||
DoDebug(d bool) (err error)
|
||||
SetPrefix(p string) (err error)
|
||||
GetPrefix() (p string, err error)
|
||||
Setup() (err error)
|
||||
Shutdown() (err error)
|
||||
}
|
||||
|
||||
/*
|
||||
@ -38,20 +38,42 @@ type StdLogger struct {
|
||||
EnableDebug bool
|
||||
// Prefix indicates the prefix for log entries; in shared logs, this helps differentiate the source.
|
||||
Prefix string
|
||||
/*
|
||||
LogFlags control some of the formatting options presented as an OR'd value.
|
||||
See https://pkg.go.dev/log#pkg-constants for flag details.
|
||||
e.g.:
|
||||
*StdLogger.LogFlags = log.Ldate | log.Lmicroseconds | log.Llongfile | log.LUTC // a very detailed log output
|
||||
*StdLogger.LogFlags = log.Ldate | log.Ltime // the flags used by log.Default() (also available as simply log.LstdFlags)
|
||||
The default is 0; no flags (no output except prefix if non-empty and message).
|
||||
You will need to run *StdLogger.Shutdown and then *StdLogger.Setup again if you wish to change this.
|
||||
*/
|
||||
LogFlags int
|
||||
}
|
||||
|
||||
// FileLogger uses a StdLogger with a file handle writer to write to the file given at Path.
|
||||
/*
|
||||
FileLogger uses a StdLogger with a file handle writer to write to the file given at Path.
|
||||
|
||||
NOTE: If you wish to change the FileLogger.StdLogger.LogFlags, do *not* run FileLogger.StdLogger.Setup after doing so as this
|
||||
will instead create a logger detached from the file handler. Instead, be sure to call FileLogger.Setup.
|
||||
(Alternatively, run FileLogger.Shutdown and replace your logger with a new FileLogger.)
|
||||
*/
|
||||
type FileLogger struct {
|
||||
// StdLogger is used for the log formation and handling. See StdLogger for more details.
|
||||
StdLogger
|
||||
// Path is the path to the logfile.
|
||||
Path string
|
||||
/*
|
||||
EnableStdOut is true if the log will send to STDOUT as well as the file.
|
||||
EnableStdOut is true if the log will send to STDOUT as well as the file (and STDERR if FileLogger.EnableStdErr == true).
|
||||
If false (default), it will only (silently) write to the log file.
|
||||
You will need to run FileLogger.Shutdown and then FileLogger.Setup again if you wish to change this.
|
||||
*/
|
||||
EnableStdOut bool
|
||||
/*
|
||||
EnableStdErr is true if the log will send to STDERR as well as the file (and STDOUT if FileLogger.EnableStdOut == true).
|
||||
If false (default), it will only (silently) write to the log file.
|
||||
You will need to run *FileLogger.Shutdown and then *FileLogger.Setup again if you wish to change this.
|
||||
*/
|
||||
EnableStdOut bool
|
||||
EnableStdErr bool
|
||||
// writer is used for the writing out of the log file.
|
||||
writer *os.File
|
||||
}
|
||||
|
@ -4,13 +4,60 @@ import (
|
||||
`golang.org/x/sys/windows/svc/eventlog`
|
||||
)
|
||||
|
||||
// WinLogger is used for logging to the Windows Event Log. These entries are viewable in the Event Viewer application, under "Windows Logs > Application".
|
||||
type WinLogger struct {
|
||||
/*
|
||||
EnableDebug indicates if the debug filter should be disabled (true) or if the filter should be enabled (false).
|
||||
This prevents potential data leak of sensitive information, as some loggers (e.g. FileLogger) will otherwise write all messages.
|
||||
*/
|
||||
EnableDebug bool
|
||||
Prefix string
|
||||
elog *eventlog.Log
|
||||
eids *WinEventID
|
||||
/*
|
||||
Prefix is used as the Event Log "Source". It's named as Prefix to retain compatability with methods in the Logger interface.
|
||||
*/
|
||||
Prefix string
|
||||
/*
|
||||
Executable is used as the path for the executable implementing this logger.
|
||||
If non-empty, it enables the "service" mode of Event Log (intended for "installed" software that's expected
|
||||
to exist as a specific path reliably).
|
||||
It can be a file within the PATHs or an absolute/relative path; an attempt to resolve the actual path will be made. If this fails or the file
|
||||
does not exist, an error will be raised.
|
||||
*/
|
||||
Executable string
|
||||
/*
|
||||
ExpandKey is only used if Executable is non-empty and valid and/or ForceService is true.
|
||||
If true, the WinLogger will be installed/registered with the REG_EXPAND_SZ mode - otherwise it will be installed as REG_SZ.
|
||||
See the definition for the two at https://docs.microsoft.com/en-us/windows/win32/sysinfo/registry-value-types for further details.
|
||||
If you're unsure which you want, it's probably REG_SZ (WinLogger.ExpandKey == false), which is the default.
|
||||
*/
|
||||
ExpandKey bool
|
||||
/*
|
||||
ForceService, if true, will enforce WinLogger to be used as if Executable is populated and valid (it will use os.Args[0] as the Executable path).
|
||||
If Executable is empty but ForceService is true and os.Args[0] is empty or invalid (not a real path, etc.), an error will be raised.
|
||||
*/
|
||||
ForceService bool
|
||||
// RemoveOnClose should be true if the logger should be removed/unregistered from the Registry upon calling WinLogger.Shutdown.
|
||||
RemoveOnClose bool
|
||||
// elog is the actual writer to the Event Log.
|
||||
elog *eventlog.Log
|
||||
// eids is used to look up what event ID to use when writing to a WinLogger.elog.
|
||||
eids *WinEventID
|
||||
}
|
||||
|
||||
/*
|
||||
WinEventID is a collection of Event IDs to use for a WinLogger.
|
||||
Because Event Log only supports three entry types (informational, warning, or error),
|
||||
these event IDs allow you to filter the messages in a slightly more granular way. They map to their corresponding method name/Logger level.
|
||||
However, this means that a WinLogger does not support custom event IDs (and thus you cannot assign individual event IDs to specific errors).
|
||||
This is the price of convenience.
|
||||
|
||||
An additional method set may be added in the future to support this, but this is currently an unplanned feature.
|
||||
|
||||
Event IDs *must* be between the constants EIDMin and EIDMax (inclusive) unless the WinLogger is used in "service" mode
|
||||
(see WinLogger.Executable and WinLogger.ForceService).
|
||||
|
||||
If you need recommended defaults, you may want to use the Event* constants (e.g. EventAlert, EventDebug, etc.)
|
||||
or even use the pre-populated DefaultEventID (which is assigned the above Event* constants).
|
||||
*/
|
||||
type WinEventID struct {
|
||||
Alert,
|
||||
Crit,
|
||||
|
Loading…
Reference in New Issue
Block a user