Logging subsystem refactoring - use log4net (#145)

* Save the progress

* Add log4net Log appender for Windows service events

* Get rid of the IEventLogger API, we use log4net now
pull/165/head
Oleg Nenashev 2016-12-09 01:41:20 +01:00 committed by GitHub
parent 2db4fb2c31
commit 12c16e40a7
19 changed files with 231 additions and 138 deletions

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using winsw.Logging;
namespace winsw.Logging
{
/// <summary>
/// Implements caching of the WindowsService reference in WinSW.
/// </summary>
public class WrapperServiceEventLogProvider : IServiceEventLogProvider
{
public WrapperService service {get; set;}
public EventLog locate()
{
WrapperService _service = service;
if (_service != null && !_service.IsShuttingDown)
{
return _service.EventLog;
}
// By default return null
return null;
}
}
}

View File

@ -20,10 +20,11 @@ using WMI;
using ServiceType = WMI.ServiceType; using ServiceType = WMI.ServiceType;
using winsw.Native; using winsw.Native;
using System.Reflection; using System.Reflection;
using winsw.Logging;
namespace winsw namespace winsw
{ {
public class WrapperService : ServiceBase, EventLogger, IEventWriter public class WrapperService : ServiceBase, EventLogger
{ {
private SERVICE_STATUS _wrapperServiceStatus; private SERVICE_STATUS _wrapperServiceStatus;
@ -34,6 +35,7 @@ namespace winsw
internal WinSWExtensionManager ExtensionManager { private set; get; } internal WinSWExtensionManager ExtensionManager { private set; get; }
private static readonly ILog Log = LogManager.GetLogger("WinSW"); private static readonly ILog Log = LogManager.GetLogger("WinSW");
private static readonly WrapperServiceEventLogProvider eventLogProvider = new WrapperServiceEventLogProvider();
/// <summary> /// <summary>
/// Indicates to the watch dog thread that we are going to terminate the process, /// Indicates to the watch dog thread that we are going to terminate the process,
@ -53,6 +55,13 @@ namespace winsw
get { return Assembly.GetExecutingAssembly().GetName().Version; } get { return Assembly.GetExecutingAssembly().GetName().Version; }
} }
/// <summary>
/// Indicates that the system is shutting down.
/// </summary>
public bool IsShuttingDown {
get { return _systemShuttingdown; }
}
public WrapperService(ServiceDescriptor descriptor) public WrapperService(ServiceDescriptor descriptor)
{ {
_descriptor = descriptor; _descriptor = descriptor;
@ -63,6 +72,9 @@ namespace winsw
CanPauseAndContinue = false; CanPauseAndContinue = false;
AutoLog = true; AutoLog = true;
_systemShuttingdown = false; _systemShuttingdown = false;
// Register the event log provider
eventLogProvider.service = this;
} }
public WrapperService() : this (new ServiceDescriptor()) public WrapperService() : this (new ServiceDescriptor())
@ -135,7 +147,7 @@ namespace winsw
} }
catch (Exception e) catch (Exception e)
{ {
WriteEvent("Thread failed unexpectedly",e); Log.Error("Thread failed unexpectedly",e);
} }
}).Start(); }).Start();
} }
@ -171,7 +183,7 @@ namespace winsw
} }
catch (Exception e) catch (Exception e)
{ {
WriteEvent("Failed to log event in Windows Event Log: " + message + "; Reason: ", e); Log.Error("Failed to log event in Windows Event Log: " + message + "; Reason: ", e);
} }
} }
} }
@ -190,28 +202,11 @@ namespace winsw
} }
catch (Exception e) catch (Exception e)
{ {
WriteEvent("Failed to log event in Windows Event Log. Reason: ", e); Log.Error("Failed to log event in Windows Event Log. Reason: ", e);
} }
} }
} }
private void WriteEvent(Exception exception)
{
//TODO: pass exception to logger
WriteEvent(exception.Message + "\nStacktrace:" + exception.StackTrace, Level.Error);
}
private void WriteEvent(String message, Exception exception)
{
//TODO: pass exception to logger
WriteEvent(message + "\nMessage:" + exception.Message + "\nStacktrace:" + exception.StackTrace, Level.Error);
}
private void WriteEvent(String message, Level logLevel = null, Exception ex = null)
{
Log.Logger.Log(GetType(), logLevel ?? Level.Info, message, ex);
}
protected override void OnStart(string[] _) protected override void OnStart(string[] _)
{ {
_envs = _descriptor.EnvironmentVariables; _envs = _descriptor.EnvironmentVariables;
@ -233,7 +228,7 @@ namespace winsw
catch (Exception e) catch (Exception e)
{ {
LogEvent("Failed to download " + d.From + " to " + d.To + "\n" + e.Message); LogEvent("Failed to download " + d.From + " to " + d.To + "\n" + e.Message);
WriteEvent("Failed to download " + d.From +" to "+d.To, e); Log.Error("Failed to download " + d.From +" to "+d.To, e);
// but just keep going // but just keep going
} }
} }
@ -250,23 +245,14 @@ namespace winsw
} }
LogEvent("Starting " + _descriptor.Executable + ' ' + startarguments); LogEvent("Starting " + _descriptor.Executable + ' ' + startarguments);
WriteEvent("Starting " + _descriptor.Executable + ' ' + startarguments); Log.Info("Starting " + _descriptor.Executable + ' ' + startarguments);
// Load and start extensions // Load and start extensions
ExtensionManager.LoadExtensions(this); ExtensionManager.LoadExtensions();
try ExtensionManager.FireOnWrapperStarted();
{
ExtensionManager.OnStart(this);
}
catch (ExtensionException ex)
{
LogEvent("Failed to start extension " + ex.ExtensionId + "\n" + ex.Message, EventLogEntryType.Error);
WriteEvent("Failed to start extension " + ex.ExtensionId, ex);
//TODO: Exit on error?
}
LogEvent("Starting " + _descriptor.Executable + ' ' + startarguments); LogEvent("Starting " + _descriptor.Executable + ' ' + startarguments);
WriteEvent("Starting " + _descriptor.Executable + ' ' + startarguments); Log.Info("Starting " + _descriptor.Executable + ' ' + startarguments);
StartProcess(_process, startarguments, _descriptor.Executable); StartProcess(_process, startarguments, _descriptor.Executable);
ExtensionManager.FireOnProcessStarted(_process); ExtensionManager.FireOnProcessStarted(_process);
@ -288,7 +274,7 @@ namespace winsw
} }
catch (Exception ex) catch (Exception ex)
{ {
WriteEvent("Shutdown exception", ex); Log.Error("Shutdown exception", ex);
} }
} }
@ -302,7 +288,7 @@ namespace winsw
} }
catch (Exception ex) catch (Exception ex)
{ {
WriteEvent("Stop exception", ex); Log.Error("Cannot stop exception", ex);
} }
} }
@ -313,14 +299,14 @@ namespace winsw
{ {
string stoparguments = _descriptor.Stoparguments; string stoparguments = _descriptor.Stoparguments;
LogEvent("Stopping " + _descriptor.Id); LogEvent("Stopping " + _descriptor.Id);
WriteEvent("Stopping " + _descriptor.Id); Log.Info("Stopping " + _descriptor.Id);
_orderlyShutdown = true; _orderlyShutdown = true;
if (stoparguments == null) if (stoparguments == null)
{ {
try try
{ {
WriteEvent("ProcessKill " + _process.Id); Log.Debug("ProcessKill " + _process.Id);
ProcessHelper.StopProcessAndChildren(_process.Id, _descriptor.StopTimeout, _descriptor.StopParentProcessFirst); ProcessHelper.StopProcessAndChildren(_process.Id, _descriptor.StopTimeout, _descriptor.StopParentProcessFirst);
ExtensionManager.FireOnProcessTerminated(_process); ExtensionManager.FireOnProcessTerminated(_process);
} }
@ -345,29 +331,21 @@ namespace winsw
StartProcess(stopProcess, stoparguments, executable); StartProcess(stopProcess, stoparguments, executable);
WriteEvent("WaitForProcessToExit "+_process.Id+"+"+stopProcess.Id); Log.Debug("WaitForProcessToExit " + _process.Id + "+" + stopProcess.Id);
WaitForProcessToExit(_process); WaitForProcessToExit(_process);
WaitForProcessToExit(stopProcess); WaitForProcessToExit(stopProcess);
SignalShutdownComplete(); SignalShutdownComplete();
} }
// Stop extensions // Stop extensions
try ExtensionManager.FireBeforeWrapperStopped();
{
ExtensionManager.OnStop(this);
}
catch (ExtensionException ex)
{
LogEvent("Failed to stop extension " + ex.ExtensionId + "\n" + ex.Message, EventLogEntryType.Error);
WriteEvent("Failed to stop extension " + ex.ExtensionId, ex);
}
if (_systemShuttingdown && _descriptor.BeepOnShutdown) if (_systemShuttingdown && _descriptor.BeepOnShutdown)
{ {
Console.Beep(); Console.Beep();
} }
WriteEvent("Finished " + _descriptor.Id); Log.Info("Finished " + _descriptor.Id);
} }
private void WaitForProcessToExit(Process processoWait) private void WaitForProcessToExit(Process processoWait)
@ -458,7 +436,7 @@ namespace winsw
ps.EnvironmentVariables[WinSWSystem.ENVVAR_NAME_SERVICE_ID.ToLower()] = _descriptor.Id; ps.EnvironmentVariables[WinSWSystem.ENVVAR_NAME_SERVICE_ID.ToLower()] = _descriptor.Id;
processToStart.Start(); processToStart.Start();
WriteEvent("Started " + processToStart.Id); Log.Info("Started " + processToStart.Id);
var priority = _descriptor.Priority; var priority = _descriptor.Priority;
if (priority != ProcessPriorityClass.Normal) if (priority != ProcessPriorityClass.Normal)
@ -509,6 +487,7 @@ namespace winsw
try try
{ {
Run(args); Run(args);
Log.Info("Completed. Exit code is 0");
return 0; return 0;
} }
catch (WmiException e) catch (WmiException e)
@ -554,7 +533,7 @@ namespace winsw
if (isCLIMode) // CLI mode, in-service mode otherwise if (isCLIMode) // CLI mode, in-service mode otherwise
{ {
Log.Debug("Starting ServiceWrapper in CLI mode"); Log.Info("Starting ServiceWrapper in the CLI mode");
// Get service info for the future use // Get service info for the future use
Win32Services svc = new WmiRoot().GetCollection<Win32Services>(); Win32Services svc = new WmiRoot().GetCollection<Win32Services>();
@ -761,17 +740,25 @@ namespace winsw
throw new Exception("Unknown command: " + args[0]); throw new Exception("Unknown command: " + args[0]);
} }
else
{
Log.Info("Starting ServiceWrapper in the service mode");
}
Run(new WrapperService()); Run(new WrapperService());
} }
private static void InitLoggers(ServiceDescriptor d, bool enableCLILogging) private static void InitLoggers(ServiceDescriptor d, bool enableCLILogging)
{ {
// TODO: Make logging levels configurable
Level logLevel = Level.Debug; Level logLevel = Level.Debug;
Level eventLogLevel = Level.Warn;
// Legacy format from winsw-1.x: (DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " - " + message); // Legacy format from winsw-1.x: (DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " - " + message);
PatternLayout pl = new PatternLayout { ConversionPattern = "%d %-5p - %m%n" }; PatternLayout pl = new PatternLayout { ConversionPattern = "%d %-5p - %m%n" };
pl.ActivateOptions(); pl.ActivateOptions();
List<IAppender> appenders = new List<IAppender>();
// wrapper.log // wrapper.log
String wrapperLogPath = Path.Combine(d.LogDirectory, d.BaseName + ".wrapper.log"); String wrapperLogPath = Path.Combine(d.LogDirectory, d.BaseName + ".wrapper.log");
var wrapperLog = new FileAppender var wrapperLog = new FileAppender
@ -785,7 +772,7 @@ namespace winsw
Layout = pl Layout = pl
}; };
wrapperLog.ActivateOptions(); wrapperLog.ActivateOptions();
BasicConfigurator.Configure(wrapperLog); appenders.Add(wrapperLog);
// Also display logs in CLI if required // Also display logs in CLI if required
if (enableCLILogging) if (enableCLILogging)
@ -794,11 +781,23 @@ namespace winsw
{ {
Name = "Wrapper console log", Name = "Wrapper console log",
Threshold = logLevel, Threshold = logLevel,
Layout = pl Layout = pl,
}; };
consoleAppender.ActivateOptions(); consoleAppender.ActivateOptions();
((Logger)Log.Logger).AddAppender(consoleAppender); appenders.Add(consoleAppender);
} }
// System log
var systemEventLogger = new ServiceEventLogAppender
{
Name = "System event log",
Threshold = eventLogLevel,
provider = eventLogProvider
};
systemEventLogger.ActivateOptions();
appenders.Add(systemEventLogger);
BasicConfigurator.Configure(appenders.ToArray());
} }
private static string ReadPassword() private static string ReadPassword()

