go_goutils/logging/funcs_winlogger_windows.go
brent saner e5191383a7
v1.7.0
ADDED:
* logging.Logger objects now are able to return a stdlib *log.Logger.
2024-06-19 18:57:26 -04:00

354 lines
6.8 KiB
Go

package logging
import (
"errors"
"fmt"
`log`
"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.
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.
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) {
/*
A sanity check on the EventIDs.
Since we use eventcreate, all Event IDs must be 1 <= eid <= 1000.
*/
for _, eid := range []uint32{
l.EIDs.Alert,
l.EIDs.Crit,
l.EIDs.Debug,
l.EIDs.Emerg,
l.EIDs.Err,
l.EIDs.Info,
l.EIDs.Notice,
l.EIDs.Warning,
} {
if !((eid <= EIDMax) && (EIDMin <= eid)) {
err = ErrBadEid
return
}
}
if err = l.Install(); err != nil {
return
}
if l.elog, err = eventlog.Open(l.Prefix); err != nil {
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
}
// Remove uninstalls a registered WinLogger source.
func (l *WinLogger) Remove() (err error) {
if err = eventlog.Remove(l.Prefix); err != nil {
return
}
return
}
/*
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
return
}
/*
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) (err error) {
l.EnableDebug = d
return
}
// GetDebug returns the debug status of this WinLogger.
func (l *WinLogger) GetDebug() (d bool) {
d = l.EnableDebug
return
}
// SetPrefix sets the prefix for this WinLogger.
func (l *WinLogger) SetPrefix(prefix string) (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
if err = l.Setup(); err != nil {
return
}
return
}
// 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()
e = true
return
}
// Alert writes an ALERT-level message to this WinLogger.
func (l *WinLogger) Alert(s string, v ...interface{}) (err error) {
var msg string
if v != nil {
msg = fmt.Sprintf(s, v...)
} else {
msg = s
}
// Treat ALERT as Warning
err = l.elog.Warning(l.EIDs.Alert, msg)
return
}
// Crit writes an CRITICAL-level message to this WinLogger.
func (l *WinLogger) Crit(s string, v ...interface{}) (err error) {
var msg string
if v != nil {
msg = fmt.Sprintf(s, v...)
} else {
msg = s
}
// Treat CRIT as Error
err = l.elog.Error(l.EIDs.Crit, msg)
return
}
// Debug writes a DEBUG-level message to this WinLogger.
func (l *WinLogger) 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
}
// Treat DEBUG as Info
err = l.elog.Info(l.EIDs.Debug, msg)
return
}
// Emerg writes an EMERGENCY-level message to this WinLogger.
func (l *WinLogger) Emerg(s string, v ...interface{}) (err error) {
var msg string
if v != nil {
msg = fmt.Sprintf(s, v...)
} else {
msg = s
}
// Treat EMERG as Error
err = l.elog.Error(l.EIDs.Emerg, msg)
return
}
// Err writes an ERROR-level message to this WinLogger.
func (l *WinLogger) Err(s string, v ...interface{}) (err error) {
var msg string
if v != nil {
msg = fmt.Sprintf(s, v...)
} else {
msg = s
}
err = l.elog.Error(l.EIDs.Err, msg)
return
}
// Info writes an INFO-level message to this WinLogger.
func (l *WinLogger) Info(s string, v ...interface{}) (err error) {
var msg string
if v != nil {
msg = fmt.Sprintf(s, v...)
} else {
msg = s
}
err = l.elog.Info(l.EIDs.Info, msg)
return
}
// Notice writes a NOTICE-level message to this WinLogger.
func (l *WinLogger) Notice(s string, v ...interface{}) (err error) {
var msg string
if v != nil {
msg = fmt.Sprintf(s, v...)
} else {
msg = s
}
// Treat NOTICE as Info
err = l.elog.Info(l.EIDs.Notice, msg)
return
}
// Warning writes a WARNING/WARN-level message to this WinLogger.
func (l *WinLogger) Warning(s string, v ...interface{}) (err error) {
var msg string
if v != nil {
msg = fmt.Sprintf(s, v...)
} else {
msg = s
}
err = l.elog.Warning(l.EIDs.Warning, msg)
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
}