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
|
||||
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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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'">
|
||||
|
|
|
@ -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)
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
Loading…
Reference in New Issue