View File

@ -79,6 +79,7 @@
</Compile> </Compile>
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SigIntHelper.cs" /> <Compile Include="SigIntHelper.cs" />
<Compile Include="Logging\WrapperServiceEventLogProvider.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="manifest.xml" /> <Content Include="manifest.xml" />

View File

@ -9,17 +9,17 @@ namespace winsw.Extensions
public abstract String DisplayName { get; } public abstract String DisplayName { get; }
public WinSWExtensionDescriptor Descriptor { get; set; } public WinSWExtensionDescriptor Descriptor { get; set; }
public virtual void Configure(ServiceDescriptor descriptor, XmlNode node, IEventWriter logger) public virtual void Configure(ServiceDescriptor descriptor, XmlNode node)
{ {
// Do nothing // Do nothing
} }
public virtual void OnStart(IEventWriter eventWriter) public virtual void OnWrapperStarted()
{ {
// Do nothing // Do nothing
} }
public virtual void OnStop(IEventWriter eventWriter) public virtual void BeforeWrapperStopped()
{ {
// Do nothing // Do nothing
} }

View File

@ -29,14 +29,14 @@ namespace winsw.Extensions
/// </summary> /// </summary>
/// <param name="descriptor">Service descriptor</param> /// <param name="descriptor">Service descriptor</param>
/// <param name="node">Configuration node</param> /// <param name="node">Configuration node</param>
void Configure(ServiceDescriptor descriptor, XmlNode node, IEventWriter logger); void Configure(ServiceDescriptor descriptor, XmlNode node);
/// <summary> /// <summary>
/// Start handler. Called during startup of the service before the child process. /// Start handler. Called during startup of the service before the child process.
/// </summary> /// </summary>
/// <param name="logger">Logger</param> /// <param name="logger">Logger</param>
/// <exception cref="ExtensionException">Any error during execution</exception> /// <exception cref="ExtensionException">Any error during execution</exception>
void OnStart(IEventWriter logger); void OnWrapperStarted();
/// <summary> /// <summary>
/// Handler, which is being invoked once the child process is started. /// Handler, which is being invoked once the child process is started.
@ -59,6 +59,6 @@ namespace winsw.Extensions
/// </summary> /// </summary>
/// <param name="logger">Logger</param> /// <param name="logger">Logger</param>
/// <exception cref="ExtensionException">Any error during execution</exception> /// <exception cref="ExtensionException">Any error during execution</exception>
void OnStop(IEventWriter logger); void BeforeWrapperStopped();
} }
} }

