mirror of https://github.com/winsw/winsw
Backport logging updates (#755)
parent
48015a5d7a
commit
8b06b6157d
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -39,10 +39,10 @@ namespace WinSW
|
||||||
// Currently there is no opportunity to alter the executable path
|
// Currently there is no opportunity to alter the executable path
|
||||||
public virtual string ExecutablePath => Defaults.ExecutablePath;
|
public virtual string ExecutablePath => Defaults.ExecutablePath;
|
||||||
|
|
||||||
public ServiceDescriptor(string baseName, DirectoryInfo d)
|
public ServiceDescriptor(string baseName, string directory)
|
||||||
{
|
{
|
||||||
this.BaseName = baseName;
|
this.BaseName = baseName;
|
||||||
this.BasePath = Path.Combine(d.FullName, this.BaseName);
|
this.BasePath = Path.Combine(directory, this.BaseName);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -54,7 +54,7 @@ namespace WinSW
|
||||||
}
|
}
|
||||||
|
|
||||||
// register the base directory as environment variable so that future expansions can refer to this.
|
// 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
|
// ditto for ID
|
||||||
Environment.SetEnvironmentVariable("SERVICE_ID", this.Name);
|
Environment.SetEnvironmentVariable("SERVICE_ID", this.Name);
|
||||||
|
|
|
@ -11,9 +11,9 @@ namespace WinSW
|
||||||
|
|
||||||
public static DefaultWinSWSettings Defaults { get; } = new DefaultWinSWSettings();
|
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"))
|
using (var reader = new StreamReader(basepath + ".yml"))
|
||||||
{
|
{
|
||||||
|
@ -23,7 +23,7 @@ namespace WinSW
|
||||||
this.Configurations = deserializer.Deserialize<YamlConfiguration>(file);
|
this.Configurations = deserializer.Deserialize<YamlConfiguration>(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
Environment.SetEnvironmentVariable("BASE", d.FullName);
|
Environment.SetEnvironmentVariable("BASE", directory);
|
||||||
|
|
||||||
// ditto for ID
|
// ditto for ID
|
||||||
Environment.SetEnvironmentVariable("SERVICE_ID", this.Configurations.Name);
|
Environment.SetEnvironmentVariable("SERVICE_ID", this.Configurations.Name);
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="log4net" Version="2.0.8" />
|
<PackageReference Include="log4net" Version="2.0.12" />
|
||||||
<PackageReference Include="YamlDotNet" Version="8.1.2" />
|
<PackageReference Include="YamlDotNet" Version="8.1.2" />
|
||||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.*">
|
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.*">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
@ -19,19 +19,8 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' == 'net5.0-windows'">
|
<ItemGroup Condition="'$(TargetFramework)' == 'net5.0-windows'">
|
||||||
<PackageReference Include="System.Diagnostics.EventLog" Version="5.0.0" />
|
|
||||||
<PackageReference Include="System.Security.AccessControl" 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.ServiceProcess.ServiceController" Version="5.0.0" />
|
||||||
<PackageReference Include="System.Threading" Version="4.3.0" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' != 'net5.0-windows'">
|
<ItemGroup Condition="'$(TargetFramework)' != 'net5.0-windows'">
|
||||||
|
|
|
@ -8,18 +8,33 @@ namespace WinSW.Logging
|
||||||
/// Implementes service Event log appender for log4j.
|
/// Implementes service Event log appender for log4j.
|
||||||
/// The implementation presumes that service gets initialized after the logging.
|
/// The implementation presumes that service gets initialized after the logging.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ServiceEventLogAppender : AppenderSkeleton
|
internal sealed class ServiceEventLogAppender : AppenderSkeleton
|
||||||
{
|
{
|
||||||
#pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.
|
private readonly WrapperServiceEventLogProvider provider;
|
||||||
public IServiceEventLogProvider Provider { get; set; }
|
|
||||||
#pragma warning restore CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.
|
internal ServiceEventLogAppender(WrapperServiceEventLogProvider provider)
|
||||||
|
{
|
||||||
|
this.provider = provider;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void Append(LoggingEvent loggingEvent)
|
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
|
if (eventLog is not null)
|
||||||
eventLog?.WriteEntry(loggingEvent.RenderedMessage, ToEventLogEntryType(loggingEvent.Level));
|
{
|
||||||
|
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)
|
private static EventLogEntryType ToEventLogEntryType(Level level)
|
|
@ -5,14 +5,14 @@ namespace WinSW.Logging
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Implements caching of the WindowsService reference in WinSW.
|
/// Implements caching of the WindowsService reference in WinSW.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class WrapperServiceEventLogProvider : IServiceEventLogProvider
|
internal sealed class WrapperServiceEventLogProvider
|
||||||
{
|
{
|
||||||
public WrapperService? Service { get; set; }
|
public WrapperService? Service { get; set; }
|
||||||
|
|
||||||
public EventLog? Locate()
|
public EventLog? Locate()
|
||||||
{
|
{
|
||||||
var service = this.Service;
|
var service = this.Service;
|
||||||
if (service != null && !service.IsShuttingDown)
|
if (service is not null && !service.IsShuttingDown)
|
||||||
{
|
{
|
||||||
return service.EventLog;
|
return service.EventLog;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,7 @@ using System.IO;
|
||||||
#if VNEXT
|
#if VNEXT
|
||||||
using System.IO.Pipes;
|
using System.IO.Pipes;
|
||||||
#endif
|
#endif
|
||||||
#if NET
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
#endif
|
|
||||||
using System.Security.AccessControl;
|
using System.Security.AccessControl;
|
||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
using System.ServiceProcess;
|
using System.ServiceProcess;
|
||||||
|
@ -37,6 +35,15 @@ namespace WinSW
|
||||||
|
|
||||||
private static readonly ILog Log = LogManager.GetLogger(typeof(Program));
|
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)
|
public static int Main(string[] args)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -81,11 +88,7 @@ namespace WinSW
|
||||||
bool inConsoleMode = argsArray.Length > 0;
|
bool inConsoleMode = argsArray.Length > 0;
|
||||||
|
|
||||||
// If descriptor is not specified, initialize the new one (and load configs from there)
|
// If descriptor is not specified, initialize the new one (and load configs from there)
|
||||||
descriptor ??= GetServiceDescriptor();
|
descriptor ??= LoadConfigAndInitLoggers(inConsoleMode);
|
||||||
|
|
||||||
// Configure the wrapper-internal logging.
|
|
||||||
// STDOUT and STDERR of the child process will be handled independently.
|
|
||||||
InitLoggers(descriptor, inConsoleMode);
|
|
||||||
|
|
||||||
if (!inConsoleMode)
|
if (!inConsoleMode)
|
||||||
{
|
{
|
||||||
|
@ -598,8 +601,6 @@ namespace WinSW
|
||||||
// [DoesNotReturn]
|
// [DoesNotReturn]
|
||||||
void Elevate()
|
void Elevate()
|
||||||
{
|
{
|
||||||
using var current = Process.GetCurrentProcess();
|
|
||||||
|
|
||||||
#if VNEXT
|
#if VNEXT
|
||||||
string? stdinName = Console.IsInputRedirected ? Guid.NewGuid().ToString() : null;
|
string? stdinName = Console.IsInputRedirected ? Guid.NewGuid().ToString() : null;
|
||||||
string? stdoutName = Console.IsOutputRedirected ? Guid.NewGuid().ToString() : null;
|
string? stdoutName = Console.IsOutputRedirected ? Guid.NewGuid().ToString() : null;
|
||||||
|
@ -624,7 +625,7 @@ namespace WinSW
|
||||||
{
|
{
|
||||||
UseShellExecute = true,
|
UseShellExecute = true,
|
||||||
Verb = "runas",
|
Verb = "runas",
|
||||||
FileName = current.MainModule!.FileName!,
|
FileName = ExecutablePath,
|
||||||
Arguments = arguments,
|
Arguments = arguments,
|
||||||
WindowStyle = ProcessWindowStyle.Hidden,
|
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
|
// TODO: Make logging levels configurable
|
||||||
var fileLogLevel = Level.Debug;
|
var fileLogLevel = Level.Debug;
|
||||||
|
@ -674,15 +675,47 @@ namespace WinSW
|
||||||
var consoleLogLevel = Level.Info;
|
var consoleLogLevel = Level.Info;
|
||||||
var eventLogLevel = Level.Warn;
|
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" };
|
var layout = new PatternLayout { ConversionPattern = "%d %-5p - %m%n" };
|
||||||
layout.ActivateOptions();
|
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
|
// .wrapper.log
|
||||||
string wrapperLogPath = Path.Combine(descriptor.LogDirectory, descriptor.BaseName + ".wrapper.log");
|
string wrapperLogPath = Path.Combine(config.LogDirectory, config.BaseName + ".wrapper.log");
|
||||||
var wrapperLog = new FileAppender
|
var fileAppender = new FileAppender
|
||||||
{
|
{
|
||||||
AppendToFile = true,
|
AppendToFile = true,
|
||||||
File = wrapperLogPath,
|
File = wrapperLogPath,
|
||||||
|
@ -692,37 +725,11 @@ namespace WinSW
|
||||||
LockingModel = new FileAppender.MinimalLock(),
|
LockingModel = new FileAppender.MinimalLock(),
|
||||||
Layout = layout,
|
Layout = layout,
|
||||||
};
|
};
|
||||||
wrapperLog.ActivateOptions();
|
fileAppender.ActivateOptions();
|
||||||
appenders.Add(wrapperLog);
|
|
||||||
|
|
||||||
// console log
|
BasicConfigurator.Configure(repository, fileAppender);
|
||||||
if (enableConsoleLogging)
|
|
||||||
{
|
|
||||||
var consoleAppender = new ConsoleAppender
|
|
||||||
{
|
|
||||||
Name = "Wrapper console log",
|
|
||||||
Threshold = consoleLogLevel,
|
|
||||||
Layout = layout,
|
|
||||||
};
|
|
||||||
consoleAppender.ActivateOptions();
|
|
||||||
appenders.Add(consoleAppender);
|
|
||||||
}
|
|
||||||
|
|
||||||
// event log
|
return config;
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static unsafe bool IsProcessElevated()
|
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()
|
private static void PrintHelp()
|
||||||
{
|
{
|
||||||
Console.WriteLine("A wrapper binary that can be used to host executables as Windows services");
|
Console.WriteLine("A wrapper binary that can be used to host executables as Windows services");
|
||||||
|
|
|
@ -28,11 +28,7 @@ namespace WinSW
|
||||||
|
|
||||||
internal WinSWExtensionManager ExtensionManager { get; private set; }
|
internal WinSWExtensionManager ExtensionManager { get; private set; }
|
||||||
|
|
||||||
private static readonly ILog Log = LogManager.GetLogger(
|
private static readonly ILog Log = LogManager.GetLogger(typeof(WrapperService));
|
||||||
#if NET
|
|
||||||
Assembly.GetExecutingAssembly(),
|
|
||||||
#endif
|
|
||||||
"WinSW");
|
|
||||||
|
|
||||||
internal static readonly WrapperServiceEventLogProvider eventLogProvider = new();
|
internal static readonly WrapperServiceEventLogProvider eventLogProvider = new();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue