diff --git a/glog_file.go b/glog_file.go index 8eb8b08c..2b478ae6 100644 --- a/glog_file.go +++ b/glog_file.go @@ -158,7 +158,10 @@ var sinks struct { func init() { // Register stderr first: that way if we crash during file-writing at least // the log will have gone somewhere. - logsink.TextSinks = append(logsink.TextSinks, &sinks.stderr, &sinks.file) + if shouldRegisterStderrSink() { + logsink.TextSinks = append(logsink.TextSinks, &sinks.stderr) + } + logsink.TextSinks = append(logsink.TextSinks, &sinks.file) sinks.file.flushChan = make(chan logsink.Severity, 1) go sinks.file.flushDaemon() diff --git a/glog_file_nonwindows.go b/glog_file_nonwindows.go index d5cdb793..a0089ba4 100644 --- a/glog_file_nonwindows.go +++ b/glog_file_nonwindows.go @@ -4,6 +4,13 @@ package glog import "os/user" +// shouldRegisterStderrSink determines whether we should register a log sink that writes to stderr. +// Today, this always returns true on non-Windows platforms, as it specifically checks for a +// condition that is only present on Windows. +func shouldRegisterStderrSink() bool { + return true +} + func lookupUser() string { if current, err := user.Current(); err == nil { return current.Username diff --git a/glog_file_windows.go b/glog_file_windows.go index a9e4f609..2f032e19 100644 --- a/glog_file_windows.go +++ b/glog_file_windows.go @@ -3,9 +3,22 @@ package glog import ( + "os" "syscall" ) +// shouldRegisterStderrSink determines whether we should register a log sink that writes to stderr. +// Today, this checks if stderr is "valid", in that it maps to a non-NULL Handle. +// Windows Services are spawned without Stdout and Stderr, so any attempt to use them equates to +// referencing an invalid file Handle. +// os.Stderr's FD is derived from a call to `syscall.GetStdHandle(syscall.STD_ERROR_HANDLE)`. +// Documentation[1] for the GetStdHandle function indicates the return value may be NULL if the +// application lacks the standard handle, so consider Stderr valid if its FD is non-NULL. +// [1]: https://learn.microsoft.com/en-us/windows/console/getstdhandle +func shouldRegisterStderrSink() bool { + return os.Stderr.Fd() != 0 +} + // This follows the logic in the standard library's user.Current() function, except // that it leaves out the potentially expensive calls required to look up the user's // display name in Active Directory.