View File

@ -22,26 +22,43 @@ namespace winsw.Extensions
} }
/// <summary> /// <summary>
/// Starts all extensions /// Notifies all extensions that the wrapper is being started.
/// They are supposed to run the initialization logic.
/// If any extensions fails, WinSW startup should be interrupted.
/// </summary> /// </summary>
/// <exception cref="ExtensionException">Start failure</exception> /// <exception cref="Exception">Start failure</exception>
public void OnStart(IEventWriter logger) public void FireOnWrapperStarted()
{ {
foreach (var ext in Extensions) foreach (var ext in Extensions)
{ {
ext.Value.OnStart(logger); try
{
ext.Value.OnWrapperStarted();
}
catch (ExtensionException ex)
{
Log.Fatal("onWrapperStarted() handler failed for " + ext.Value.DisplayName, ex);
throw ex; // Propagate error to stop the startup
}
} }
} }
/// <summary> /// <summary>
/// Stops all extensions /// Notifies all extensions that the wrapper is being stopped.
/// If an error happens, further extensions will be tried
/// </summary> /// </summary>
/// <exception cref="ExtensionException">Stop failure</exception> public void FireBeforeWrapperStopped()
public void OnStop(IEventWriter logger)
{ {
foreach (var ext in Extensions) foreach (var ext in Extensions)
{ {
ext.Value.OnStop(logger); try
{
ext.Value.BeforeWrapperStopped();
}
catch (ExtensionException ex)
{
Log.Error("beforeWrapperStopped() handler failed for " + ext.Value.DisplayName, ex);
}
} }
} }
@ -86,17 +103,17 @@ namespace winsw.Extensions
//TODO: Implement loading of external extensions. Current version supports internal hack //TODO: Implement loading of external extensions. Current version supports internal hack
#region Extension load management #region Extension load management
/// <summary>
/// Loads extensions according to the configuration file. /// Loads extensions according to the configuration file.
/// </summary> /// </summary>
/// <param name="logger">Logger</param> /// <param name="logger">Logger</param>
/// <exception cref="Exception">Loading failure</exception> /// <exception cref="Exception">Loading failure</exception>
public void LoadExtensions(IEventWriter logger) public void LoadExtensions()
{ {
var extensionIds = ServiceDescriptor.ExtensionIds; var extensionIds = ServiceDescriptor.ExtensionIds;
foreach (String extensionId in extensionIds) foreach (String extensionId in extensionIds)
{ {
LoadExtension(extensionId, logger); LoadExtension(extensionId);
} }
} }
@ -106,7 +123,7 @@ namespace winsw.Extensions
/// <param name="id">Extension ID</param> /// <param name="id">Extension ID</param>
/// <param name="logger">Logger</param> /// <param name="logger">Logger</param>
/// <exception cref="Exception">Loading failure</exception> /// <exception cref="Exception">Loading failure</exception>
private void LoadExtension(string id, IEventWriter logger) private void LoadExtension(string id)
{ {
if (Extensions.ContainsKey(id)) if (Extensions.ContainsKey(id))
{ {
@ -127,7 +144,7 @@ namespace winsw.Extensions
extension.Descriptor = descriptor; extension.Descriptor = descriptor;
try try
{ {
extension.Configure(ServiceDescriptor, configNode, logger); extension.Configure(ServiceDescriptor, configNode);
} }
catch (Exception ex) catch (Exception ex)
{ // Consider any unexpected exception as fatal { // Consider any unexpected exception as fatal
@ -135,11 +152,11 @@ namespace winsw.Extensions
throw ex; throw ex;
} }
Extensions.Add(id, extension); Extensions.Add(id, extension);
logger.LogEvent("Extension loaded: "+id, EventLogEntryType.Information); Log.Info("Extension loaded: " + id);
} }
else else
{ {
logger.LogEvent("Extension is disabled: " + id, EventLogEntryType.Warning); Log.Warn("Extension is disabled: " + id);
} }
} }

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
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

