337 lines
6.5 KiB
Go
337 lines
6.5 KiB
Go
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.
|
|
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
|
|
}
|
|
|
|
// 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
|
|
|
|
}
|