mirror of
https://github.com/rclone/rclone.git
synced 2025-06-20 19:57:51 +02:00
log: add --windows-event-log-level to support Windows Event Log
This provides JSON logs in the Windows Event Log.
This commit is contained in:
parent
dfa4d94827
commit
15510c66d4
@ -1515,6 +1515,33 @@ warnings and significant events.
|
|||||||
|
|
||||||
`ERROR` is equivalent to `-q`. It only outputs error messages.
|
`ERROR` is equivalent to `-q`. It only outputs error messages.
|
||||||
|
|
||||||
|
### --windows-event-log LEVEL ###
|
||||||
|
|
||||||
|
If this is configured (the default is `OFF`) then logs of this level
|
||||||
|
and above will be logged to the Windows event log in **addition** to
|
||||||
|
the normal logs. These will be logged in JSON format as described
|
||||||
|
below regardless of what format the main logs are configured for.
|
||||||
|
|
||||||
|
The Windows event log only has 3 levels of severity `Info`, `Warning`
|
||||||
|
and `Error`. If enabled we map rclone levels like this.
|
||||||
|
|
||||||
|
- `Error` ← `ERROR` (and above)
|
||||||
|
- `Warning` ← `WARNING` (note that this level is defined but not currently used).
|
||||||
|
- `Info` ← `NOTICE`, `INFO` and `DEBUG`.
|
||||||
|
|
||||||
|
Rclone will declare its log source as "rclone" if it is has enough
|
||||||
|
permissions to create the registry key needed. If not then logs will
|
||||||
|
appear as "Application". You can run `rclone version --windows-event-log DEBUG`
|
||||||
|
once as administrator to create the registry key in advance.
|
||||||
|
|
||||||
|
**Note** that the `--windows-event-log` level must be greater (more
|
||||||
|
severe) than or equal to the `--log-level`. For example to log DEBUG
|
||||||
|
to a log file but ERRORs to the event log you would use
|
||||||
|
|
||||||
|
--log-file rclone.log --log-level DEBUG --windows-event-log ERROR
|
||||||
|
|
||||||
|
This option is only supported Windows platforms.
|
||||||
|
|
||||||
### --use-json-log ###
|
### --use-json-log ###
|
||||||
|
|
||||||
This switches the log format to JSON for rclone. The fields of JSON
|
This switches the log format to JSON for rclone. The fields of JSON
|
||||||
|
@ -32,6 +32,7 @@ const (
|
|||||||
LogLevelNotice // Normal logging, -q suppresses
|
LogLevelNotice // Normal logging, -q suppresses
|
||||||
LogLevelInfo // Transfers, needs -v
|
LogLevelInfo // Transfers, needs -v
|
||||||
LogLevelDebug // Debug level, needs -vv
|
LogLevelDebug // Debug level, needs -vv
|
||||||
|
LogLevelOff
|
||||||
)
|
)
|
||||||
|
|
||||||
type logLevelChoices struct{}
|
type logLevelChoices struct{}
|
||||||
@ -46,6 +47,7 @@ func (logLevelChoices) Choices() []string {
|
|||||||
LogLevelNotice: "NOTICE",
|
LogLevelNotice: "NOTICE",
|
||||||
LogLevelInfo: "INFO",
|
LogLevelInfo: "INFO",
|
||||||
LogLevelDebug: "DEBUG",
|
LogLevelDebug: "DEBUG",
|
||||||
|
LogLevelOff: "OFF",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,6 +68,7 @@ const (
|
|||||||
SlogLevelCritical = slog.Level(12) // More severe than Error
|
SlogLevelCritical = slog.Level(12) // More severe than Error
|
||||||
SlogLevelAlert = slog.Level(16) // More severe than Critical
|
SlogLevelAlert = slog.Level(16) // More severe than Critical
|
||||||
SlogLevelEmergency = slog.Level(20) // Most severe
|
SlogLevelEmergency = slog.Level(20) // Most severe
|
||||||
|
SlogLevelOff = slog.Level(24) // A very high value
|
||||||
)
|
)
|
||||||
|
|
||||||
// Map our level numbers to slog level numbers
|
// Map our level numbers to slog level numbers
|
||||||
@ -78,6 +81,7 @@ var levelToSlog = []slog.Level{
|
|||||||
LogLevelNotice: SlogLevelNotice,
|
LogLevelNotice: SlogLevelNotice,
|
||||||
LogLevelInfo: slog.LevelInfo,
|
LogLevelInfo: slog.LevelInfo,
|
||||||
LogLevelDebug: slog.LevelDebug,
|
LogLevelDebug: slog.LevelDebug,
|
||||||
|
LogLevelOff: SlogLevelOff,
|
||||||
}
|
}
|
||||||
|
|
||||||
// LogValueItem describes keyed item for a JSON log entry
|
// LogValueItem describes keyed item for a JSON log entry
|
||||||
|
15
fs/log/event_log.go
Normal file
15
fs/log/event_log.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// Windows event logging stubs for non windows machines
|
||||||
|
|
||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package log
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Starts windows event log if configured.
|
||||||
|
func startWindowsEventLog(*OutputHandler) error {
|
||||||
|
return fmt.Errorf("windows event log not supported on %s platform", runtime.GOOS)
|
||||||
|
}
|
79
fs/log/event_log_windows.go
Normal file
79
fs/log/event_log_windows.go
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
// Windows event logging
|
||||||
|
|
||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package log
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"github.com/rclone/rclone/fs"
|
||||||
|
"github.com/rclone/rclone/lib/atexit"
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
"golang.org/x/sys/windows/svc/eventlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
errorID = uint32(windows.ERROR_INTERNAL_ERROR)
|
||||||
|
infoID = uint32(windows.ERROR_SUCCESS)
|
||||||
|
sourceName = "rclone"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
windowsEventLog *eventlog.Log
|
||||||
|
)
|
||||||
|
|
||||||
|
func startWindowsEventLog(handler *OutputHandler) error {
|
||||||
|
// Don't install Windows event log if it is disabled.
|
||||||
|
if Opt.WindowsEventLogLevel == fs.LogLevelOff {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Install the event source - we don't care if this fails as Windows has sensible fallbacks.
|
||||||
|
_ = eventlog.InstallAsEventCreate(sourceName, eventlog.Info|eventlog.Warning|eventlog.Error)
|
||||||
|
|
||||||
|
// Open the event log
|
||||||
|
// If sourceName didn't get registered then Windows will use "Application" instead which is fine.
|
||||||
|
// Though in my tests it seemsed to use sourceName regardless.
|
||||||
|
elog, err := eventlog.Open(sourceName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("open event log: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the global for the handler
|
||||||
|
windowsEventLog = elog
|
||||||
|
|
||||||
|
// Close it on exit
|
||||||
|
atexit.Register(func() {
|
||||||
|
err := elog.Close()
|
||||||
|
if err != nil {
|
||||||
|
fs.Errorf(nil, "Failed to close Windows event log: %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Add additional JSON logging to the eventLog handler.
|
||||||
|
handler.AddOutput(true, eventLog)
|
||||||
|
|
||||||
|
fs.Infof(nil, "Logging to Windows event log at level %v", Opt.WindowsEventLogLevel)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// We use levels ERROR, NOTICE, INFO, DEBUG
|
||||||
|
// Need to map to ERROR, WARNING, INFO
|
||||||
|
func eventLog(level slog.Level, text string) {
|
||||||
|
// Check to see if this level is required
|
||||||
|
if level < fs.LogLevelToSlog(Opt.WindowsEventLogLevel) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now log to windows eventLog
|
||||||
|
switch level {
|
||||||
|
case fs.SlogLevelEmergency, fs.SlogLevelAlert, fs.SlogLevelCritical, slog.LevelError:
|
||||||
|
_ = windowsEventLog.Error(errorID, text)
|
||||||
|
case slog.LevelWarn:
|
||||||
|
_ = windowsEventLog.Warning(infoID, text)
|
||||||
|
case fs.SlogLevelNotice, slog.LevelInfo, slog.LevelDebug:
|
||||||
|
_ = windowsEventLog.Info(infoID, text)
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@ package log
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
@ -38,15 +39,27 @@ var OptionsInfo = fs.Options{{
|
|||||||
Default: false,
|
Default: false,
|
||||||
Help: "Activate systemd integration for the logger",
|
Help: "Activate systemd integration for the logger",
|
||||||
Groups: "Logging",
|
Groups: "Logging",
|
||||||
|
}, {
|
||||||
|
Name: "windows_event_log_level",
|
||||||
|
Default: fs.LogLevelOff,
|
||||||
|
Help: "Windows Event Log level DEBUG|INFO|NOTICE|ERROR|OFF",
|
||||||
|
Groups: "Logging",
|
||||||
|
Hide: func() fs.OptionVisibility {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return fs.OptionHideBoth
|
||||||
|
}(),
|
||||||
}}
|
}}
|
||||||
|
|
||||||
// Options contains options for controlling the logging
|
// Options contains options for controlling the logging
|
||||||
type Options struct {
|
type Options struct {
|
||||||
File string `config:"log_file"` // Log everything to this file
|
File string `config:"log_file"` // Log everything to this file
|
||||||
Format logFormat `config:"log_format"` // Comma separated list of log format options
|
Format logFormat `config:"log_format"` // Comma separated list of log format options
|
||||||
UseSyslog bool `config:"syslog"` // Use Syslog for logging
|
UseSyslog bool `config:"syslog"` // Use Syslog for logging
|
||||||
SyslogFacility string `config:"syslog_facility"` // Facility for syslog, e.g. KERN,USER,...
|
SyslogFacility string `config:"syslog_facility"` // Facility for syslog, e.g. KERN,USER,...
|
||||||
LogSystemdSupport bool `config:"log_systemd"` // set if using systemd logging
|
LogSystemdSupport bool `config:"log_systemd"` // set if using systemd logging
|
||||||
|
WindowsEventLogLevel fs.LogLevel `config:"windows_event_log_level"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -149,6 +162,11 @@ func Stack(o any, info string) {
|
|||||||
// externally visible in the rc.
|
// externally visible in the rc.
|
||||||
func logReload(ci *fs.ConfigInfo) error {
|
func logReload(ci *fs.ConfigInfo) error {
|
||||||
Handler.SetLevel(fs.LogLevelToSlog(ci.LogLevel))
|
Handler.SetLevel(fs.LogLevelToSlog(ci.LogLevel))
|
||||||
|
|
||||||
|
if Opt.WindowsEventLogLevel != fs.LogLevelOff && Opt.WindowsEventLogLevel > ci.LogLevel {
|
||||||
|
return fmt.Errorf("--windows-event-log-level %q must be >= --log-level %q", Opt.WindowsEventLogLevel, ci.LogLevel)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,6 +225,14 @@ func InitLogging() {
|
|||||||
if Opt.LogSystemdSupport {
|
if Opt.LogSystemdSupport {
|
||||||
startSystemdLog(Handler)
|
startSystemdLog(Handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Windows event logging
|
||||||
|
if Opt.WindowsEventLogLevel != fs.LogLevelOff {
|
||||||
|
err := startWindowsEventLog(Handler)
|
||||||
|
if err != nil {
|
||||||
|
fs.Fatalf(nil, "Failed to start windows event log: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redirected returns true if the log has been redirected from stdout
|
// Redirected returns true if the log has been redirected from stdout
|
||||||
|
Loading…
x
Reference in New Issue
Block a user