@ -0,0 +1,43 @@
using log4net.Appender;
using log4net.Core;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
namespace winsw.Logging
{
/// <summary>
/// Implementes service Event log appender for log4j.
/// The implementation presumes that service gets initialized after the logging.
/// </summary>
public class ServiceEventLogAppender : AppenderSkeleton
{
public IServiceEventLogProvider provider { get; set; }
override protected void Append(LoggingEvent loggingEvent)
{
EventLog eventLog = provider.locate();
if (eventLog != null)
{
// We write the event iff the provider is ready
eventLog.WriteEntry(loggingEvent.RenderedMessage, toEventLogEntryType(loggingEvent.Level));
}
}
private static EventLogEntryType toEventLogEntryType(Level level)
{
if (level.Value >= Level.Error.Value)
{
return EventLogEntryType.Error;
}
if (level.Value >= Level.Warn.Value)
{
return EventLogEntryType.Warning;
}
// All other events will be posted as information
return EventLogEntryType.Information;
}
}
}

View File

@ -1,13 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
namespace winsw.Util
{
public interface IEventWriter
{
void LogEvent(String message);
void LogEvent(String message, EventLogEntryType type);
}
}

View File

@ -54,12 +54,13 @@
<Compile Include="Extensions\WinSWExtensionDescriptor.cs" /> <Compile Include="Extensions\WinSWExtensionDescriptor.cs" />
<Compile Include="Extensions\WinSWExtensionManager.cs" /> <Compile Include="Extensions\WinSWExtensionManager.cs" />
<Compile Include="LogAppenders.cs" /> <Compile Include="LogAppenders.cs" />
<Compile Include="Logging\ServiceEventLogAppender.cs" />
<Compile Include="Logging\IServiceEventLogProvider.cs" />
<Compile Include="Native\Advapi32.cs" /> <Compile Include="Native\Advapi32.cs" />
<Compile Include="Native\Kernel32.cs" /> <Compile Include="Native\Kernel32.cs" />
<Compile Include="PeriodicRollingCalendar.cs" /> <Compile Include="PeriodicRollingCalendar.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ServiceDescriptor.cs" /> <Compile Include="ServiceDescriptor.cs" />
<Compile Include="Util\IEventWriter.cs" />
<Compile Include="Util\ProcessHelper.cs" /> <Compile Include="Util\ProcessHelper.cs" />
<Compile Include="Util\SigIntHelper.cs" /> <Compile Include="Util\SigIntHelper.cs" />
<Compile Include="Util\XmlHelper.cs" /> <Compile Include="Util\XmlHelper.cs" />
@ -71,6 +72,7 @@
<ItemGroup> <ItemGroup>
<None Include="packages.config" /> <None Include="packages.config" />
</ItemGroup> </ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" /> <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">

