From 8b06b6157dce8fd410886a837f103e5b56006c6b Mon Sep 17 00:00:00 2001 From: Next Turn <45985406+nxtn@users.noreply.github.com> Date: Sun, 27 Dec 2020 01:05:46 +0800 Subject: [PATCH] Backport logging updates (#755) --- .../Logging/IServiceEventLogProvider.cs | 16 --- src/WinSW.Core/ServiceDescriptor.cs | 6 +- src/WinSW.Core/ServiceDescriptorYaml.cs | 6 +- src/WinSW.Core/WinSW.Core.csproj | 13 +- .../Logging/ServiceEventLogAppender.cs | 29 +++-- .../Logging/WrapperServiceEventLogProvider.cs | 4 +- src/WinSW/Program.cs | 115 ++++++++---------- src/WinSW/WrapperService.cs | 6 +- 8 files changed, 83 insertions(+), 112 deletions(-) delete mode 100644 src/WinSW.Core/Logging/IServiceEventLogProvider.cs rename src/{WinSW.Core => WinSW}/Logging/ServiceEventLogAppender.cs (50%) diff --git a/src/WinSW.Core/Logging/IServiceEventLogProvider.cs b/src/WinSW.Core/Logging/IServiceEventLogProvider.cs deleted file mode 100644 index 0c79fd0..0000000 --- a/src/WinSW.Core/Logging/IServiceEventLogProvider.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Diagnostics; - -namespace WinSW.Logging -{ - /// - /// Indicates that the class may reference the event log - /// - public interface IServiceEventLogProvider - { - /// - /// Locates Event Log for the service. - /// - /// Event Log or null if it is not avilable - EventLog? Locate(); - } -} diff --git a/src/WinSW.Core/ServiceDescriptor.cs b/src/WinSW.Core/ServiceDescriptor.cs index 5bd46bd..50baf24 100644 --- a/src/WinSW.Core/ServiceDescriptor.cs +++ b/src/WinSW.Core/ServiceDescriptor.cs @@ -39,10 +39,10 @@ namespace WinSW // Currently there is no opportunity to alter the executable path public virtual string ExecutablePath => Defaults.ExecutablePath; - public ServiceDescriptor(string baseName, DirectoryInfo d) + public ServiceDescriptor(string baseName, string directory) { this.BaseName = baseName; - this.BasePath = Path.Combine(d.FullName, this.BaseName); + this.BasePath = Path.Combine(directory, this.BaseName); try { @@ -54,7 +54,7 @@ namespace WinSW } // register the base directory as environment variable so that future expansions can refer to this. - Environment.SetEnvironmentVariable("BASE", d.FullName); + Environment.SetEnvironmentVariable("BASE", directory); // ditto for ID Environment.SetEnvironmentVariable("SERVICE_ID", this.Name); diff --git a/src/WinSW.Core/ServiceDescriptorYaml.cs b/src/WinSW.Core/ServiceDescriptorYaml.cs index 5305e72..2cd5b55 100644 --- a/src/WinSW.Core/ServiceDescriptorYaml.cs +++ b/src/WinSW.Core/ServiceDescriptorYaml.cs @@ -11,9 +11,9 @@ namespace WinSW public static DefaultWinSWSettings Defaults { get; } = new DefaultWinSWSettings(); - public ServiceDescriptorYaml(string baseName, DirectoryInfo d) + public ServiceDescriptorYaml(string baseName, string directory) { - string basepath = Path.Combine(d.FullName, baseName); + string basepath = Path.Combine(directory, baseName); using (var reader = new StreamReader(basepath + ".yml")) { @@ -23,7 +23,7 @@ namespace WinSW this.Configurations = deserializer.Deserialize(file); } - Environment.SetEnvironmentVariable("BASE", d.FullName); + Environment.SetEnvironmentVariable("BASE", directory); // ditto for ID Environment.SetEnvironmentVariable("SERVICE_ID", this.Configurations.Name); diff --git a/src/WinSW.Core/WinSW.Core.csproj b/src/WinSW.Core/WinSW.Core.csproj index d64883e..88f4dbd 100644 --- a/src/WinSW.Core/WinSW.Core.csproj +++ b/src/WinSW.Core/WinSW.Core.csproj @@ -10,7 +10,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive @@ -19,19 +19,8 @@ - - - - - - - - - - - diff --git a/src/WinSW.Core/Logging/ServiceEventLogAppender.cs b/src/WinSW/Logging/ServiceEventLogAppender.cs similarity index 50% rename from src/WinSW.Core/Logging/ServiceEventLogAppender.cs rename to src/WinSW/Logging/ServiceEventLogAppender.cs index d2c1860..aa566e7 100644 --- a/src/WinSW.Core/Logging/ServiceEventLogAppender.cs +++ b/src/WinSW/Logging/ServiceEventLogAppender.cs @@ -8,18 +8,33 @@ namespace WinSW.Logging /// Implementes service Event log appender for log4j. /// The implementation presumes that service gets initialized after the logging. /// - public class ServiceEventLogAppender : AppenderSkeleton + internal sealed class ServiceEventLogAppender : AppenderSkeleton { -#pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable. - public IServiceEventLogProvider Provider { get; set; } -#pragma warning restore CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable. + private readonly WrapperServiceEventLogProvider provider; + + internal ServiceEventLogAppender(WrapperServiceEventLogProvider provider) + { + this.provider = provider; + } protected override void Append(LoggingEvent loggingEvent) { - var eventLog = this.Provider.Locate(); + var eventLog = this.provider.Locate(); - // We write the event iff the provider is ready - eventLog?.WriteEntry(loggingEvent.RenderedMessage, ToEventLogEntryType(loggingEvent.Level)); + if (eventLog is not null) + { + eventLog.WriteEntry(loggingEvent.RenderedMessage, ToEventLogEntryType(loggingEvent.Level)); + return; + } + + try + { + using var backupLog = new EventLog("Application", ".", "Windows Service Wrapper"); + backupLog.WriteEntry(loggingEvent.RenderedMessage, ToEventLogEntryType(loggingEvent.Level)); + } + catch + { + } } private static EventLogEntryType ToEventLogEntryType(Level level) diff --git a/src/WinSW/Logging/WrapperServiceEventLogProvider.cs b/src/WinSW/Logging/WrapperServiceEventLogProvider.cs index c53fb30..1ed6f49 100644 --- a/src/WinSW/Logging/WrapperServiceEventLogProvider.cs +++ b/src/WinSW/Logging/WrapperServiceEventLogProvider.cs @@ -5,14 +5,14 @@ namespace WinSW.Logging /// /// Implements caching of the WindowsService reference in WinSW. /// - public class WrapperServiceEventLogProvider : IServiceEventLogProvider + internal sealed class WrapperServiceEventLogProvider { public WrapperService? Service { get; set; } public EventLog? Locate() { var service = this.Service; - if (service != null && !service.IsShuttingDown) + if (service is not null && !service.IsShuttingDown) { return service.EventLog; } diff --git a/src/WinSW/Program.cs b/src/WinSW/Program.cs index ceca2b5..99b2767 100644 --- a/src/WinSW/Program.cs +++ b/src/WinSW/Program.cs @@ -6,9 +6,7 @@ using System.IO; #if VNEXT using System.IO.Pipes; #endif -#if NET using System.Reflection; -#endif using System.Security.AccessControl; using System.Security.Principal; using System.ServiceProcess; @@ -37,6 +35,15 @@ namespace WinSW private static readonly ILog Log = LogManager.GetLogger(typeof(Program)); + private static string ExecutablePath + { + get + { + using var current = Process.GetCurrentProcess(); + return current.MainModule!.FileName!; + } + } + public static int Main(string[] args) { try @@ -81,11 +88,7 @@ namespace WinSW bool inConsoleMode = argsArray.Length > 0; // If descriptor is not specified, initialize the new one (and load configs from there) - descriptor ??= GetServiceDescriptor(); - - // Configure the wrapper-internal logging. - // STDOUT and STDERR of the child process will be handled independently. - InitLoggers(descriptor, inConsoleMode); + descriptor ??= LoadConfigAndInitLoggers(inConsoleMode); if (!inConsoleMode) { @@ -598,8 +601,6 @@ namespace WinSW // [DoesNotReturn] void Elevate() { - using var current = Process.GetCurrentProcess(); - #if VNEXT string? stdinName = Console.IsInputRedirected ? Guid.NewGuid().ToString() : null; string? stdoutName = Console.IsOutputRedirected ? Guid.NewGuid().ToString() : null; @@ -624,7 +625,7 @@ namespace WinSW { UseShellExecute = true, Verb = "runas", - FileName = current.MainModule!.FileName!, + FileName = ExecutablePath, Arguments = arguments, WindowStyle = ProcessWindowStyle.Hidden, }; @@ -664,7 +665,7 @@ namespace WinSW } } - private static void InitLoggers(IWinSWConfiguration descriptor, bool enableConsoleLogging) + private static IWinSWConfiguration LoadConfigAndInitLoggers(bool inConsoleMode) { // TODO: Make logging levels configurable var fileLogLevel = Level.Debug; @@ -674,15 +675,47 @@ namespace WinSW var consoleLogLevel = Level.Info; var eventLogLevel = Level.Warn; - // Legacy format from winsw-1.x: (DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " - " + message); var layout = new PatternLayout { ConversionPattern = "%d %-5p - %m%n" }; layout.ActivateOptions(); - var appenders = new List(); + var repository = LogManager.GetRepository(Assembly.GetExecutingAssembly()); + + if (inConsoleMode) + { + var consoleAppender = new ConsoleAppender + { + Name = "Wrapper console log", + Threshold = consoleLogLevel, + Layout = layout, + }; + consoleAppender.ActivateOptions(); + + BasicConfigurator.Configure(repository, consoleAppender); + } + else + { + var eventLogAppender = new ServiceEventLogAppender(WrapperService.eventLogProvider) + { + Name = "Wrapper event log", + Threshold = eventLogLevel, + }; + eventLogAppender.ActivateOptions(); + + BasicConfigurator.Configure(repository, eventLogAppender); + } + + string executablePath = ExecutablePath; + string directory = Path.GetDirectoryName(executablePath)!; + string baseName = Path.GetFileNameWithoutExtension(executablePath); + + IWinSWConfiguration config = + File.Exists(Path.Combine(directory, baseName + ".xml")) ? new ServiceDescriptor(baseName, directory) : + File.Exists(Path.Combine(directory, baseName + ".yml")) ? new ServiceDescriptorYaml(baseName, directory).Configurations : + throw new FileNotFoundException($"Unable to locate {baseName}.[xml|yml] file within executable directory"); // .wrapper.log - string wrapperLogPath = Path.Combine(descriptor.LogDirectory, descriptor.BaseName + ".wrapper.log"); - var wrapperLog = new FileAppender + string wrapperLogPath = Path.Combine(config.LogDirectory, config.BaseName + ".wrapper.log"); + var fileAppender = new FileAppender { AppendToFile = true, File = wrapperLogPath, @@ -692,37 +725,11 @@ namespace WinSW LockingModel = new FileAppender.MinimalLock(), Layout = layout, }; - wrapperLog.ActivateOptions(); - appenders.Add(wrapperLog); + fileAppender.ActivateOptions(); - // console log - if (enableConsoleLogging) - { - var consoleAppender = new ConsoleAppender - { - Name = "Wrapper console log", - Threshold = consoleLogLevel, - Layout = layout, - }; - consoleAppender.ActivateOptions(); - appenders.Add(consoleAppender); - } + BasicConfigurator.Configure(repository, fileAppender); - // event log - var systemEventLogger = new ServiceEventLogAppender - { - Name = "Wrapper event log", - Threshold = eventLogLevel, - Provider = WrapperService.eventLogProvider, - }; - systemEventLogger.ActivateOptions(); - appenders.Add(systemEventLogger); - - BasicConfigurator.Configure( -#if NET - LogManager.GetRepository(Assembly.GetExecutingAssembly()), -#endif - appenders.ToArray()); + return config; } internal static unsafe bool IsProcessElevated() @@ -776,26 +783,6 @@ namespace WinSW } } - private static IWinSWConfiguration GetServiceDescriptor() - { - string executablePath = new DefaultWinSWSettings().ExecutablePath; - string baseName = Path.GetFileNameWithoutExtension(executablePath); - - var d = new DirectoryInfo(Path.GetDirectoryName(executablePath)!); - - if (File.Exists(Path.Combine(d.FullName, baseName + ".xml"))) - { - return new ServiceDescriptor(baseName, d); - } - - if (File.Exists(Path.Combine(d.FullName, baseName + ".yml"))) - { - return new ServiceDescriptorYaml(baseName, d).Configurations; - } - - throw new FileNotFoundException($"Unable to locate { baseName }.[xml|yml] file within executable directory"); - } - private static void PrintHelp() { Console.WriteLine("A wrapper binary that can be used to host executables as Windows services"); diff --git a/src/WinSW/WrapperService.cs b/src/WinSW/WrapperService.cs index fa51f20..d45fb0a 100644 --- a/src/WinSW/WrapperService.cs +++ b/src/WinSW/WrapperService.cs @@ -28,11 +28,7 @@ namespace WinSW internal WinSWExtensionManager ExtensionManager { get; private set; } - private static readonly ILog Log = LogManager.GetLogger( -#if NET - Assembly.GetExecutingAssembly(), -#endif - "WinSW"); + private static readonly ILog Log = LogManager.GetLogger(typeof(WrapperService)); internal static readonly WrapperServiceEventLogProvider eventLogProvider = new();