Backport logging updates (#755)

pull/759/head
Next Turn 2020-12-27 01:05:46 +08:00 committed by GitHub
parent 48015a5d7a
commit 8b06b6157d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 83 additions and 112 deletions

View File

@ -1,16 +0,0 @@
using System.Diagnostics;
namespace WinSW.Logging
{
/// <summary>
/// Indicates that the class may reference the event log
/// </summary>
public interface IServiceEventLogProvider
{
/// <summary>
/// Locates Event Log for the service.
/// </summary>
/// <returns>Event Log or null if it is not avilable</returns>
EventLog? Locate();
}
}

View File

@ -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);

View File

@ -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<YamlConfiguration>(file);
}
Environment.SetEnvironmentVariable("BASE", d.FullName);
Environment.SetEnvironmentVariable("BASE", directory);
// ditto for ID
Environment.SetEnvironmentVariable("SERVICE_ID", this.Configurations.Name);

View File

@ -10,7 +10,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="log4net" Version="2.0.8" />
<PackageReference Include="log4net" Version="2.0.12" />
<PackageReference Include="YamlDotNet" Version="8.1.2" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.*">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
@ -19,19 +19,8 @@
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net5.0-windows'">
<PackageReference Include="System.Diagnostics.EventLog" Version="5.0.0" />
<PackageReference Include="System.Security.AccessControl" Version="5.0.0" />
</ItemGroup>
<!-- error NU1605: Detected package downgrade: log4net 2.0.8 -->
<ItemGroup Condition="'$(TargetFramework)' == 'net5.0-windows'">
<PackageReference Include="System.Diagnostics.Debug" Version="4.3.0" />
<PackageReference Include="System.IO.FileSystem" Version="4.3.0" />
<PackageReference Include="System.Net.NameResolution" Version="4.3.0" />
<PackageReference Include="System.Runtime.Extensions" Version="4.3.1" />
<PackageReference Include="System.Runtime.InteropServices" Version="4.3.0" />
<PackageReference Include="System.ServiceProcess.ServiceController" Version="5.0.0" />
<PackageReference Include="System.Threading" Version="4.3.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'net5.0-windows'">

View File

@ -8,18 +8,33 @@ namespace WinSW.Logging
/// Implementes service Event log appender for log4j.
/// The implementation presumes that service gets initialized after the logging.
/// </summary>
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)

View File

@ -5,14 +5,14 @@ namespace WinSW.Logging
/// <summary>
/// Implements caching of the WindowsService reference in WinSW.
/// </summary>
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;
}

View File

@ -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<IAppender>();
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");

View File

@ -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();