View File

@ -43,7 +43,7 @@ namespace winsw.Plugins.RunawayProcessKiller
this.Pidfile = pidfile; this.Pidfile = pidfile;
} }
public override void Configure(ServiceDescriptor descriptor, XmlNode node, IEventWriter logger) public override void Configure(ServiceDescriptor descriptor, XmlNode node)
{ {
// We expect the upper logic to process any errors // We expect the upper logic to process any errors
// TODO: a better parser API for types would be useful // TODO: a better parser API for types would be useful
@ -57,7 +57,7 @@ namespace winsw.Plugins.RunawayProcessKiller
/// This method checks if the PID file is stored on the disk and then terminates runaway processes if they exist. /// This method checks if the PID file is stored on the disk and then terminates runaway processes if they exist.
/// </summary> /// </summary>
/// <param name="logger">Unused logger</param> /// <param name="logger">Unused logger</param>
public override void OnStart(IEventWriter logger) public override void OnWrapperStarted()
{ {
// Read PID file from the disk // Read PID file from the disk
int pid; int pid;

View File

@ -4,6 +4,7 @@ using System.Xml;
using System.Diagnostics; using System.Diagnostics;
using winsw.Extensions; using winsw.Extensions;
using winsw.Util; using winsw.Util;
using log4net;
namespace winsw.Plugins.SharedDirectoryMapper namespace winsw.Plugins.SharedDirectoryMapper
{ {
@ -14,6 +15,8 @@ namespace winsw.Plugins.SharedDirectoryMapper
public override String DisplayName { get { return "Shared Directory Mapper"; } } public override String DisplayName { get { return "Shared Directory Mapper"; } }
private static readonly ILog Logger = LogManager.GetLogger(typeof(SharedDirectoryMapper));
public SharedDirectoryMapper() public SharedDirectoryMapper()
{ {
} }
@ -24,7 +27,7 @@ namespace winsw.Plugins.SharedDirectoryMapper
_entries.Add(config); _entries.Add(config);
} }
public override void Configure(ServiceDescriptor descriptor, XmlNode node, IEventWriter logger) public override void Configure(ServiceDescriptor descriptor, XmlNode node)
{ {
var nodes = XmlHelper.SingleNode(node, "mapping", false).SelectNodes("map"); var nodes = XmlHelper.SingleNode(node, "mapping", false).SelectNodes("map");
if (nodes != null) if (nodes != null)
@ -41,30 +44,30 @@ namespace winsw.Plugins.SharedDirectoryMapper
} }
} }
public override void OnStart(IEventWriter eventWriter) public override void OnWrapperStarted()
{ {
foreach (SharedDirectoryMapperConfig config in _entries) foreach (SharedDirectoryMapperConfig config in _entries)
{ {
if (config.EnableMapping) if (config.EnableMapping)
{ {
eventWriter.LogEvent(DisplayName + ": Mapping shared directory " + config.UNCPath + " to " + config.Label, EventLogEntryType.Information); Logger.Info(DisplayName + ": Mapping shared directory " + config.UNCPath + " to " + config.Label);
try try
{ {
_mapper.MapDirectory(config.Label, config.UNCPath); _mapper.MapDirectory(config.Label, config.UNCPath);
} }
catch (MapperException ex) catch (MapperException ex)
{ {
HandleMappingError(config, eventWriter, ex); HandleMappingError(config, ex);
} }
} }
else else
{ {
eventWriter.LogEvent(DisplayName + ": Mapping of " + config.Label + " is disabled", EventLogEntryType.Warning); Logger.Warn(DisplayName + ": Mapping of " + config.Label + " is disabled");
} }
} }
} }
public override void OnStop(IEventWriter eventWriter) public override void BeforeWrapperStopped()
{ {
foreach (SharedDirectoryMapperConfig config in _entries) foreach (SharedDirectoryMapperConfig config in _entries)
{ {
@ -76,18 +79,16 @@ namespace winsw.Plugins.SharedDirectoryMapper
} }
catch (MapperException ex) catch (MapperException ex)
{ {
HandleMappingError(config, eventWriter, ex); HandleMappingError(config, ex);
} }
} }
} }
} }
private void HandleMappingError(SharedDirectoryMapperConfig config, IEventWriter eventWriter, MapperException ex) { private void HandleMappingError(SharedDirectoryMapperConfig config, MapperException ex) {
String prefix = "Mapping of " + config.Label+ " "; Logger.Error("Mapping of " + config.Label + " failed. STDOUT: " + ex.Process.StandardOutput.ReadToEnd()
eventWriter.LogEvent(prefix + "STDOUT: " + ex.Process.StandardOutput.ReadToEnd(), EventLogEntryType.Information); + " \r\nSTDERR: " + ex.Process.StandardError.ReadToEnd(), ex);
eventWriter.LogEvent(prefix + "STDERR: " + ex.Process.StandardError.ReadToEnd(), EventLogEntryType.Information); throw new ExtensionException(Descriptor.Id, DisplayName + ": Mapping of " + config.Label + "failed", ex);
throw new ExtensionException(Descriptor.Id, DisplayName + ": " + prefix + "failed", ex);
} }
} }
} }

View File

@ -11,6 +11,8 @@
<AssemblyName>SharedDirectoryMapper</AssemblyName> <AssemblyName>SharedDirectoryMapper</AssemblyName>
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion> <TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
<RestorePackages>true</RestorePackages>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
@ -34,6 +36,9 @@
<AssemblyOriginatorKeyFile>$(SolutionDir)..\winsw_key.snk</AssemblyOriginatorKeyFile> <AssemblyOriginatorKeyFile>$(SolutionDir)..\winsw_key.snk</AssemblyOriginatorKeyFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="log4net">
<HintPath>..\..\packages\log4net.2.0.3\lib\net20-full\log4net.dll</HintPath>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
@ -50,7 +55,17 @@
<Name>WinSWCore</Name> <Name>WinSWCore</Name>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="log4net" version="2.0.3" targetFramework="net20" />
</packages>

View File

@ -2,7 +2,6 @@
using NUnit.Framework; using NUnit.Framework;
using winsw.Extensions; using winsw.Extensions;
using winsw.Plugins.SharedDirectoryMapper; using winsw.Plugins.SharedDirectoryMapper;
using winswTests.util;
using winsw.Plugins.RunawayProcessKiller; using winsw.Plugins.RunawayProcessKiller;
namespace winswTests.Extensions namespace winswTests.Extensions
@ -11,7 +10,6 @@ namespace winswTests.Extensions
class RunawayProcessKillerExtensionTest : ExtensionTestBase class RunawayProcessKillerExtensionTest : ExtensionTestBase
{ {
ServiceDescriptor _testServiceDescriptor; ServiceDescriptor _testServiceDescriptor;
readonly TestLogger _logger = new TestLogger();
string testExtension = getExtensionClassNameWithAssembly(typeof(RunawayProcessKillerExtension)); string testExtension = getExtensionClassNameWithAssembly(typeof(RunawayProcessKillerExtension));
@ -41,7 +39,7 @@ namespace winswTests.Extensions
public void LoadExtensions() public void LoadExtensions()
{ {
WinSWExtensionManager manager = new WinSWExtensionManager(_testServiceDescriptor); WinSWExtensionManager manager = new WinSWExtensionManager(_testServiceDescriptor);
manager.LoadExtensions(_logger); manager.LoadExtensions();
Assert.AreEqual(1, manager.Extensions.Count, "One extension should be loaded"); Assert.AreEqual(1, manager.Extensions.Count, "One extension should be loaded");
// Check the file is correct // Check the file is correct
@ -56,9 +54,9 @@ namespace winswTests.Extensions
public void StartStopExtension() public void StartStopExtension()
{ {
WinSWExtensionManager manager = new WinSWExtensionManager(_testServiceDescriptor); WinSWExtensionManager manager = new WinSWExtensionManager(_testServiceDescriptor);
manager.LoadExtensions(_logger); manager.LoadExtensions();
manager.OnStart(_logger); manager.FireOnWrapperStarted();
manager.OnStop(_logger); manager.FireBeforeWrapperStopped();
} }
} }
} }

View File

@ -2,7 +2,6 @@
using NUnit.Framework; using NUnit.Framework;
using winsw.Extensions; using winsw.Extensions;
using winsw.Plugins.SharedDirectoryMapper; using winsw.Plugins.SharedDirectoryMapper;
using winswTests.util;
namespace winswTests.Extensions namespace winswTests.Extensions
{ {
@ -10,7 +9,6 @@ namespace winswTests.Extensions
class SharedDirectoryMapperTest : ExtensionTestBase class SharedDirectoryMapperTest : ExtensionTestBase
{ {
ServiceDescriptor _testServiceDescriptor; ServiceDescriptor _testServiceDescriptor;
readonly TestLogger _logger = new TestLogger();
string testExtension = getExtensionClassNameWithAssembly(typeof(SharedDirectoryMapper)); string testExtension = getExtensionClassNameWithAssembly(typeof(SharedDirectoryMapper));
@ -48,7 +46,7 @@ namespace winswTests.Extensions
public void LoadExtensions() public void LoadExtensions()
{ {
WinSWExtensionManager manager = new WinSWExtensionManager(_testServiceDescriptor); WinSWExtensionManager manager = new WinSWExtensionManager(_testServiceDescriptor);
manager.LoadExtensions(_logger); manager.LoadExtensions();
Assert.AreEqual(2, manager.Extensions.Count, "Two extensions should be loaded"); Assert.AreEqual(2, manager.Extensions.Count, "Two extensions should be loaded");
} }
@ -56,9 +54,9 @@ namespace winswTests.Extensions
public void StartStopExtension() public void StartStopExtension()
{ {
WinSWExtensionManager manager = new WinSWExtensionManager(_testServiceDescriptor); WinSWExtensionManager manager = new WinSWExtensionManager(_testServiceDescriptor);
manager.LoadExtensions(_logger); manager.LoadExtensions();
manager.OnStart(_logger); manager.FireOnWrapperStarted();
manager.OnStop(_logger); manager.FireBeforeWrapperStopped();
} }
} }
} }

View File

@ -1,19 +0,0 @@
using System;
using System.Diagnostics;
using winsw.Util;
namespace winswTests.util
{
class TestLogger : IEventWriter
{
public void LogEvent(String message)
{
Console.WriteLine(message);
}
public void LogEvent(String message, EventLogEntryType type)
{
Console.WriteLine("[" + type + "]" + message);
}
}
}

View File

@ -61,7 +61,6 @@
<Compile Include="MainTest.cs" /> <Compile Include="MainTest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ServiceDescriptorTests.cs" /> <Compile Include="ServiceDescriptorTests.cs" />
<Compile Include="Util\TestLogger.cs" />
<Compile Include="Util\CLITestHelper.cs" /> <Compile Include="Util\CLITestHelper.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -3,5 +3,6 @@
<repository path="..\Core\ServiceWrapper\packages.config" /> <repository path="..\Core\ServiceWrapper\packages.config" />
<repository path="..\Core\WinSWCore\packages.config" /> <repository path="..\Core\WinSWCore\packages.config" />
<repository path="..\Plugins\RunawayProcessKiller\packages.config" /> <repository path="..\Plugins\RunawayProcessKiller\packages.config" />
<repository path="..\Plugins\SharedDirectoryMapper\packages.config" />
<repository path="..\Test\winswTests\packages.config" /> <repository path="..\Test\winswTests\packages.config" />
</repositories> </repositories>